@mohasinac/appkit 4.59.0 → 4.60.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/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.d.ts +20 -2
- package/dist/_internal/server/features/orders/adapters.js +44 -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 +10 -4
- package/dist/client.js +29 -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 +43 -0
- package/dist/server.js +98 -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,170 @@
|
|
|
1
|
+
import type { DocumentReference } from "firebase-admin/firestore";
|
|
2
|
+
import type { FirebaseSieveFields, FirebaseSieveResult, SieveModel } from "../../../providers/db-firebase";
|
|
3
|
+
import { BaseRepository } from "../../../providers/db-firebase";
|
|
4
|
+
import { type PaymentCaseDocument, type PaymentCaseKind, type PaymentCaseSeverity, type PaymentCaseStatus } from "../schemas/firestore";
|
|
5
|
+
export interface OpenCaseInput {
|
|
6
|
+
kind: PaymentCaseKind;
|
|
7
|
+
/** Identifies THE PROBLEM, not the observation — see `createPaymentCaseId`. */
|
|
8
|
+
subjectId: string;
|
|
9
|
+
severity: PaymentCaseSeverity;
|
|
10
|
+
title: string;
|
|
11
|
+
detail: string;
|
|
12
|
+
entityType: PaymentCaseDocument["entityType"];
|
|
13
|
+
entityId: string;
|
|
14
|
+
dueAt?: Date;
|
|
15
|
+
amountAtRisk?: number;
|
|
16
|
+
gatewayEventIds?: string[];
|
|
17
|
+
orderIds?: string[];
|
|
18
|
+
gatewayPaymentId?: string;
|
|
19
|
+
now?: Date;
|
|
20
|
+
}
|
|
21
|
+
export type OpenCaseResult = {
|
|
22
|
+
id: string;
|
|
23
|
+
/** `true` on first sight; `false` when an existing case was observed again. */
|
|
24
|
+
created: boolean;
|
|
25
|
+
};
|
|
26
|
+
export interface ResolveCaseInput {
|
|
27
|
+
/** `actioned` (a human did something) or `dismissed` (nothing was wrong). */
|
|
28
|
+
status: Extract<PaymentCaseStatus, "actioned" | "dismissed">;
|
|
29
|
+
/** Required, min 10 chars — see the 🛑 on `resolve`. */
|
|
30
|
+
resolution: string;
|
|
31
|
+
/**
|
|
32
|
+
* The admin uid. 🛑 There is no overload accepting `"system"`, and that
|
|
33
|
+
* absence is the enforcement — see `resolve`.
|
|
34
|
+
*/
|
|
35
|
+
resolvedBy: string;
|
|
36
|
+
now?: Date;
|
|
37
|
+
}
|
|
38
|
+
export declare class PaymentCasesRepository extends BaseRepository<PaymentCaseDocument> {
|
|
39
|
+
static readonly SIEVE_FIELDS: FirebaseSieveFields;
|
|
40
|
+
constructor();
|
|
41
|
+
/**
|
|
42
|
+
* Open a case, or record another observation of one that already exists.
|
|
43
|
+
*
|
|
44
|
+
* 🛑 THIS IS WHAT MAKES IT SAFE TO RUN RECONCILERS ON A HOT CADENCE, and it
|
|
45
|
+
* is the reason the dedupe key is the document id. A reconciler that re-finds
|
|
46
|
+
* the same problem every 5 minutes for 30 days produces **one** case and
|
|
47
|
+
* **one** notification. Without that property the queue fills with duplicates
|
|
48
|
+
* of the same fact, an operator learns to ignore it, and the first real
|
|
49
|
+
* chargeback is lost in 8,000 rows of noise — which is a worse outcome than
|
|
50
|
+
* having no queue at all.
|
|
51
|
+
*
|
|
52
|
+
* On an existing case it increments `observedCount`, bumps `lastObservedAt`,
|
|
53
|
+
* and ESCALATES severity if the new observation is worse. It deliberately
|
|
54
|
+
* does not touch `title`, `detail`, `status`, `assignedTo` or `resolution` —
|
|
55
|
+
* a human may have edited those, and a sweep must not overwrite a human.
|
|
56
|
+
*
|
|
57
|
+
* 🛑 Severity escalates and never falls. A reconciler seeing a smaller
|
|
58
|
+
* manifestation of a known problem must not be able to quietly downgrade an
|
|
59
|
+
* `error` an operator has already been paged about.
|
|
60
|
+
*/
|
|
61
|
+
openOrObserve(input: OpenCaseInput): Promise<OpenCaseResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Resolve a case. **Humans only.**
|
|
64
|
+
*
|
|
65
|
+
* 🛑 A CASE IS NEVER AUTO-CLOSABLE, AND THAT IS ENFORCED BY THE ABSENCE OF A
|
|
66
|
+
* CODE PATH RATHER THAN BY A FLAG.
|
|
67
|
+
*
|
|
68
|
+
* There is no overload a job can call: `resolvedBy` is required, `"system"`
|
|
69
|
+
* is rejected at runtime, and `resolution` has a length floor. A rule
|
|
70
|
+
* enforced by a boolean can be flipped by whoever is in a hurry; a rule
|
|
71
|
+
* enforced by there being no function to call cannot.
|
|
72
|
+
*
|
|
73
|
+
* The reason this matters more than it looks: every case here is money, and
|
|
74
|
+
* the thing that closes a money case is a human understanding what happened.
|
|
75
|
+
* A sweep that closed its own cases would turn the queue into a log — and a
|
|
76
|
+
* log that scrolls is exactly what the 2026-10-21 incident had instead of a
|
|
77
|
+
* record.
|
|
78
|
+
*
|
|
79
|
+
* A reconciler that no longer sees the problem calls `markSelfHealed`.
|
|
80
|
+
*/
|
|
81
|
+
resolve(id: string, input: ResolveCaseInput): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* A reconciler no longer observes the problem.
|
|
84
|
+
*
|
|
85
|
+
* 🛑 Writes `selfHealedAt` and NOTHING ELSE — the status stays whatever it
|
|
86
|
+
* was. "The symptom is gone" and "somebody understands why" are different
|
|
87
|
+
* facts, and conflating them on a money case is how a hand-refunded payment
|
|
88
|
+
* gets filed as fixed.
|
|
89
|
+
*/
|
|
90
|
+
markSelfHealed(id: string, now?: Date): Promise<void>;
|
|
91
|
+
assign(id: string, assignedTo: string, now?: Date): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Open + under-review, worst first, then most-recently-observed first.
|
|
94
|
+
*
|
|
95
|
+
* ## 🛑 `orderBy(SEVERITY)` IS THE BUG THIS FIXES — never put it back
|
|
96
|
+
*
|
|
97
|
+
* This read `.orderBy(SEVERITY, "desc").orderBy(LAST_OBSERVED_AT, "desc")`
|
|
98
|
+
* under the docstring "worst first". **It returned the opposite.** `severity`
|
|
99
|
+
* stores the STRINGS `error` / `warning` / `info`, and Firestore orders
|
|
100
|
+
* strings lexicographically — so `desc` yields **warning, info, error**, the
|
|
101
|
+
* most urgent cases last, in the one query whose whole job is to put them
|
|
102
|
+
* first. Nothing errored; the list was simply backwards.
|
|
103
|
+
*
|
|
104
|
+
* So the QUERY now selects its window by `lastObservedAt` — always written by
|
|
105
|
+
* `openOrObserve`, one index, no composite on an enum — and the ORDER is
|
|
106
|
+
* applied in memory from `PAYMENT_CASE_SEVERITY_RANK`, which already exists
|
|
107
|
+
* and is exact. A stored numeric rank would also work and is worse: a second
|
|
108
|
+
* representation of severity that can drift from the first.
|
|
109
|
+
*
|
|
110
|
+
* ## Why it SURVIVED, and what measuring corrected
|
|
111
|
+
*
|
|
112
|
+
* The order was wrong and harmless, because **neither consumer reads it** —
|
|
113
|
+
* `countActiveBySeverity` tallies and `activeGatewayEventIds` collects into a
|
|
114
|
+
* Set. Two of my own assumptions were false and are recorded so they are not
|
|
115
|
+
* re-assumed:
|
|
116
|
+
*
|
|
117
|
+
* · *"the digest reads this"* — it does NOT. `dailyStatusDigest` runs three
|
|
118
|
+
* `.count()` queries, one per severity, so it never ordered by severity
|
|
119
|
+
* and was correct throughout.
|
|
120
|
+
* · *"it has zero consumers, so delete it"* — it has TWO, both in this
|
|
121
|
+
* file. A grep for `listActive()` with empty parentheses missed
|
|
122
|
+
* `listActive(500)` twice, and deleting on that basis would have broken
|
|
123
|
+
* the digest's counts and the gateway-event retention exclusion.
|
|
124
|
+
*
|
|
125
|
+
* 🛑 The order DOES still matter to those two, via the WINDOW: at >500 active
|
|
126
|
+
* cases, a severity-ordered window filled with alphabetically-first
|
|
127
|
+
* `warning`s would have cut `error`s out of the count entirely. Theoretical
|
|
128
|
+
* at this volume, real in principle, and fixed by the same change.
|
|
129
|
+
*
|
|
130
|
+
* `/admin/money-cases` deliberately does NOT use this — `DataListingView`
|
|
131
|
+
* needs pagination, filters and a sort dropdown, so it goes through the Sieve
|
|
132
|
+
* `list()` below. That view's `sortOptions` carries the matching 🛑 about why
|
|
133
|
+
* it offers no severity sort: same arithmetic, found at the same time.
|
|
134
|
+
*/
|
|
135
|
+
listActive(limit?: number): Promise<PaymentCaseDocument[]>;
|
|
136
|
+
/**
|
|
137
|
+
* Active cases whose external deadline falls inside `horizon`.
|
|
138
|
+
*
|
|
139
|
+
* 🛑 One equality (`status in`) and one inequality (`dueAt <=`), ordered by
|
|
140
|
+
* `dueAt` — the only shape Firestore serves without demanding the inequality
|
|
141
|
+
* field lead the sort (Root Cause #59).
|
|
142
|
+
*
|
|
143
|
+
* 🛑 A case with NO `dueAt` is structurally absent from this query, because a
|
|
144
|
+
* Firestore inequality excludes documents lacking the field (Root Cause
|
|
145
|
+
* #100). That is correct here and is worth stating so nobody "fixes" it: a
|
|
146
|
+
* case without a deadline cannot be nearly-overdue. It is NOT correct for
|
|
147
|
+
* `listActive`, which is why that one filters on `status` only.
|
|
148
|
+
*/
|
|
149
|
+
listDueBefore(horizon: Date, limit?: number): Promise<PaymentCaseDocument[]>;
|
|
150
|
+
/**
|
|
151
|
+
* Counts by severity over active cases.
|
|
152
|
+
*
|
|
153
|
+
* 🛑 Its docstring said *"what the daily digest prints"* and that is FALSE,
|
|
154
|
+
* measured 2026-10-21: `dailyStatusDigest` runs its own three `.count()`
|
|
155
|
+
* aggregations against the collection directly — deliberately, because that
|
|
156
|
+
* file is reachable from the Functions bundle and avoids the repository. So
|
|
157
|
+
* this method currently has no caller, and the digest does not depend on it.
|
|
158
|
+
*
|
|
159
|
+
* Kept rather than deleted because `activeGatewayEventIds` beside it is live
|
|
160
|
+
* and shares `listActive`, so the pair earns its keep — but do not "wire the
|
|
161
|
+
* digest to it" on the strength of the old comment: three `.count()`s are
|
|
162
|
+
* cheaper than paging 500 documents to tally them.
|
|
163
|
+
*/
|
|
164
|
+
countActiveBySeverity(): Promise<Record<PaymentCaseSeverity, number>>;
|
|
165
|
+
/** Every gateway-event id referenced by an ACTIVE case — the retention exclusion. */
|
|
166
|
+
activeGatewayEventIds(): Promise<Set<string>>;
|
|
167
|
+
listRefs(limit?: number): Promise<DocumentReference[]>;
|
|
168
|
+
list(model: SieveModel): Promise<FirebaseSieveResult<PaymentCaseDocument>>;
|
|
169
|
+
}
|
|
170
|
+
export declare function paymentCasesRepository(): PaymentCasesRepository;
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { BaseRepository, applySieveToFirestore, parseSieveDateValue, prepareForFirestore, } from "../../../providers/db-firebase/index.js";
|
|
2
|
+
import { normalizeError } from "../../../errors/normalize.js";
|
|
3
|
+
import { buildHistoryEntry } from "../../../_internal/shared/history/history.js";
|
|
4
|
+
import { PAYMENT_CASES_COLLECTION, PAYMENT_CASE_ACTIVE_STATUSES, PAYMENT_CASE_FIELDS, PAYMENT_CASE_RESOLUTION_MIN, PAYMENT_CASE_SEVERITY_RANK, createPaymentCaseId, } from "../schemas/firestore.js";
|
|
5
|
+
/** gRPC `ALREADY_EXISTS` — the one `.create()` failure meaning "we have it". */
|
|
6
|
+
const GRPC_ALREADY_EXISTS = 6;
|
|
7
|
+
function isAlreadyExists(err) {
|
|
8
|
+
if (typeof err !== "object" || err === null)
|
|
9
|
+
return false;
|
|
10
|
+
const code = err.code;
|
|
11
|
+
if (code === GRPC_ALREADY_EXISTS)
|
|
12
|
+
return true;
|
|
13
|
+
if (code === "already-exists" || code === "ALREADY_EXISTS")
|
|
14
|
+
return true;
|
|
15
|
+
const message = err.message;
|
|
16
|
+
return typeof message === "string" && message.includes("ALREADY_EXISTS");
|
|
17
|
+
}
|
|
18
|
+
export class PaymentCasesRepository extends BaseRepository {
|
|
19
|
+
constructor() {
|
|
20
|
+
super(PAYMENT_CASES_COLLECTION);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Open a case, or record another observation of one that already exists.
|
|
24
|
+
*
|
|
25
|
+
* 🛑 THIS IS WHAT MAKES IT SAFE TO RUN RECONCILERS ON A HOT CADENCE, and it
|
|
26
|
+
* is the reason the dedupe key is the document id. A reconciler that re-finds
|
|
27
|
+
* the same problem every 5 minutes for 30 days produces **one** case and
|
|
28
|
+
* **one** notification. Without that property the queue fills with duplicates
|
|
29
|
+
* of the same fact, an operator learns to ignore it, and the first real
|
|
30
|
+
* chargeback is lost in 8,000 rows of noise — which is a worse outcome than
|
|
31
|
+
* having no queue at all.
|
|
32
|
+
*
|
|
33
|
+
* On an existing case it increments `observedCount`, bumps `lastObservedAt`,
|
|
34
|
+
* and ESCALATES severity if the new observation is worse. It deliberately
|
|
35
|
+
* does not touch `title`, `detail`, `status`, `assignedTo` or `resolution` —
|
|
36
|
+
* a human may have edited those, and a sweep must not overwrite a human.
|
|
37
|
+
*
|
|
38
|
+
* 🛑 Severity escalates and never falls. A reconciler seeing a smaller
|
|
39
|
+
* manifestation of a known problem must not be able to quietly downgrade an
|
|
40
|
+
* `error` an operator has already been paged about.
|
|
41
|
+
*/
|
|
42
|
+
async openOrObserve(input) {
|
|
43
|
+
const now = input.now ?? new Date();
|
|
44
|
+
const id = createPaymentCaseId(input.kind, input.subjectId);
|
|
45
|
+
const ref = this.getCollection().doc(id);
|
|
46
|
+
const doc = {
|
|
47
|
+
id,
|
|
48
|
+
kind: input.kind,
|
|
49
|
+
status: "open",
|
|
50
|
+
severity: input.severity,
|
|
51
|
+
title: input.title,
|
|
52
|
+
detail: input.detail,
|
|
53
|
+
entityType: input.entityType,
|
|
54
|
+
entityId: input.entityId,
|
|
55
|
+
dedupeKey: id,
|
|
56
|
+
observedCount: 1,
|
|
57
|
+
lastObservedAt: now,
|
|
58
|
+
...(input.dueAt ? { dueAt: input.dueAt } : {}),
|
|
59
|
+
...(typeof input.amountAtRisk === "number"
|
|
60
|
+
? { amountAtRisk: input.amountAtRisk }
|
|
61
|
+
: {}),
|
|
62
|
+
...(input.gatewayEventIds?.length ? { gatewayEventIds: input.gatewayEventIds } : {}),
|
|
63
|
+
...(input.orderIds?.length ? { orderIds: input.orderIds } : {}),
|
|
64
|
+
...(input.gatewayPaymentId ? { gatewayPaymentId: input.gatewayPaymentId } : {}),
|
|
65
|
+
statusHistory: [
|
|
66
|
+
buildHistoryEntry({
|
|
67
|
+
actor: { role: "system" },
|
|
68
|
+
trigger: `paymentCase:open:${input.kind}`,
|
|
69
|
+
changes: { status: { from: null, to: "open" } },
|
|
70
|
+
now,
|
|
71
|
+
}),
|
|
72
|
+
],
|
|
73
|
+
createdAt: now,
|
|
74
|
+
updatedAt: now,
|
|
75
|
+
};
|
|
76
|
+
try {
|
|
77
|
+
await ref.create(prepareForFirestore(doc));
|
|
78
|
+
return { id, created: true };
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (!isAlreadyExists(err)) {
|
|
82
|
+
void normalizeError(err);
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/*
|
|
87
|
+
* Already open. A transaction, because `observedCount` is a read-modify-
|
|
88
|
+
* write and two reconcilers can observe the same problem in the same tick —
|
|
89
|
+
* `FieldValue.increment` would handle the counter but not the conditional
|
|
90
|
+
* severity escalation, which needs the current value to compare.
|
|
91
|
+
*/
|
|
92
|
+
try {
|
|
93
|
+
await this.db.runTransaction(async (tx) => {
|
|
94
|
+
const snap = await tx.get(ref);
|
|
95
|
+
if (!snap.exists)
|
|
96
|
+
return; // raced a delete; nothing to observe
|
|
97
|
+
const current = this.mapDoc(snap);
|
|
98
|
+
const escalate = PAYMENT_CASE_SEVERITY_RANK[input.severity] >
|
|
99
|
+
PAYMENT_CASE_SEVERITY_RANK[current.severity];
|
|
100
|
+
tx.update(ref, {
|
|
101
|
+
observedCount: (current.observedCount ?? 0) + 1,
|
|
102
|
+
lastObservedAt: now,
|
|
103
|
+
updatedAt: now,
|
|
104
|
+
...(escalate ? { severity: input.severity } : {}),
|
|
105
|
+
/*
|
|
106
|
+
* A fresh observation clears `selfHealedAt`: the problem is back (or
|
|
107
|
+
* never left), so a stale "looks resolved" banner would actively
|
|
108
|
+
* mislead. Cleared with `null` rather than `undefined` because
|
|
109
|
+
* `prepareForFirestore` strips undefined, which would leave the old
|
|
110
|
+
* value in place — a delete that silently does nothing.
|
|
111
|
+
*/
|
|
112
|
+
selfHealedAt: null,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
void normalizeError(err);
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
120
|
+
return { id, created: false };
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Resolve a case. **Humans only.**
|
|
124
|
+
*
|
|
125
|
+
* 🛑 A CASE IS NEVER AUTO-CLOSABLE, AND THAT IS ENFORCED BY THE ABSENCE OF A
|
|
126
|
+
* CODE PATH RATHER THAN BY A FLAG.
|
|
127
|
+
*
|
|
128
|
+
* There is no overload a job can call: `resolvedBy` is required, `"system"`
|
|
129
|
+
* is rejected at runtime, and `resolution` has a length floor. A rule
|
|
130
|
+
* enforced by a boolean can be flipped by whoever is in a hurry; a rule
|
|
131
|
+
* enforced by there being no function to call cannot.
|
|
132
|
+
*
|
|
133
|
+
* The reason this matters more than it looks: every case here is money, and
|
|
134
|
+
* the thing that closes a money case is a human understanding what happened.
|
|
135
|
+
* A sweep that closed its own cases would turn the queue into a log — and a
|
|
136
|
+
* log that scrolls is exactly what the 2026-10-21 incident had instead of a
|
|
137
|
+
* record.
|
|
138
|
+
*
|
|
139
|
+
* A reconciler that no longer sees the problem calls `markSelfHealed`.
|
|
140
|
+
*/
|
|
141
|
+
async resolve(id, input) {
|
|
142
|
+
const resolution = input.resolution?.trim() ?? "";
|
|
143
|
+
if (resolution.length < PAYMENT_CASE_RESOLUTION_MIN) {
|
|
144
|
+
throw new Error(`A payment case resolution must be at least ${PAYMENT_CASE_RESOLUTION_MIN} characters — "${resolution}" says nothing a later reader can use`);
|
|
145
|
+
}
|
|
146
|
+
if (!input.resolvedBy || input.resolvedBy === "system") {
|
|
147
|
+
throw new Error("A payment case can only be resolved by a human: `resolvedBy` must be an admin uid, never 'system'");
|
|
148
|
+
}
|
|
149
|
+
const now = input.now ?? new Date();
|
|
150
|
+
const ref = this.getCollection().doc(id);
|
|
151
|
+
const existing = await this.findById(id);
|
|
152
|
+
await ref.update({
|
|
153
|
+
status: input.status,
|
|
154
|
+
resolution,
|
|
155
|
+
resolvedBy: input.resolvedBy,
|
|
156
|
+
resolvedAt: now,
|
|
157
|
+
updatedAt: now,
|
|
158
|
+
statusHistory: [
|
|
159
|
+
...(existing?.statusHistory ?? []),
|
|
160
|
+
buildHistoryEntry({
|
|
161
|
+
actor: { role: "admin", uid: input.resolvedBy },
|
|
162
|
+
trigger: "paymentCase:resolve",
|
|
163
|
+
changes: { status: { from: existing?.status ?? null, to: input.status } },
|
|
164
|
+
reason: resolution,
|
|
165
|
+
now,
|
|
166
|
+
}),
|
|
167
|
+
],
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* A reconciler no longer observes the problem.
|
|
172
|
+
*
|
|
173
|
+
* 🛑 Writes `selfHealedAt` and NOTHING ELSE — the status stays whatever it
|
|
174
|
+
* was. "The symptom is gone" and "somebody understands why" are different
|
|
175
|
+
* facts, and conflating them on a money case is how a hand-refunded payment
|
|
176
|
+
* gets filed as fixed.
|
|
177
|
+
*/
|
|
178
|
+
async markSelfHealed(id, now = new Date()) {
|
|
179
|
+
try {
|
|
180
|
+
await this.getCollection().doc(id).update({ selfHealedAt: now, updatedAt: now });
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
// A case deleted between the sweep's read and this write is not a fault.
|
|
184
|
+
void normalizeError(err);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async assign(id, assignedTo, now = new Date()) {
|
|
188
|
+
await this.getCollection()
|
|
189
|
+
.doc(id)
|
|
190
|
+
.update({ assignedTo, status: "under-review", updatedAt: now });
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Open + under-review, worst first, then most-recently-observed first.
|
|
194
|
+
*
|
|
195
|
+
* ## 🛑 `orderBy(SEVERITY)` IS THE BUG THIS FIXES — never put it back
|
|
196
|
+
*
|
|
197
|
+
* This read `.orderBy(SEVERITY, "desc").orderBy(LAST_OBSERVED_AT, "desc")`
|
|
198
|
+
* under the docstring "worst first". **It returned the opposite.** `severity`
|
|
199
|
+
* stores the STRINGS `error` / `warning` / `info`, and Firestore orders
|
|
200
|
+
* strings lexicographically — so `desc` yields **warning, info, error**, the
|
|
201
|
+
* most urgent cases last, in the one query whose whole job is to put them
|
|
202
|
+
* first. Nothing errored; the list was simply backwards.
|
|
203
|
+
*
|
|
204
|
+
* So the QUERY now selects its window by `lastObservedAt` — always written by
|
|
205
|
+
* `openOrObserve`, one index, no composite on an enum — and the ORDER is
|
|
206
|
+
* applied in memory from `PAYMENT_CASE_SEVERITY_RANK`, which already exists
|
|
207
|
+
* and is exact. A stored numeric rank would also work and is worse: a second
|
|
208
|
+
* representation of severity that can drift from the first.
|
|
209
|
+
*
|
|
210
|
+
* ## Why it SURVIVED, and what measuring corrected
|
|
211
|
+
*
|
|
212
|
+
* The order was wrong and harmless, because **neither consumer reads it** —
|
|
213
|
+
* `countActiveBySeverity` tallies and `activeGatewayEventIds` collects into a
|
|
214
|
+
* Set. Two of my own assumptions were false and are recorded so they are not
|
|
215
|
+
* re-assumed:
|
|
216
|
+
*
|
|
217
|
+
* · *"the digest reads this"* — it does NOT. `dailyStatusDigest` runs three
|
|
218
|
+
* `.count()` queries, one per severity, so it never ordered by severity
|
|
219
|
+
* and was correct throughout.
|
|
220
|
+
* · *"it has zero consumers, so delete it"* — it has TWO, both in this
|
|
221
|
+
* file. A grep for `listActive()` with empty parentheses missed
|
|
222
|
+
* `listActive(500)` twice, and deleting on that basis would have broken
|
|
223
|
+
* the digest's counts and the gateway-event retention exclusion.
|
|
224
|
+
*
|
|
225
|
+
* 🛑 The order DOES still matter to those two, via the WINDOW: at >500 active
|
|
226
|
+
* cases, a severity-ordered window filled with alphabetically-first
|
|
227
|
+
* `warning`s would have cut `error`s out of the count entirely. Theoretical
|
|
228
|
+
* at this volume, real in principle, and fixed by the same change.
|
|
229
|
+
*
|
|
230
|
+
* `/admin/money-cases` deliberately does NOT use this — `DataListingView`
|
|
231
|
+
* needs pagination, filters and a sort dropdown, so it goes through the Sieve
|
|
232
|
+
* `list()` below. That view's `sortOptions` carries the matching 🛑 about why
|
|
233
|
+
* it offers no severity sort: same arithmetic, found at the same time.
|
|
234
|
+
*/
|
|
235
|
+
async listActive(limit = 100) {
|
|
236
|
+
const snap = await this.getCollection()
|
|
237
|
+
.where(PAYMENT_CASE_FIELDS.STATUS, "in", [...PAYMENT_CASE_ACTIVE_STATUSES])
|
|
238
|
+
.orderBy(PAYMENT_CASE_FIELDS.LAST_OBSERVED_AT, "desc")
|
|
239
|
+
.limit(limit)
|
|
240
|
+
.get();
|
|
241
|
+
const rows = snap.docs.map((d) => this.mapDoc(d));
|
|
242
|
+
return rows.sort((a, b) => {
|
|
243
|
+
const bySeverity = (PAYMENT_CASE_SEVERITY_RANK[b.severity] ?? 0) -
|
|
244
|
+
(PAYMENT_CASE_SEVERITY_RANK[a.severity] ?? 0);
|
|
245
|
+
if (bySeverity !== 0)
|
|
246
|
+
return bySeverity;
|
|
247
|
+
return b.lastObservedAt.getTime() - a.lastObservedAt.getTime();
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Active cases whose external deadline falls inside `horizon`.
|
|
252
|
+
*
|
|
253
|
+
* 🛑 One equality (`status in`) and one inequality (`dueAt <=`), ordered by
|
|
254
|
+
* `dueAt` — the only shape Firestore serves without demanding the inequality
|
|
255
|
+
* field lead the sort (Root Cause #59).
|
|
256
|
+
*
|
|
257
|
+
* 🛑 A case with NO `dueAt` is structurally absent from this query, because a
|
|
258
|
+
* Firestore inequality excludes documents lacking the field (Root Cause
|
|
259
|
+
* #100). That is correct here and is worth stating so nobody "fixes" it: a
|
|
260
|
+
* case without a deadline cannot be nearly-overdue. It is NOT correct for
|
|
261
|
+
* `listActive`, which is why that one filters on `status` only.
|
|
262
|
+
*/
|
|
263
|
+
async listDueBefore(horizon, limit = 50) {
|
|
264
|
+
const snap = await this.getCollection()
|
|
265
|
+
.where(PAYMENT_CASE_FIELDS.STATUS, "in", [...PAYMENT_CASE_ACTIVE_STATUSES])
|
|
266
|
+
.where(PAYMENT_CASE_FIELDS.DUE_AT, "<=", horizon)
|
|
267
|
+
.orderBy(PAYMENT_CASE_FIELDS.DUE_AT, "asc")
|
|
268
|
+
.limit(limit)
|
|
269
|
+
.get();
|
|
270
|
+
return snap.docs.map((d) => this.mapDoc(d));
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Counts by severity over active cases.
|
|
274
|
+
*
|
|
275
|
+
* 🛑 Its docstring said *"what the daily digest prints"* and that is FALSE,
|
|
276
|
+
* measured 2026-10-21: `dailyStatusDigest` runs its own three `.count()`
|
|
277
|
+
* aggregations against the collection directly — deliberately, because that
|
|
278
|
+
* file is reachable from the Functions bundle and avoids the repository. So
|
|
279
|
+
* this method currently has no caller, and the digest does not depend on it.
|
|
280
|
+
*
|
|
281
|
+
* Kept rather than deleted because `activeGatewayEventIds` beside it is live
|
|
282
|
+
* and shares `listActive`, so the pair earns its keep — but do not "wire the
|
|
283
|
+
* digest to it" on the strength of the old comment: three `.count()`s are
|
|
284
|
+
* cheaper than paging 500 documents to tally them.
|
|
285
|
+
*/
|
|
286
|
+
async countActiveBySeverity() {
|
|
287
|
+
const rows = await this.listActive(500);
|
|
288
|
+
const out = { info: 0, warning: 0, error: 0 };
|
|
289
|
+
for (const row of rows)
|
|
290
|
+
out[row.severity] = (out[row.severity] ?? 0) + 1;
|
|
291
|
+
return out;
|
|
292
|
+
}
|
|
293
|
+
/** Every gateway-event id referenced by an ACTIVE case — the retention exclusion. */
|
|
294
|
+
async activeGatewayEventIds() {
|
|
295
|
+
const rows = await this.listActive(500);
|
|
296
|
+
const ids = new Set();
|
|
297
|
+
for (const row of rows)
|
|
298
|
+
for (const id of row.gatewayEventIds ?? [])
|
|
299
|
+
ids.add(id);
|
|
300
|
+
return ids;
|
|
301
|
+
}
|
|
302
|
+
async listRefs(limit = 500) {
|
|
303
|
+
const snap = await this.getCollection().limit(limit).get();
|
|
304
|
+
return snap.docs.map((d) => d.ref);
|
|
305
|
+
}
|
|
306
|
+
async list(model) {
|
|
307
|
+
return applySieveToFirestore({
|
|
308
|
+
baseQuery: this.getCollection(),
|
|
309
|
+
model,
|
|
310
|
+
fields: PaymentCasesRepository.SIEVE_FIELDS,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
PaymentCasesRepository.SIEVE_FIELDS = {
|
|
315
|
+
kind: { canFilter: true, canSort: false },
|
|
316
|
+
status: { canFilter: true, canSort: false },
|
|
317
|
+
severity: { canFilter: true, canSort: true },
|
|
318
|
+
entityType: { canFilter: true, canSort: false },
|
|
319
|
+
entityId: { canFilter: true, canSort: false },
|
|
320
|
+
dedupeKey: { canFilter: true, canSort: false },
|
|
321
|
+
assignedTo: { canFilter: true, canSort: false },
|
|
322
|
+
observedCount: { canFilter: true, canSort: true },
|
|
323
|
+
amountAtRisk: { canFilter: true, canSort: true },
|
|
324
|
+
gatewayPaymentId: { canFilter: true, canSort: false },
|
|
325
|
+
/*
|
|
326
|
+
* Every date field needs `parseValue` — these are stored Timestamps and
|
|
327
|
+
* sievejs' default coercion leaves an ISO string as a string, which
|
|
328
|
+
* Firestore cannot compare to a Timestamp and so matches ZERO documents
|
|
329
|
+
* with no error (Root Cause #47).
|
|
330
|
+
*/
|
|
331
|
+
dueAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
332
|
+
lastObservedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
333
|
+
resolvedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
334
|
+
selfHealedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
335
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
336
|
+
};
|
|
337
|
+
let cached = null;
|
|
338
|
+
export function paymentCasesRepository() {
|
|
339
|
+
if (!cached)
|
|
340
|
+
cached = new PaymentCasesRepository();
|
|
341
|
+
return cached;
|
|
342
|
+
}
|