@mohasinac/appkit 4.30.18 → 4.31.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/_internal/client/features/digital-code/CodeRevealPanel.js +5 -2
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +3 -3
- package/dist/_internal/client/features/layout/useSidebarSearch.d.ts +21 -5
- package/dist/_internal/client/features/layout/useSidebarSearch.js +27 -6
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +23 -3
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +106 -86
- package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +16 -5
- package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -40
- package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +5 -2
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +11 -3
- package/dist/_internal/client/hand-mode/HandModeProvider.js +2 -0
- package/dist/_internal/server/features/auctions/actions.js +14 -3
- package/dist/_internal/server/features/auctions/service.js +1 -1
- package/dist/_internal/server/features/auth/permissions.d.ts +2 -0
- package/dist/_internal/server/features/auth/permissions.js +3 -2
- package/dist/_internal/server/features/blog/data.js +2 -2
- package/dist/_internal/server/features/blog/service.js +3 -3
- package/dist/_internal/server/features/brands/data.js +1 -1
- package/dist/_internal/server/features/brands/service.js +3 -3
- package/dist/_internal/server/features/bundles/actions.js +6 -1
- package/dist/_internal/server/features/bundles/data.js +21 -4
- package/dist/_internal/server/features/cart/data.js +3 -1
- package/dist/_internal/server/features/cart/service.js +13 -2
- package/dist/_internal/server/features/categories/data.js +26 -6
- package/dist/_internal/server/features/classified/index.d.ts +0 -1
- package/dist/_internal/server/features/classified/index.js +0 -1
- package/dist/_internal/server/features/digital-code/index.d.ts +0 -1
- package/dist/_internal/server/features/digital-code/index.js +0 -1
- package/dist/_internal/server/features/events/actions.js +17 -4
- package/dist/_internal/server/features/events/data.js +1 -1
- package/dist/_internal/server/features/events/service.js +1 -1
- package/dist/_internal/server/features/grouped/data.js +7 -2
- package/dist/_internal/server/features/item-requests/data.js +1 -1
- package/dist/_internal/server/features/live/index.d.ts +0 -1
- package/dist/_internal/server/features/live/index.js +0 -1
- package/dist/_internal/server/features/lottery/actions.js +2 -2
- package/dist/_internal/server/features/lottery/data.js +1 -1
- package/dist/_internal/server/features/orders/actions.js +7 -7
- package/dist/_internal/server/features/orders/data.d.ts +1 -1
- package/dist/_internal/server/features/orders/data.js +14 -2
- package/dist/_internal/server/features/orders/service.js +1 -1
- package/dist/_internal/server/features/pre-orders/service.js +1 -1
- package/dist/_internal/server/features/prize-draws/actions.js +1 -1
- package/dist/_internal/server/features/products/data.js +21 -6
- package/dist/_internal/server/features/products/list-public.d.ts +9 -0
- package/dist/_internal/server/features/products/list-public.js +95 -7
- package/dist/_internal/server/features/products/service.js +1 -1
- package/dist/_internal/server/features/promotions/actions.js +1 -1
- package/dist/_internal/server/features/promotions/data.js +1 -1
- package/dist/_internal/server/features/promotions/service.js +1 -1
- package/dist/_internal/server/features/reviews/data.d.ts +1 -1
- package/dist/_internal/server/features/reviews/data.js +6 -1
- package/dist/_internal/server/features/reviews/og-data.js +1 -1
- package/dist/_internal/server/features/shared/listing-data-factory.js +7 -2
- package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
- package/dist/_internal/server/features/site-settings/adapters.js +8 -5
- package/dist/_internal/server/features/stores/data.js +1 -1
- package/dist/_internal/server/features/sublisting-categories/data.js +1 -1
- package/dist/_internal/server/features/tester/visibility.d.ts +2 -1
- package/dist/_internal/server/features/tester/visibility.js +2 -2
- package/dist/_internal/server/functions/scheduled.d.ts +3 -1
- package/dist/_internal/server/functions/scheduled.js +27 -1
- package/dist/_internal/server/jobs/core/auctionSettlement.js +6 -1
- package/dist/_internal/server/jobs/core/hardBanCascade.js +4 -0
- 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/onOrderStatusChange.js +7 -15
- package/dist/_internal/server/jobs/core/pageViewPrune.d.ts +10 -0
- package/dist/_internal/server/jobs/core/pageViewPrune.js +44 -0
- package/dist/_internal/server/jobs/core/pageViewRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/core/pageViewRollup.js +87 -0
- package/dist/_internal/server/jobs/core/revenueRollup.js +7 -1
- package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/index.js +2 -0
- package/dist/_internal/server/jobs/handlers/pageViewPrune.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/pageViewPrune.js +2 -0
- package/dist/_internal/server/jobs/handlers/pageViewRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/pageViewRollup.js +2 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +69 -22
- package/dist/_internal/shared/checkout/rules/classified.rule.d.ts +9 -4
- package/dist/_internal/shared/checkout/rules/classified.rule.js +7 -2
- package/dist/_internal/shared/features/layout/matchesNavQuery.d.ts +14 -0
- package/dist/_internal/shared/features/layout/matchesNavQuery.js +72 -0
- package/dist/_internal/shared/features/layout/types.d.ts +37 -1
- package/dist/_internal/shared/features/layout/types.js +19 -1
- package/dist/_internal/shared/features/notifications/action-url.d.ts +9 -1
- package/dist/_internal/shared/features/notifications/action-url.js +47 -0
- package/dist/_internal/shared/features/offers/config.d.ts +65 -0
- package/dist/_internal/shared/features/offers/config.js +73 -2
- package/dist/_internal/shared/features/orders/schema.d.ts +19 -1
- package/dist/_internal/shared/features/orders/schema.js +22 -2
- package/dist/_internal/shared/features/site-settings/types.d.ts +12 -3
- package/dist/_internal/shared/listing-types/capabilities.d.ts +8 -2
- package/dist/_internal/shared/listing-types/capabilities.js +6 -1
- package/dist/_internal/shared/listing-types/classified/ctas.js +6 -1
- package/dist/_internal/shared/listing-types/feature-flags.d.ts +10 -5
- package/dist/_internal/shared/listing-types/feature-flags.js +11 -6
- package/dist/client/api/useApiQuery.js +17 -6
- package/dist/client.d.ts +38 -20
- package/dist/client.js +39 -14
- package/dist/configs/next.js +10 -2
- package/dist/constants/api-endpoints.d.ts +18 -54
- package/dist/constants/api-endpoints.js +6 -26
- package/dist/constants/field-names.d.ts +6 -24
- package/dist/constants/field-names.js +10 -27
- package/dist/constants/geo/countries.d.ts +50 -0
- package/dist/constants/geo/countries.js +129 -0
- package/dist/constants/geo/subdivisions.d.ts +13 -0
- package/dist/constants/geo/subdivisions.js +104 -0
- package/dist/constants/index.d.ts +2 -2
- package/dist/constants/index.js +2 -2
- package/dist/constants/table-keys.d.ts +24 -0
- package/dist/constants/table-keys.js +28 -0
- package/dist/contracts/client-realtime.d.ts +6 -0
- package/dist/contracts/extend.d.ts +0 -9
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/realtime-session.d.ts +12 -12
- package/dist/contracts/realtime-session.js +79 -77
- package/dist/core/browser.d.ts +1 -1
- package/dist/core/browser.js +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/site-config.d.ts +0 -3
- package/dist/core/site-config.js +0 -3
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/errors/messages.d.ts +0 -8
- package/dist/errors/messages.js +0 -9
- package/dist/features/about/components/DeveloperView.js +7 -2
- package/dist/features/about/components/PolicyPageView.js +9 -1
- package/dist/features/about/components/PublicProfileView.js +23 -5
- package/dist/features/about/schemas/index.d.ts +8 -8
- package/dist/features/account/components/AddressFilters.js +32 -11
- package/dist/features/account/components/AddressForm.d.ts +26 -25
- package/dist/features/account/components/AddressForm.js +81 -103
- package/dist/features/account/components/AddressesIndexListing.js +8 -4
- package/dist/features/account/components/NotificationBell.js +4 -3
- package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
- package/dist/features/account/components/UserOffersPanel.js +1 -1
- package/dist/features/account/components/UserSidebar.d.ts +3 -4
- package/dist/features/account/components/UserSupportView.js +89 -58
- package/dist/features/account/components/index.d.ts +1 -7
- package/dist/features/account/components/index.js +0 -3
- package/dist/features/account/hooks/useAddresses.d.ts +4 -3
- package/dist/features/account/hooks/useAddresses.js +4 -6
- package/dist/features/account/hooks/useCollapsedSections.d.ts +51 -10
- package/dist/features/account/hooks/useCollapsedSections.js +211 -38
- package/dist/features/account/hooks/useProfile.d.ts +8 -0
- package/dist/features/account/index.d.ts +0 -3
- package/dist/features/account/index.js +0 -3
- package/dist/features/account/schemas/index.d.ts +0 -92
- package/dist/features/account/schemas/index.js +42 -20
- package/dist/features/addresses/components/AddressLocationFields.d.ts +13 -0
- package/dist/features/addresses/components/AddressLocationFields.js +41 -0
- package/dist/features/addresses/hooks/usePostalLookup.d.ts +17 -0
- package/dist/features/addresses/hooks/usePostalLookup.js +133 -0
- package/dist/features/addresses/schemas/address-form.d.ts +423 -0
- package/dist/features/addresses/schemas/address-form.js +212 -0
- package/dist/features/addresses/schemas/index.d.ts +1 -0
- package/dist/features/addresses/schemas/index.js +8 -0
- package/dist/features/admin/actions/admin-read-actions.d.ts +1 -1
- package/dist/features/admin/actions/index.d.ts +0 -1
- package/dist/features/admin/actions/index.js +0 -1
- package/dist/features/admin/actions/notification-actions.js +1 -41
- package/dist/features/admin/components/AdminActionIndexView.d.ts +32 -0
- package/dist/features/admin/components/AdminActionIndexView.js +128 -0
- package/dist/features/admin/components/AdminAdEditorView.js +159 -163
- package/dist/features/admin/components/AdminAddressEditorView.js +97 -55
- package/dist/features/admin/components/AdminBrandEditorView.d.ts +15 -0
- package/dist/features/admin/components/AdminBrandEditorView.js +113 -77
- package/dist/features/admin/components/AdminBundleEditorView.d.ts +19 -9
- package/dist/features/admin/components/AdminBundleEditorView.js +136 -168
- package/dist/features/admin/components/AdminCategoryEditorView.js +120 -43
- package/dist/features/admin/components/AdminCouponEditorView.js +145 -109
- package/dist/features/admin/components/AdminEmployeeEditorView.js +125 -56
- package/dist/features/admin/components/AdminFaqEditorView.js +112 -58
- package/dist/features/admin/components/AdminFeatureEditorView.js +108 -68
- package/dist/features/admin/components/AdminFulfillmentView.js +5 -2
- package/dist/features/admin/components/AdminMediaView.js +3 -3
- package/dist/features/admin/components/AdminNavEditorView.js +69 -44
- package/dist/features/admin/components/AdminOrderEditorView.js +47 -10
- package/dist/features/admin/components/AdminPrizeDrawsView.js +16 -2
- package/dist/features/admin/components/AdminSidebar.d.ts +11 -7
- package/dist/features/admin/components/AdminSiteSettingsView.js +37 -37
- package/dist/features/admin/components/AdminStoreEditorView.js +75 -52
- package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +71 -30
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +95 -65
- package/dist/features/admin/components/BrandQuickCreateForm.d.ts +10 -0
- package/dist/features/admin/components/BrandQuickCreateForm.js +49 -26
- package/dist/features/admin/components/CategoryQuickCreateForm.d.ts +11 -0
- package/dist/features/admin/components/CategoryQuickCreateForm.js +48 -26
- package/dist/features/admin/components/index.d.ts +2 -2
- package/dist/features/admin/components/index.js +1 -1
- package/dist/features/admin/constants/site-settings-tabs.d.ts +120 -0
- package/dist/features/admin/constants/site-settings-tabs.js +152 -0
- package/dist/features/admin/index.d.ts +0 -3
- package/dist/features/admin/index.js +0 -2
- package/dist/features/admin/repository/analytics-rollup.repository.d.ts +35 -0
- package/dist/features/admin/repository/analytics-rollup.repository.js +30 -0
- package/dist/features/admin/schemas/admin-editor-forms.d.ts +56 -57
- package/dist/features/admin/schemas/admin-editor-forms.js +97 -54
- package/dist/features/admin/schemas/admin-ops-forms.d.ts +57 -5
- package/dist/features/admin/schemas/admin-ops-forms.js +104 -20
- package/dist/features/admin/schemas/admin-user-form.js +7 -0
- package/dist/features/admin/schemas/firestore.d.ts +48 -95
- package/dist/features/admin/schemas/firestore.js +15 -151
- package/dist/features/admin/schemas/nav-item-form.js +9 -2
- package/dist/features/admin/server.d.ts +0 -4
- package/dist/features/admin/server.js +0 -2
- package/dist/features/admin/types/index.d.ts +0 -7
- package/dist/features/analytics/repository/page-views.repository.d.ts +14 -0
- package/dist/features/analytics/repository/page-views.repository.js +24 -0
- package/dist/features/analytics/types.d.ts +1 -1
- package/dist/features/analytics/types.js +13 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +6 -2
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -1
- package/dist/features/auth/actions/index.d.ts +0 -1
- package/dist/features/auth/actions/index.js +0 -1
- package/dist/features/auth/actions/profile-actions.js +4 -3
- package/dist/features/auth/actions/realtime-token-actions.js +1 -12
- package/dist/features/auth/auth-helpers.js +5 -7
- package/dist/features/auth/components/ForgotPasswordView.js +1 -1
- package/dist/features/auth/components/Guards.js +2 -1
- package/dist/features/auth/components/LoginForm.d.ts +0 -2
- package/dist/features/auth/components/LoginForm.js +2 -4
- package/dist/features/auth/components/RegisterForm.js +62 -19
- package/dist/features/auth/components/ResetPasswordView.js +1 -1
- package/dist/features/auth/hooks/useAuth.js +3 -0
- package/dist/features/auth/permissions/constants.d.ts +27 -1
- package/dist/features/auth/role-predicates.d.ts +75 -7
- package/dist/features/auth/role-predicates.js +28 -2
- package/dist/features/auth/schemas/firestore.d.ts +22 -0
- package/dist/features/auth/schemas/index.d.ts +1 -1
- package/dist/features/auth/schemas/index.js +44 -8
- package/dist/features/auth/types/index.d.ts +14 -1
- package/dist/features/blog/api/route.js +10 -2
- package/dist/features/blog/components/BlogIndexListing.js +1 -1
- package/dist/features/blog/hooks/useBlog.js +2 -2
- package/dist/features/blog/repository/blog.repository.js +3 -1
- package/dist/features/blog/schemas/index.d.ts +4 -0
- package/dist/features/blog/schemas/index.js +2 -0
- package/dist/features/blog/types/index.d.ts +1 -1
- package/dist/features/cart/actions/cart-actions.js +13 -4
- package/dist/features/cart/utils/pending-ops.js +1 -1
- package/dist/features/catalogue/components/CatalogueItemEditorView.d.ts +22 -0
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +112 -52
- package/dist/features/catalogue/schemas/validation.d.ts +4 -4
- package/dist/features/catalogue/schemas/validation.js +47 -10
- package/dist/features/categories/components/BrandDetailPageView.js +28 -12
- package/dist/features/categories/components/CategoryDetailPageView.js +37 -20
- package/dist/features/categories/repository/categories.repository.js +3 -1
- package/dist/features/categories/schemas/bundle-form.d.ts +142 -0
- package/dist/features/categories/schemas/bundle-form.js +149 -0
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +10 -1
- package/dist/features/classified/components/ClassifiedContactSellerPanel.js +1 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +5 -4
- package/dist/features/classified/components/ClassifiedDetailPageView.js +47 -18
- package/dist/features/consultation/components/ConsultationForm.js +1 -1
- package/dist/features/consultation/schemas/index.js +34 -10
- package/dist/features/corporate/schemas/index.js +36 -15
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +4 -1
- package/dist/features/events/components/EventBanner.js +5 -2
- package/dist/features/events/components/EventOfferCard.js +3 -1
- package/dist/features/events/components/EventPollWidget.js +1 -1
- package/dist/features/events/components/SpinWheelView.js +3 -2
- package/dist/features/filters/FilterPanel.js +3 -3
- package/dist/features/filters/RangeFilter.d.ts +10 -0
- package/dist/features/filters/RangeFilter.js +41 -4
- package/dist/features/grouped/components/GroupedListingEditorView.js +2 -1
- package/dist/features/history/hooks/useHistory.js +29 -27
- package/dist/features/homepage/components/AdSlot.js +3 -1
- package/dist/features/homepage/components/GoogleReviewsSection.js +8 -2
- package/dist/features/homepage/components/MarketplaceHomepageView.js +58 -15
- package/dist/features/homepage/components/SocialFeedSection.js +4 -1
- package/dist/features/homepage/hooks/useBlogArticles.js +1 -1
- package/dist/features/homepage/lib/live-stats.js +8 -1
- package/dist/features/homepage/repository/carousel.repository.js +2 -1
- package/dist/features/layout/BottomActions.js +84 -11
- package/dist/features/layout/BottomActionsContext.d.ts +1 -6
- package/dist/features/layout/BottomActionsContext.js +1 -1
- package/dist/features/layout/hooks/useBottomActions.js +2 -2
- package/dist/features/layout/hooks/useFormBottomActions.d.ts +12 -1
- package/dist/features/layout/hooks/useFormBottomActions.js +12 -6
- package/dist/features/live/components/LiveItemDetailPageView.js +4 -1
- package/dist/features/lottery/schemas/config-write.d.ts +9 -5
- package/dist/features/lottery/schemas/config-write.js +69 -9
- package/dist/features/media/finalize.js +3 -1
- package/dist/features/media/modals/ImageEditor.js +1 -1
- package/dist/features/media/modals/VideoThumbnailSelector.js +5 -2
- package/dist/features/media/modals/VideoTrimModal.js +5 -2
- package/dist/features/media/upload/MediaUploadField.js +2 -2
- package/dist/features/messages/hooks/useConversation.js +18 -4
- package/dist/features/messages/hooks/useConversations.js +25 -8
- package/dist/features/orders/components/RefundRequestView.js +5 -2
- package/dist/features/orders/schemas/buyer-forms.d.ts +6 -3
- package/dist/features/orders/schemas/buyer-forms.js +31 -3
- package/dist/features/orders/schemas/index.d.ts +2 -2
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -1
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -1
- package/dist/features/products/components/GroupSettingsPanel.js +3 -1
- package/dist/features/products/components/MakeOfferButton.d.ts +13 -2
- package/dist/features/products/components/MakeOfferButton.js +33 -11
- package/dist/features/products/components/PrizeDrawDetailPageView.js +4 -1
- package/dist/features/products/components/ProductDetailActions.js +0 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +7 -3
- package/dist/features/products/components/ProductDetailPageView.js +16 -7
- package/dist/features/products/components/ShareButton.js +3 -1
- package/dist/features/products/constants/action-defs.d.ts +1 -2
- package/dist/features/products/constants/action-defs.js +40 -43
- package/dist/features/products/repository/loadProductFeatures.js +10 -2
- package/dist/features/products/repository/products.repository.d.ts +5 -1
- package/dist/features/products/repository/products.repository.js +25 -2
- package/dist/features/products/schemas/firestore.d.ts +7 -3
- package/dist/features/products/schemas/product-features.validators.js +3 -0
- package/dist/features/promotions/components/CouponCard.js +11 -8
- package/dist/features/reviews/actions/review-actions.js +8 -1
- package/dist/features/reviews/components/ReviewDetailPageView.js +13 -3
- package/dist/features/reviews/components/ReviewDetailShell.js +3 -1
- package/dist/features/scams/actions/scam-actions.d.ts +9 -1
- package/dist/features/scams/actions/scam-actions.js +52 -12
- package/dist/features/scams/components/SellerTrustBadge.d.ts +1 -1
- package/dist/features/scams/components/SellerTrustBadge.js +13 -0
- package/dist/features/scams/repository/scammer.repository.js +2 -1
- package/dist/features/scams/schemas/firestore.d.ts +15 -1
- package/dist/features/scams/schemas/report-form.d.ts +5 -5
- package/dist/features/scams/schemas/report-form.js +18 -3
- package/dist/features/search/action-index/derive.d.ts +49 -0
- package/dist/features/search/action-index/derive.js +121 -0
- package/dist/features/search/action-index/repository.d.ts +42 -0
- package/dist/features/search/action-index/repository.js +100 -0
- package/dist/features/search/action-index/settings-entries.d.ts +41 -0
- package/dist/features/search/action-index/settings-entries.js +268 -0
- package/dist/features/search/action-index/types.d.ts +81 -0
- package/dist/features/search/action-index/types.js +102 -0
- package/dist/features/search/action-index/validate-entry.d.ts +34 -0
- package/dist/features/search/action-index/validate-entry.js +114 -0
- package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
- package/dist/features/search/hooks/useNavSuggestions.js +23 -3
- package/dist/features/search/repository/search.repository.js +3 -1
- package/dist/features/seller/actions/offer-actions.js +43 -19
- package/dist/features/seller/actions/seller-actions.js +29 -5
- package/dist/features/seller/components/CategoryChipPicker.d.ts +9 -0
- package/dist/features/seller/components/CategoryChipPicker.js +12 -0
- package/dist/features/seller/components/FulfillmentView.js +5 -2
- package/dist/features/seller/components/PhysicalLocationModal.js +1 -1
- package/dist/features/seller/components/PrintCenterView.js +3 -1
- package/dist/features/seller/components/SellerAddressesView.js +5 -1
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +83 -43
- package/dist/features/seller/components/SellerArtView.d.ts +14 -7
- package/dist/features/seller/components/SellerArtView.js +4 -119
- package/dist/features/seller/components/SellerBidsView.js +1 -1
- package/dist/features/seller/components/SellerClassifiedView.d.ts +14 -7
- package/dist/features/seller/components/SellerClassifiedView.js +4 -124
- package/dist/features/seller/components/SellerCouponEditorView.d.ts +22 -1
- package/dist/features/seller/components/SellerCouponEditorView.js +91 -45
- package/dist/features/seller/components/SellerCouponsView.js +1 -1
- package/dist/features/seller/components/SellerDigitalCodesView.d.ts +14 -7
- package/dist/features/seller/components/SellerDigitalCodesView.js +4 -126
- package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -2
- package/dist/features/seller/components/SellerListingTypeView.d.ts +66 -0
- package/dist/features/seller/components/SellerListingTypeView.js +277 -0
- package/dist/features/seller/components/SellerLiveView.d.ts +14 -7
- package/dist/features/seller/components/SellerLiveView.js +4 -126
- package/dist/features/seller/components/SellerOrdersView.js +3 -3
- package/dist/features/seller/components/SellerPayoutMethodsView.js +2 -2
- package/dist/features/seller/components/SellerProductShell.d.ts +5 -0
- package/dist/features/seller/components/SellerProductShell.js +15 -5
- package/dist/features/seller/components/SellerProductsView.js +24 -10
- package/dist/features/seller/components/SellerReviewsView.js +47 -16
- package/dist/features/seller/components/SellerShippingConfigsView.js +5 -4
- package/dist/features/seller/components/SellerSidebar.d.ts +3 -6
- package/dist/features/seller/components/SellerStickersView.d.ts +14 -7
- package/dist/features/seller/components/SellerStickersView.js +4 -119
- package/dist/features/seller/components/SellerStoreCategoriesView.js +3 -2
- package/dist/features/seller/components/SellerSupportView.d.ts +31 -0
- package/dist/features/seller/components/SellerSupportView.js +100 -0
- package/dist/features/seller/components/index.d.ts +4 -8
- package/dist/features/seller/components/index.js +2 -4
- package/dist/features/seller/index.d.ts +0 -1
- package/dist/features/seller/index.js +0 -1
- package/dist/features/seller/schemas/coupon-form.d.ts +214 -0
- package/dist/features/seller/schemas/coupon-form.js +155 -23
- package/dist/features/seller/schemas/offer-forms.d.ts +16 -7
- package/dist/features/seller/schemas/offer-forms.js +28 -12
- package/dist/features/shell/FormShell.js +2 -2
- package/dist/features/shell/QuickFormDrawer.js +2 -2
- package/dist/features/shell/SectionForm.d.ts +19 -1
- package/dist/features/shell/SectionForm.js +104 -17
- package/dist/features/shell/build-sections.d.ts +23 -0
- package/dist/features/shell/build-sections.js +106 -8
- package/dist/features/shell/field-ui-meta.d.ts +44 -0
- package/dist/features/shell/field-ui-meta.js +14 -7
- package/dist/features/shell/form-sections.d.ts +46 -0
- package/dist/features/shell/form-sections.js +114 -0
- package/dist/features/shipments/components/AdminShipmentEditorView.js +87 -64
- package/dist/features/shipments/schemas/validation.d.ts +2 -2
- package/dist/features/shipments/schemas/validation.js +44 -13
- package/dist/features/store-extensions/schemas/custom-role-form.js +6 -2
- package/dist/features/store-extensions/schemas/listing-template-form.d.ts +7 -7
- package/dist/features/store-extensions/schemas/payout-method-form.js +14 -0
- package/dist/features/stores/actions/store-query-actions.js +3 -2
- package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -1
- package/dist/features/stores/api/[storeSlug]/products/route.js +3 -1
- package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +12 -3
- package/dist/features/support/repository/support.repository.d.ts +21 -0
- package/dist/features/support/repository/support.repository.js +63 -0
- package/dist/features/support/schemas/firestore.d.ts +27 -2
- package/dist/features/support/schemas/firestore.js +2 -0
- package/dist/features/support/schemas/ticket-create-form.js +7 -0
- package/dist/features/tester/schemas/checklist-item-form.d.ts +43 -0
- package/dist/features/tester/schemas/checklist-item-form.js +109 -0
- package/dist/features/tester/seed-data/blog-tester-seed-data.js +2 -2
- package/dist/features/tester/seed-data/categories-tester-seed-data.js +2 -2
- package/dist/features/tester/seed-data/events-tester-seed-data.js +5 -5
- package/dist/features/tester/seed-data/orders-tester-seed-data.js +2 -2
- package/dist/features/tester/seed-data/products-tester-seed-data.js +48 -49
- package/dist/features/tester/seed-data/stores-tester-seed-data.js +3 -3
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +291 -30
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +51 -14
- package/dist/features/wishlist/hooks/useWishlistToggle.js +8 -1
- package/dist/http/api-handler.js +2 -1
- package/dist/index.d.ts +25 -104
- package/dist/index.js +38 -143
- package/dist/jobs.d.ts +1 -1
- package/dist/jobs.js +1 -1
- package/dist/monitoring/browser.d.ts +0 -2
- package/dist/monitoring/browser.js +12 -1
- package/dist/monitoring/index.d.ts +0 -2
- package/dist/monitoring/index.js +0 -1
- package/dist/next/api/routeHandler.js +9 -4
- package/dist/next/index.d.ts +1 -2
- package/dist/next/index.js +0 -1
- package/dist/next/middleware/index.d.ts +0 -2
- package/dist/next/middleware/index.js +0 -1
- package/dist/next/routing/route-map.d.ts +8 -7
- package/dist/next/routing/route-map.js +10 -3
- package/dist/providers/auth-firebase/session.js +3 -1
- package/dist/providers/db-firebase/admin-app-lite.js +8 -8
- package/dist/providers/db-firebase/admin.js +27 -13
- package/dist/providers/db-firebase/browser-stub.d.ts +1 -11
- package/dist/providers/db-firebase/browser-stub.js +6 -11
- package/dist/providers/db-firebase/index.d.ts +0 -1
- package/dist/providers/db-firebase/index.js +0 -1
- package/dist/providers/db-firebase/rtdb-paths.d.ts +15 -6
- package/dist/providers/db-firebase/rtdb-paths.js +15 -6
- package/dist/providers/db-firebase/rtdb-url.d.ts +27 -0
- package/dist/providers/db-firebase/rtdb-url.js +50 -0
- package/dist/providers/db-firebase/sieve.d.ts +22 -0
- package/dist/providers/firebase-client/realtime.d.ts +20 -0
- package/dist/providers/firebase-client/realtime.js +34 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +1 -1
- package/dist/react/hooks/use-action-dispatch.js +8 -2
- package/dist/react/hooks/useFormStatePreservation.js +3 -1
- package/dist/react/hooks/useListingTypeFlags.d.ts +1 -1
- package/dist/react/hooks/useListingTypeFlags.js +1 -1
- package/dist/react/hooks/usePendingFilters.js +5 -3
- package/dist/react/hooks/usePendingTable.js +15 -3
- package/dist/react/hooks/useRealtimeEvent.js +20 -9
- package/dist/repositories/index.d.ts +0 -1
- package/dist/repositories/index.js +0 -1
- package/dist/schemas/registry.d.ts +197 -232
- package/dist/schemas/registry.js +5 -6
- package/dist/schemas/webhooks/razorpay.d.ts +50 -50
- package/dist/security/authorization.d.ts +9 -0
- package/dist/security/authorization.js +16 -2
- package/dist/security/browser.d.ts +1 -3
- package/dist/security/browser.js +5 -2
- package/dist/security/index.d.ts +1 -3
- package/dist/security/index.js +19 -2
- package/dist/security/pii-encrypt.js +16 -0
- package/dist/security/pii-schemas.d.ts +0 -14
- package/dist/security/pii-schemas.js +7 -15
- package/dist/seed/_helpers/category-forest.js +2 -2
- package/dist/seed/_helpers/media.d.ts +19 -0
- package/dist/seed/_helpers/media.js +75 -0
- package/dist/seed/blog-posts-seed-data.js +17 -17
- package/dist/seed/carousel-slides-seed-data.js +7 -7
- package/dist/seed/cart-seed-data.js +13 -13
- package/dist/seed/categories-seed-data.js +10 -10
- package/dist/seed/events-seed-data.js +24 -24
- package/dist/seed/factories/product.factory.js +4 -4
- package/dist/seed/factories/store.factory.js +3 -3
- package/dist/seed/factories/user.factory.js +2 -2
- package/dist/seed/grouped-listings-seed-data.js +11 -11
- package/dist/seed/homepage-sections-seed-data.js +2 -2
- package/dist/seed/index.d.ts +0 -1
- package/dist/seed/index.js +0 -1
- package/dist/seed/manifest.d.ts +1 -1
- package/dist/seed/manifest.js +0 -5
- package/dist/seed/notifications-seed-data.js +20 -1
- package/dist/seed/orders-seed-data.js +2 -2
- package/dist/seed/products-art-seed-data.js +16 -16
- package/dist/seed/products-auctions-seed-data.js +34 -34
- package/dist/seed/products-classifieds-seed-data.js +35 -37
- package/dist/seed/products-digital-codes-seed-data.js +20 -20
- package/dist/seed/products-live-items-seed-data.js +11 -11
- package/dist/seed/products-preorders-seed-data.js +13 -13
- package/dist/seed/products-prize-draws-seed-data.js +43 -43
- package/dist/seed/products-standard-seed-data.js +54 -54
- package/dist/seed/products-stickers-seed-data.js +15 -15
- package/dist/seed/reviews-seed-data.js +4 -4
- package/dist/seed/scammers-seed-data.js +3 -3
- package/dist/seed/site-settings-seed-data.js +11 -24
- package/dist/seed/store-extensions-seed-data.js +10 -10
- package/dist/seed/stores-seed-data.js +11 -11
- package/dist/seed/support-tickets-seed-data.d.ts +14 -0
- package/dist/seed/support-tickets-seed-data.js +20 -1
- package/dist/seed/users-seed-data.js +20 -20
- package/dist/server-entry.d.ts +0 -1
- package/dist/server-entry.js +0 -1
- package/dist/server.d.ts +9 -6
- package/dist/server.js +7 -13
- package/dist/styles.css +248 -68
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +2 -2
- package/dist/tokens/themes/default-light.js +2 -2
- package/dist/tokens/tokens.css +24 -4
- package/dist/ui/components/ActionRow.d.ts +50 -0
- package/dist/ui/components/ActionRow.js +53 -0
- package/dist/ui/components/ActionRow.style.css +145 -0
- package/dist/ui/components/Drawer.d.ts +10 -0
- package/dist/ui/components/Drawer.js +2 -1
- package/dist/ui/components/FilterDrawer.js +4 -1
- package/dist/ui/components/FilterDrawer.style.css +6 -12
- package/dist/ui/components/FormActionBar.js +14 -1
- package/dist/ui/components/FormActionBar.style.css +12 -11
- package/dist/ui/components/ListingLayout.js +8 -1
- package/dist/ui/components/ListingLayout.style.css +6 -13
- package/dist/ui/components/Modal.d.ts +15 -1
- package/dist/ui/components/Modal.js +17 -3
- package/dist/ui/components/QuickCreateModal.js +2 -2
- package/dist/ui/components/QuickCreateModal.style.css +78 -84
- package/dist/ui/components/RecordDetailModal.js +2 -2
- package/dist/ui/components/SideDrawer.d.ts +8 -0
- package/dist/ui/components/SideDrawer.js +2 -1
- package/dist/ui/components/SideDrawer.style.css +6 -3
- package/dist/ui/components/SideModal.d.ts +11 -1
- package/dist/ui/components/SideModal.js +3 -2
- package/dist/ui/components/SideModal.style.css +14 -0
- package/dist/ui/components/Typography.style.css +33 -24
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/components/toolbar-collapse.js +1 -2
- package/dist/ui/forms/FormShell.style.css +260 -311
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/schemas.d.ts +0 -19
- package/dist/validation/schemas.js +8 -19
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +3 -6
|
@@ -8,6 +8,7 @@ import { productRepository } from "../../../../repositories";
|
|
|
8
8
|
import { reviewRepository } from "../../../../repositories";
|
|
9
9
|
import { getAdminDb } from "../../../../providers/db-firebase";
|
|
10
10
|
import { normalizeError } from "../../../../errors/normalize";
|
|
11
|
+
import { safeRead } from "../../../../errors/safe-read";
|
|
11
12
|
import { serverLogger } from "../../../../monitoring/server-logger";
|
|
12
13
|
import { PRODUCT_COLLECTION } from "../../../../features/products/schemas/firestore";
|
|
13
14
|
import { PRODUCTS_SITEMAP_LIMIT } from "../../../shared/features/products/config";
|
|
@@ -23,15 +24,29 @@ import { REVIEWS_DETAIL_PAGE_SIZE } from "../../../shared/features/reviews/confi
|
|
|
23
24
|
// ---------------------------------------------------------------------------
|
|
24
25
|
/** Fetch a single product by slug or id, deduped per request. */
|
|
25
26
|
export const getProductForDetail = cache(async (slugOrId) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
/*
|
|
28
|
+
* 🛑 No view-count write here — deleted 2026-08-31.
|
|
29
|
+
*
|
|
30
|
+
* This fired `incrementViewCount` on every render, which was two defects at
|
|
31
|
+
* once. It is a Firestore WRITE on the render path (Rule #6), and product
|
|
32
|
+
* and category detail were being counted TWICE by two systems that key
|
|
33
|
+
* differently: this one on the document by id, and the client
|
|
34
|
+
* `PageViewTracker` into `pageViews` by slug. They can never reconcile —
|
|
35
|
+
* they disagree on bots, on prefetch, and on JS-disabled clients — so the
|
|
36
|
+
* two numbers were guaranteed to drift and neither could be trusted.
|
|
37
|
+
*
|
|
38
|
+
* `pageViews` is the single counter now. `viewCount` on the document stays
|
|
39
|
+
* for historical rows; nothing increments it.
|
|
40
|
+
*/
|
|
41
|
+
return (await productRepository.findByIdOrSlug(slugOrId)) ?? null;
|
|
31
42
|
});
|
|
32
43
|
/** Fetch the first page of approved reviews for a product, deduped per request. */
|
|
33
44
|
export const getReviewsForProduct = cache(async (productId, limit = REVIEWS_DETAIL_PAGE_SIZE) => {
|
|
34
|
-
return reviewRepository.findApprovedByProduct(productId, limit)
|
|
45
|
+
return safeRead(() => reviewRepository.findApprovedByProduct(productId, limit), {
|
|
46
|
+
route: "/products",
|
|
47
|
+
key: "products.getReviewsForProduct",
|
|
48
|
+
fallback: [],
|
|
49
|
+
});
|
|
35
50
|
});
|
|
36
51
|
/** Firestore review doc → client Review shape. Shared by every listing-type detail page's reviews tab. */
|
|
37
52
|
export function toReview(doc) {
|
|
@@ -157,6 +157,15 @@ export interface PublicProductListOptions {
|
|
|
157
157
|
*/
|
|
158
158
|
now?: Date;
|
|
159
159
|
}
|
|
160
|
+
/** Every field path a per-type facet can filter on. Read by the wiring audit. */
|
|
161
|
+
export declare const TYPE_FACET_FIELD_PATHS: string[];
|
|
162
|
+
/**
|
|
163
|
+
* The ONE sort a per-type facet query is fetched at, so each facet needs one
|
|
164
|
+
* composite index instead of one per offered sort. The user's chosen sort is
|
|
165
|
+
* applied in memory afterwards. Changing this string invalidates all 8 indexes
|
|
166
|
+
* — the audit derives its expected shapes from it, so it will say so.
|
|
167
|
+
*/
|
|
168
|
+
export declare const FACET_FETCH_SORT: string;
|
|
160
169
|
/**
|
|
161
170
|
* Read the shared public-listing query params. SSR views hand in Next's
|
|
162
171
|
* `searchParams` record; the API route hands in `url.searchParams`. Both produce
|
|
@@ -104,6 +104,16 @@ function num(raw) {
|
|
|
104
104
|
* declared these keys in their FILTER_KEYS (so they counted toward the
|
|
105
105
|
* filter badge) and rendered the controls, but never put them on the wire.
|
|
106
106
|
*/
|
|
107
|
+
/*
|
|
108
|
+
* Facet key → the Firestore field path it filters.
|
|
109
|
+
*
|
|
110
|
+
* 🛑 Every value here MUST have an entry in the products `SIEVE_FIELDS`, and
|
|
111
|
+
* a `(status, listingType, <field>, createdAt DESC)` composite index. Miss the
|
|
112
|
+
* first and sievejs drops the clause silently; miss the second and the query
|
|
113
|
+
* throws FAILED_PRECONDITION, which `runQuery` logs and turns into an empty
|
|
114
|
+
* grid. Both read to the user as "this filter matches nothing".
|
|
115
|
+
* `scripts/audit-type-facet-wiring.mjs` checks all three ends.
|
|
116
|
+
*/
|
|
107
117
|
const TYPE_FACET_FIELD = {
|
|
108
118
|
[TABLE_KEYS.CITY]: "classified.meetupArea.city",
|
|
109
119
|
[TABLE_KEYS.NEGOTIABLE]: "classified.negotiable",
|
|
@@ -112,7 +122,17 @@ const TYPE_FACET_FIELD = {
|
|
|
112
122
|
[TABLE_KEYS.SPECIES]: "liveItem.species",
|
|
113
123
|
[TABLE_KEYS.LIVE_SEX]: "liveItem.sex",
|
|
114
124
|
[TABLE_KEYS.JURISDICTION]: "liveItem.jurisdictionAllowed",
|
|
125
|
+
[TABLE_KEYS.LIVE_TRANSPORT_METHOD]: "liveItem.transport.method",
|
|
115
126
|
};
|
|
127
|
+
/** Every field path a per-type facet can filter on. Read by the wiring audit. */
|
|
128
|
+
export const TYPE_FACET_FIELD_PATHS = Object.values(TYPE_FACET_FIELD);
|
|
129
|
+
/**
|
|
130
|
+
* The ONE sort a per-type facet query is fetched at, so each facet needs one
|
|
131
|
+
* composite index instead of one per offered sort. The user's chosen sort is
|
|
132
|
+
* applied in memory afterwards. Changing this string invalidates all 8 indexes
|
|
133
|
+
* — the audit derives its expected shapes from it, so it will say so.
|
|
134
|
+
*/
|
|
135
|
+
export const FACET_FETCH_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
|
|
116
136
|
/** Read every known per-type facet present in the params. */
|
|
117
137
|
function readTypeFacets(get) {
|
|
118
138
|
const out = {};
|
|
@@ -287,12 +307,21 @@ function buildFirestoreSafeFilters(input) {
|
|
|
287
307
|
// Search now travels as `query.search` (see PublicProductQuery) and becomes an
|
|
288
308
|
// `array-contains` on `searchTxt` inside the repository, which is the only
|
|
289
309
|
// shape Firestore can serve for token matching.
|
|
290
|
-
|
|
291
|
-
|
|
310
|
+
/*
|
|
311
|
+
* `array-contains-any` isn't supported by the Sieve Firebase adapter, so one
|
|
312
|
+
* value pushes down as `array-contains` and several cannot.
|
|
313
|
+
*
|
|
314
|
+
* "Falls through to the caller" was the old comment here, and no caller ever
|
|
315
|
+
* caught it — a two-tag selection filtered NOTHING while the chips stayed lit
|
|
316
|
+
* and the badge counted them. The multi-value case now runs as a per-row
|
|
317
|
+
* predicate over the bounded window (see `hasArrayMultiSelect` below), which
|
|
318
|
+
* is the same trade the per-type facets make. A partial pushdown is not an
|
|
319
|
+
* option: `array-contains` on the first value would return a strict SUBSET
|
|
320
|
+
* for OR semantics, quietly hiding rows that match only the second.
|
|
321
|
+
*/
|
|
292
322
|
if (input.features?.length === 1) {
|
|
293
323
|
parts.push(sieveFilter(PRODUCT_FIELDS.FEATURES, SIEVE_OP.CONTAINS, input.features[0]));
|
|
294
324
|
}
|
|
295
|
-
// `tags` is an array field, so the same one-value limit applies.
|
|
296
325
|
if (input.tags?.length === 1) {
|
|
297
326
|
parts.push(sieveFilter(PRODUCT_FIELDS.TAGS, SIEVE_OP.CONTAINS, input.tags[0]));
|
|
298
327
|
}
|
|
@@ -504,7 +533,29 @@ export async function listPublicProducts(input, opts) {
|
|
|
504
533
|
// ── Path A ("all") and the dense "available" case share one query. The only
|
|
505
534
|
// difference is whether a per-row predicate runs afterwards.
|
|
506
535
|
const filters = sieveAnd(safeFilters, ...dateClauses);
|
|
507
|
-
|
|
536
|
+
/*
|
|
537
|
+
* A per-type facet IS pushed down — it is a sparse equality, which is the
|
|
538
|
+
* side of the availability asymmetry that belongs in the query. What is not
|
|
539
|
+
* tractable is the SORT cross-product: `(status, listingType, <facet>,
|
|
540
|
+
* <sort>)` over 8 facets and the 7 publicly-offered sorts is 56 composite
|
|
541
|
+
* indexes, and every sort added later costs 8 more.
|
|
542
|
+
*
|
|
543
|
+
* So the facet goes down and the SORT comes back up: fetch a bounded window
|
|
544
|
+
* ordered by the one sort we index for every facet, then re-order in memory.
|
|
545
|
+
* 56 indexes become 8, a new sort costs none, and `truncated` keeps the
|
|
546
|
+
* pager honest — the same trade `fetchSorts` already makes for a date range
|
|
547
|
+
* that cannot ride its own sort.
|
|
548
|
+
*
|
|
549
|
+
* Before this, none of the 8 had a public index at all: every facet on
|
|
550
|
+
* /classified, /live and /digital-codes threw FAILED_PRECONDITION, which
|
|
551
|
+
* `runQuery` logs and returns as null, i.e. an empty grid. The store-scoped
|
|
552
|
+
* copies were indexed for 3 of the 7 sorts, so those pages worked until
|
|
553
|
+
* someone chose "Name: A–Z".
|
|
554
|
+
*/
|
|
555
|
+
const hasTypeFacet = Object.keys(input.typeFacets ?? {}).length > 0;
|
|
556
|
+
/** Multi-value `tags`/`features` — no `array-contains-any`, so per-row. */
|
|
557
|
+
const hasArrayMultiSelect = (input.tags?.length ?? 0) > 1 || (input.features?.length ?? 0) > 1;
|
|
558
|
+
const inMemory = wantAvailable || hasTypeFacet || hasArrayMultiSelect || (hasDateRange && !canPushDate);
|
|
508
559
|
// When a date range stays in memory, fetch in whichever direction front-loads
|
|
509
560
|
// the rows that will PASS it: `dateFrom` (">=", still live) wants the most
|
|
510
561
|
// future dates first; `dateTo` ("<=", ends by) wants the earliest first.
|
|
@@ -514,7 +565,9 @@ export async function listPublicProducts(input, opts) {
|
|
|
514
565
|
? input.dateFrom
|
|
515
566
|
? sortBy(dateField, "DESC")
|
|
516
567
|
: sortBy(dateField, "ASC")
|
|
517
|
-
:
|
|
568
|
+
: hasTypeFacet
|
|
569
|
+
? FACET_FETCH_SORT
|
|
570
|
+
: sorts;
|
|
518
571
|
const result = await runQuery(executor, {
|
|
519
572
|
filters,
|
|
520
573
|
sorts: fetchSorts,
|
|
@@ -534,9 +587,24 @@ export async function listPublicProducts(input, opts) {
|
|
|
534
587
|
let truncated = false;
|
|
535
588
|
if (inMemory) {
|
|
536
589
|
truncated = items.length >= PUBLIC_PRODUCT_MAX_PAGE_SIZE;
|
|
590
|
+
/** OR semantics, matching the single-value `array-contains` pushdown. */
|
|
591
|
+
const matchesAny = (raw, wanted) => {
|
|
592
|
+
if (!Array.isArray(raw))
|
|
593
|
+
return false;
|
|
594
|
+
return wanted.some((w) => raw.includes(w));
|
|
595
|
+
};
|
|
537
596
|
const filtered = items.filter((item) => {
|
|
538
597
|
if (wantAvailable && !isListingRowAvailable(item, now))
|
|
539
598
|
return false;
|
|
599
|
+
if (hasArrayMultiSelect) {
|
|
600
|
+
if ((input.tags?.length ?? 0) > 1 && !matchesAny(item[PRODUCT_FIELDS.TAGS], input.tags)) {
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
if ((input.features?.length ?? 0) > 1 &&
|
|
604
|
+
!matchesAny(item[PRODUCT_FIELDS.FEATURES], input.features)) {
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
540
608
|
if (hasDateRange && !canPushDate && dateField) {
|
|
541
609
|
const raw = item[dateField];
|
|
542
610
|
if (!raw)
|
|
@@ -638,10 +706,30 @@ export async function listStoreProducts(storeId, listingTypes, searchParams = {}
|
|
|
638
706
|
storeId,
|
|
639
707
|
});
|
|
640
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* Comparable primitive for one row value.
|
|
711
|
+
*
|
|
712
|
+
* 🛑 The `Date` branch is load-bearing. Without it a Date falls through to
|
|
713
|
+
* `String(value)` — `"Wed Aug 30 2026 …"` — and a lexical compare of those
|
|
714
|
+
* orders by WEEKDAY NAME. Every in-memory date sort was wrong: the "Sold &
|
|
715
|
+
* Ended" tab ordered by `createdAt`/`auctionEndDate`, and now every per-type
|
|
716
|
+
* facet under "Oldest First". A Firestore Timestamp arrives here as a Date via
|
|
717
|
+
* `mapDoc`, so the ISO-string case is only for a row that has already been
|
|
718
|
+
* serialised; both are handled.
|
|
719
|
+
*/
|
|
720
|
+
function sortKey(value) {
|
|
721
|
+
if (value == null)
|
|
722
|
+
return null;
|
|
723
|
+
if (value instanceof Date)
|
|
724
|
+
return value.getTime();
|
|
725
|
+
if (typeof value === "number" || typeof value === "string")
|
|
726
|
+
return value;
|
|
727
|
+
return String(value);
|
|
728
|
+
}
|
|
641
729
|
function sortRows(rows, field, desc) {
|
|
642
730
|
return [...rows].sort((a, b) => {
|
|
643
|
-
const av = a[field];
|
|
644
|
-
const bv = b[field];
|
|
731
|
+
const av = sortKey(a[field]);
|
|
732
|
+
const bv = sortKey(b[field]);
|
|
645
733
|
if (av == null && bv == null)
|
|
646
734
|
return 0;
|
|
647
735
|
if (av == null)
|
|
@@ -12,7 +12,7 @@ const ALLOWED_STATUS_TRANSITIONS = {
|
|
|
12
12
|
};
|
|
13
13
|
/** Assert a product exists and belongs to the given seller. Throws on failure. */
|
|
14
14
|
export async function assertProductOwnership(productId, sellerId) {
|
|
15
|
-
const product = await productRepository.findById(productId)
|
|
15
|
+
const product = await productRepository.findById(productId);
|
|
16
16
|
if (!product)
|
|
17
17
|
throw new ProductNotFoundError(productId);
|
|
18
18
|
if (product.storeId !== sellerId)
|
|
@@ -33,7 +33,7 @@ export async function createCouponAction(input) {
|
|
|
33
33
|
export async function updateCouponAction(couponId, input) {
|
|
34
34
|
return wrapAction(async () => {
|
|
35
35
|
await requireRoleUser(["admin", "seller"]);
|
|
36
|
-
const coupon = await couponsRepository.findById(couponId)
|
|
36
|
+
const coupon = await couponsRepository.findById(couponId);
|
|
37
37
|
if (!coupon)
|
|
38
38
|
throw new CouponNotFoundError(couponId);
|
|
39
39
|
const parsed = updateCouponSchema.safeParse(input);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cache } from "react";
|
|
2
2
|
import { couponsRepository } from "../../../../repositories";
|
|
3
3
|
export const getCouponByCode = cache(async (code) => {
|
|
4
|
-
return (await couponsRepository.getCouponByCode(code)
|
|
4
|
+
return (await couponsRepository.getCouponByCode(code)) ?? null;
|
|
5
5
|
});
|
|
@@ -2,7 +2,7 @@ import { couponsRepository } from "../../../../repositories";
|
|
|
2
2
|
import { roundRupees } from "../../../../utils/number.formatter";
|
|
3
3
|
import { CouponNotFoundError, CouponExpiredError, CouponUsageLimitError, CouponPerUserLimitError, CouponMinPurchaseError, CouponScopeError, } from "../../../shared/features/promotions/errors";
|
|
4
4
|
export async function validateCoupon(input, userId) {
|
|
5
|
-
const coupon = await couponsRepository.getCouponByCode(input.code)
|
|
5
|
+
const coupon = await couponsRepository.getCouponByCode(input.code);
|
|
6
6
|
if (!coupon)
|
|
7
7
|
throw new CouponNotFoundError(input.code);
|
|
8
8
|
if (!coupon.validity?.isActive)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const getReviewsForProduct: (productId: string, limit?: number) => Promise<
|
|
1
|
+
export declare const getReviewsForProduct: (productId: string, limit?: number) => Promise<import("../../../..").ReviewDocument[]>;
|
|
2
2
|
export declare const getReviewsForStore: (storeId: string, page?: number, pageSize?: number) => Promise<import("../../../..").FirebaseSieveResult<import("../../../..").ReviewDocument> | {
|
|
3
3
|
items: never[];
|
|
4
4
|
total: number;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { cache } from "react";
|
|
2
2
|
import { reviewRepository, orderRepository } from "../../../../repositories";
|
|
3
|
+
import { safeRead } from "../../../../errors/safe-read";
|
|
3
4
|
import { REVIEWS_DETAIL_PAGE_SIZE } from "../../../shared/features/reviews/config";
|
|
4
5
|
export const getReviewsForProduct = cache(async (productId, limit = REVIEWS_DETAIL_PAGE_SIZE) => {
|
|
5
|
-
return reviewRepository.findApprovedByProduct(productId, limit)
|
|
6
|
+
return safeRead(() => reviewRepository.findApprovedByProduct(productId, limit), {
|
|
7
|
+
route: "/products",
|
|
8
|
+
key: "reviews.getReviewsForProduct",
|
|
9
|
+
fallback: [],
|
|
10
|
+
});
|
|
6
11
|
});
|
|
7
12
|
export const getReviewsForStore = cache(async (storeId, page = 1, pageSize = 20) => {
|
|
8
13
|
return reviewRepository.listForStore(storeId, { page, pageSize }).catch(() => ({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cache } from "react";
|
|
2
2
|
import { productRepository } from "../../../../repositories";
|
|
3
|
+
import { safeRead } from "../../../../errors/safe-read";
|
|
3
4
|
import { loadProductFeaturesForStore } from "../../../../features/products/repository/loadProductFeatures";
|
|
4
5
|
// Returns a React.cache()-wrapped fetch gated on one listing type.
|
|
5
6
|
// The guard prevents cross-type URL collisions (e.g. /auctions/classified-slug returning wrong product).
|
|
@@ -7,7 +8,7 @@ export function makeGetListingForDetail(type) {
|
|
|
7
8
|
return cache(async (slugOrId) => {
|
|
8
9
|
if (!slugOrId)
|
|
9
10
|
return null;
|
|
10
|
-
const product = await productRepository.findByIdOrSlug(slugOrId)
|
|
11
|
+
const product = await productRepository.findByIdOrSlug(slugOrId);
|
|
11
12
|
if (!product || product.listingType !== type)
|
|
12
13
|
return null;
|
|
13
14
|
return product;
|
|
@@ -15,7 +16,11 @@ export function makeGetListingForDetail(type) {
|
|
|
15
16
|
}
|
|
16
17
|
// Product-feature badges are keyed by store, not listing type.
|
|
17
18
|
// Per-type data.ts files re-export this under a type-specific name.
|
|
18
|
-
export const getProductFeaturesForStore = cache(async (storeId) =>
|
|
19
|
+
export const getProductFeaturesForStore = cache(async (storeId) => safeRead(() => loadProductFeaturesForStore(storeId), {
|
|
20
|
+
route: "/products",
|
|
21
|
+
key: "products.getProductFeaturesForStore",
|
|
22
|
+
fallback: [],
|
|
23
|
+
}));
|
|
19
24
|
// Returns a React.cache()-wrapped function for store SSR first-page data.
|
|
20
25
|
// Export names in stores/data.ts stay identical so consumers need no changes.
|
|
21
26
|
export function makeGetStoreListingsInitial(type, pageSize) {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* `legalPages`), which meant everything else was public by default and
|
|
14
14
|
* edge-cached for 300s. That shipped `gst.gstin` + the registered legal name
|
|
15
15
|
* and address, all 25 `commissions.*` fields, `laborRate.hourlyRate`,
|
|
16
|
-
* `emi.surchargeSellerSharePercent`, `
|
|
16
|
+
* `emi.surchargeSellerSharePercent`, `payment.adminCheckoutBypass`, and —
|
|
17
17
|
* because they weren't even declared on the interface —
|
|
18
18
|
* `adSettings.providerCredentials` (which the ADMIN ads endpoint bothers to
|
|
19
19
|
* mask) plus every draft/paused/scheduled ad in `adSettings.inventory`.
|
|
@@ -37,7 +37,7 @@ import type { PublicSiteSettings } from "../../../shared/features/site-settings/
|
|
|
37
37
|
* was traced to a call site; anything without one belongs in the private list,
|
|
38
38
|
* because an unused public field is pure attack surface.
|
|
39
39
|
*/
|
|
40
|
-
export declare const PUBLIC_SITE_SETTINGS_FIELDS: readonly ["contact", "payment", "
|
|
40
|
+
export declare const PUBLIC_SITE_SETTINGS_FIELDS: readonly ["contact", "payment", "listings", "notificationChannels", "announcementBar", "navConfig", "actionConfig", "background", "watermark"];
|
|
41
41
|
/** Everything else, with the reason it stays server-side. */
|
|
42
42
|
export declare const PRIVATE_SITE_SETTINGS_FIELDS: readonly ["id", "createdAt", "updatedAt", "credentials", "emailSettings", "legalPages", "commissions", "emi", "gst", "laborRate", "adSettings", "integrations", "platformLimits", "auctionConfig", "disabledRoutes", "siteName", "tagline", "motto", "logo", "favicon", "seo", "socialLinks", "features", "shipping", "returns", "faq", "aboutContent", "navbarConfig", "footerConfig", "theme", "featuredResults"];
|
|
43
43
|
export interface ToPublicSiteSettingsOptions {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
export const PUBLIC_SITE_SETTINGS_FIELDS = [
|
|
7
7
|
"contact", // user/orders/[id]/payment — contact.whatsappNumber (narrowed: no address/upiVpa)
|
|
8
8
|
"payment", // CheckoutRouteClient — payment.otpCheckoutThreshold (narrowed)
|
|
9
|
-
"
|
|
9
|
+
"listings", // useListingTypeFlags — narrowed to listings.listingTypes; categoryTypes has no client reader
|
|
10
10
|
"notificationChannels", // NotificationPreferencesPanel + CheckoutRouteClient (whatsapp.otpEnabled)
|
|
11
11
|
"announcementBar", // AdminSiteView; also the homepage banner
|
|
12
12
|
"navConfig", // NavbarWithSettings, DashboardLayoutClient
|
|
@@ -51,8 +51,11 @@ export const PRIVATE_SITE_SETTINGS_FIELDS = [
|
|
|
51
51
|
export function toPublicSiteSettings(doc, opts) {
|
|
52
52
|
return {
|
|
53
53
|
// Explicit sub-literals, never spreads. These two are the ones that carry
|
|
54
|
-
// real weight: `contact` drops `address` + `upiVpa`, and `
|
|
55
|
-
//
|
|
54
|
+
// real weight: `contact` drops `address` + `upiVpa`, and `payment` emits
|
|
55
|
+
// ONLY `otpCheckoutThreshold` — it also holds `adminCheckoutBypass` (the
|
|
56
|
+
// setting that skips OTP and payment capture) and `smsVerification`, both
|
|
57
|
+
// of which must never reach a client. `listings` is narrowed too:
|
|
58
|
+
// `categoryTypes` has no client reader.
|
|
56
59
|
contact: {
|
|
57
60
|
email: doc.contact?.email ?? "",
|
|
58
61
|
phone: doc.contact?.phone ?? "",
|
|
@@ -61,8 +64,8 @@ export function toPublicSiteSettings(doc, opts) {
|
|
|
61
64
|
payment: {
|
|
62
65
|
otpCheckoutThreshold: doc.payment?.otpCheckoutThreshold,
|
|
63
66
|
},
|
|
64
|
-
|
|
65
|
-
listingTypes: doc.
|
|
67
|
+
listings: {
|
|
68
|
+
listingTypes: doc.listings?.listingTypes,
|
|
66
69
|
},
|
|
67
70
|
notificationChannels: doc.notificationChannels,
|
|
68
71
|
announcementBar: doc.announcementBar,
|
|
@@ -5,7 +5,7 @@ import { STORES_PRODUCTS_PAGE_SIZE, STORES_SITEMAP_LIMIT, } from "../../../share
|
|
|
5
5
|
import { makeGetStoreListingsInitial } from "../shared/listing-data-factory";
|
|
6
6
|
/** Full store document by slug — deduped per request via React.cache(). */
|
|
7
7
|
export const getStoreForDetail = cache(async (slug) => {
|
|
8
|
-
return (await storeRepository.findBySlug(slug)
|
|
8
|
+
return (await storeRepository.findBySlug(slug)) ?? null;
|
|
9
9
|
});
|
|
10
10
|
// Collapse of three near-identical 14-line functions via factory — export names unchanged.
|
|
11
11
|
export const listStoreProductsInitial = makeGetStoreListingsInitial("standard", STORES_PRODUCTS_PAGE_SIZE);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cache } from "react";
|
|
2
2
|
import { categoriesRepository } from "../../../../repositories";
|
|
3
3
|
export const getSublistingCategoryForDetail = cache(async (slug) => {
|
|
4
|
-
return categoriesRepository.findBySlugAndType(slug, "sublisting")
|
|
4
|
+
return categoriesRepository.findBySlugAndType(slug, "sublisting");
|
|
5
5
|
});
|
|
@@ -2,8 +2,9 @@ export interface TestDataFlagged {
|
|
|
2
2
|
isTestData?: boolean;
|
|
3
3
|
}
|
|
4
4
|
export interface ViewerLike {
|
|
5
|
-
isTester?: boolean;
|
|
6
5
|
role?: string | null;
|
|
6
|
+
/** @deprecated Legacy flag; read via isTesterUser during the migration window. */
|
|
7
|
+
isTester?: boolean;
|
|
7
8
|
}
|
|
8
9
|
/** True when the viewer is allowed to see isTestData:true documents. */
|
|
9
10
|
export declare function canViewTestData(viewer: ViewerLike | null | undefined): boolean;
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* (a `!=` clause) would silently exclude every pre-existing document that doesn't have
|
|
8
8
|
* the field set at all, which is worse than the problem it solves.
|
|
9
9
|
*/
|
|
10
|
-
import { isAdminUser } from "../../../../features/auth/role-predicates";
|
|
10
|
+
import { isAdminUser, isTesterUser } from "../../../../features/auth/role-predicates";
|
|
11
11
|
/** True when the viewer is allowed to see isTestData:true documents. */
|
|
12
12
|
export function canViewTestData(viewer) {
|
|
13
|
-
return
|
|
13
|
+
return isTesterUser(viewer) || isAdminUser(viewer);
|
|
14
14
|
}
|
|
15
15
|
/** Strips isTestData:true items from a list unless the viewer is a tester or admin. */
|
|
16
16
|
export function filterTestDataForViewer(items, viewer) {
|
|
@@ -7,6 +7,8 @@ export declare const couponExpiry: import("./types").ScheduledFunctionDefinition
|
|
|
7
7
|
export declare const offerExpiry: import("./types").ScheduledFunctionDefinition;
|
|
8
8
|
export declare const productStatsSync: import("./types").ScheduledFunctionDefinition;
|
|
9
9
|
export declare const revenueRollup: import("./types").ScheduledFunctionDefinition;
|
|
10
|
+
export declare const pageViewRollup: import("./types").ScheduledFunctionDefinition;
|
|
11
|
+
export declare const pageViewPrune: import("./types").ScheduledFunctionDefinition;
|
|
10
12
|
export declare const dailyStatusDigest: import("./types").ScheduledFunctionDefinition;
|
|
11
13
|
export declare const dailyDataCleanup: import("./types").ScheduledFunctionDefinition;
|
|
12
14
|
export declare const countersReconcile: import("./types").ScheduledFunctionDefinition;
|
|
@@ -25,4 +27,4 @@ export declare const prizeDrawExpiryReveal: import("./types").ScheduledFunctionD
|
|
|
25
27
|
export declare const bundleStockSync: import("./types").ScheduledFunctionDefinition;
|
|
26
28
|
export declare const emiInstallmentReminder: import("./types").ScheduledFunctionDefinition;
|
|
27
29
|
export declare const catalogueImageStalenessReminder: import("./types").ScheduledFunctionDefinition;
|
|
28
|
-
export declare const SCHEDULED_FUNCTIONS: readonly [import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition];
|
|
30
|
+
export declare const SCHEDULED_FUNCTIONS: readonly [import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Scheduled function definitions (Cloud Scheduler triggers).
|
|
2
2
|
// Cron syntax accepts both Firebase shorthand and standard cron expressions.
|
|
3
|
-
import { auctionSettlementHandler, autoPayoutEligibilityHandler, bundleStockSyncHandler, cartPruneHandler, cleanupRtdbEventsHandler, countersReconcileHandler, couponExpiryHandler, dailyDataCleanupHandler, draftPruneHandler, emiInstallmentReminderHandler, catalogueImageStalenessReminderHandler, mediaTmpCleanupHandler, notificationPruneHandler, offerExpiryHandler, payoutBatchHandler, paymentWindowTimeoutHandler, hardBanReinstatementHandler, paymentReviewAutoApproveHandler, pendingOrderTimeoutHandler, positionsReconcileHandler, prizeDrawExpiryRevealHandler, productStatsSyncHandler, revenueRollupHandler, dailyStatusDigestHandler, weeklyPayoutEligibilityHandler, testerSandboxCleanupHandler, testerSandboxRefreshHandler, } from "../jobs/handlers";
|
|
3
|
+
import { auctionSettlementHandler, autoPayoutEligibilityHandler, bundleStockSyncHandler, cartPruneHandler, cleanupRtdbEventsHandler, countersReconcileHandler, couponExpiryHandler, dailyDataCleanupHandler, draftPruneHandler, emiInstallmentReminderHandler, catalogueImageStalenessReminderHandler, mediaTmpCleanupHandler, notificationPruneHandler, offerExpiryHandler, payoutBatchHandler, paymentWindowTimeoutHandler, hardBanReinstatementHandler, paymentReviewAutoApproveHandler, pendingOrderTimeoutHandler, positionsReconcileHandler, prizeDrawExpiryRevealHandler, productStatsSyncHandler, revenueRollupHandler, pageViewRollupHandler, pageViewPruneHandler, dailyStatusDigestHandler, weeklyPayoutEligibilityHandler, testerSandboxCleanupHandler, testerSandboxRefreshHandler, } from "../jobs/handlers";
|
|
4
4
|
import { defineFunction } from "./define";
|
|
5
5
|
const REGION = "asia-south1";
|
|
6
6
|
const EVERY_5_MIN = "every 5 minutes";
|
|
@@ -67,6 +67,30 @@ export const revenueRollup = defineFunction({
|
|
|
67
67
|
handler: revenueRollupHandler,
|
|
68
68
|
options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
|
|
69
69
|
});
|
|
70
|
+
/*
|
|
71
|
+
* The rollup runs BEFORE the prune, and both before the retention boundary
|
|
72
|
+
* moves. `pageViews` had neither until 2026-08-31: it wrote one document per
|
|
73
|
+
* entity per day, forever, and was the only unbounded writer among 27 scheduled
|
|
74
|
+
* functions.
|
|
75
|
+
*
|
|
76
|
+
* Order matters — the prune deletes the day-buckets the rollup reads, so a
|
|
77
|
+
* prune that ran first would delete views nobody had counted yet. 01:45 then
|
|
78
|
+
* 02:15, either side of nothing else.
|
|
79
|
+
*/
|
|
80
|
+
export const pageViewRollup = defineFunction({
|
|
81
|
+
name: "pageViewRollup",
|
|
82
|
+
description: "Fold new page-view day-buckets into analytics/pageViewRollup so the prune below cannot delete uncounted views (daily 01:45 UTC).",
|
|
83
|
+
trigger: { kind: "schedule", cron: "45 1 * * *", timeZone: "UTC" },
|
|
84
|
+
handler: pageViewRollupHandler,
|
|
85
|
+
options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
|
|
86
|
+
});
|
|
87
|
+
export const pageViewPrune = defineFunction({
|
|
88
|
+
name: "pageViewPrune",
|
|
89
|
+
description: "Delete page-view day-buckets past the 90-day retention window; lifetime totals survive in the rollup (daily 02:15 UTC).",
|
|
90
|
+
trigger: { kind: "schedule", cron: "15 2 * * *", timeZone: "UTC" },
|
|
91
|
+
handler: pageViewPruneHandler,
|
|
92
|
+
options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
|
|
93
|
+
});
|
|
70
94
|
export const dailyStatusDigest = defineFunction({
|
|
71
95
|
name: "dailyStatusDigest",
|
|
72
96
|
description: "Email the previous 24h order summary + platform status to the team every morning at 10:00 IST.",
|
|
@@ -203,6 +227,8 @@ export const SCHEDULED_FUNCTIONS = [
|
|
|
203
227
|
offerExpiry,
|
|
204
228
|
productStatsSync,
|
|
205
229
|
revenueRollup,
|
|
230
|
+
pageViewRollup,
|
|
231
|
+
pageViewPrune,
|
|
206
232
|
dailyStatusDigest,
|
|
207
233
|
dailyDataCleanup,
|
|
208
234
|
countersReconcile,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { bidRepository, cartRepository, productRepository, storeRepository, } from "../../../../repositories";
|
|
2
2
|
import { sendNotification } from "../../../../features/admin/actions/notification-actions";
|
|
3
3
|
import { normalizeError } from "../../../../errors/normalize";
|
|
4
|
+
import { safeRead } from "../../../../errors/safe-read";
|
|
4
5
|
import { AUCTION_MESSAGES, BID_MESSAGES } from "../handlers/messages";
|
|
5
6
|
import { ROUTES } from "../../../../next/routing/route-map";
|
|
6
7
|
import { CART_LANE, LANE_CHECKOUT_WINDOW_MS } from "../../../shared/checkout/lanes";
|
|
@@ -94,7 +95,11 @@ async function settleAuction(ctx, product) {
|
|
|
94
95
|
productRepository.updateStatusInBatch(batch, product.id, "archived");
|
|
95
96
|
await batch.commit();
|
|
96
97
|
const reserveStore = product.storeId
|
|
97
|
-
? await storeRepository.findById(product.storeId)
|
|
98
|
+
? await safeRead(() => storeRepository.findById(product.storeId), {
|
|
99
|
+
route: "job:auctionSettlement",
|
|
100
|
+
key: "auctionSettlement.reserveNotMet.store",
|
|
101
|
+
fallback: null,
|
|
102
|
+
})
|
|
98
103
|
: null;
|
|
99
104
|
if (reserveStore?.ownerId) {
|
|
100
105
|
await sendNotification({
|
|
@@ -79,6 +79,10 @@ export async function runHardBanCascade(input, ctx) {
|
|
|
79
79
|
// ban to take effect. Left unguarded so a failure here marks the whole job
|
|
80
80
|
// failed (matches the original route's behavior of surfacing a 500).
|
|
81
81
|
await userRepository.update(uid, {
|
|
82
|
+
// BOTH fields: every server-side guard reads `disabled`, and until
|
|
83
|
+
// 2026-08-29 no ban path wrote it — so a hard ban blocked a fresh login and
|
|
84
|
+
// left every existing session fully authenticated.
|
|
85
|
+
disabled: true,
|
|
82
86
|
isDisabled: true,
|
|
83
87
|
hardBanReason: reason,
|
|
84
88
|
hardBannedAt: new Date(),
|
|
@@ -12,6 +12,8 @@ export { runCouponExpiry } from "./couponExpiry";
|
|
|
12
12
|
export { runOfferExpiry } from "./offerExpiry";
|
|
13
13
|
export { runCartPrune } from "./cartPrune";
|
|
14
14
|
export { runNotificationPrune } from "./notificationPrune";
|
|
15
|
+
export { runPageViewRollup } from "./pageViewRollup";
|
|
16
|
+
export { runPageViewPrune, PAGE_VIEW_TTL_DAYS } from "./pageViewPrune";
|
|
15
17
|
export { runDailyDataCleanup } from "./dailyDataCleanup";
|
|
16
18
|
export { runCleanupRtdbEvents } from "./cleanupRtdbEvents";
|
|
17
19
|
export { runMediaTmpCleanup } from "./mediaTmpCleanup";
|
|
@@ -13,6 +13,8 @@ export { runCouponExpiry } from "./couponExpiry";
|
|
|
13
13
|
export { runOfferExpiry } from "./offerExpiry";
|
|
14
14
|
export { runCartPrune } from "./cartPrune";
|
|
15
15
|
export { runNotificationPrune } from "./notificationPrune";
|
|
16
|
+
export { runPageViewRollup } from "./pageViewRollup";
|
|
17
|
+
export { runPageViewPrune, PAGE_VIEW_TTL_DAYS } from "./pageViewPrune";
|
|
16
18
|
export { runDailyDataCleanup } from "./dailyDataCleanup";
|
|
17
19
|
export { runCleanupRtdbEvents } from "./cleanupRtdbEvents";
|
|
18
20
|
export { runMediaTmpCleanup } from "./mediaTmpCleanup";
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { normalizeError } from "../../../../errors/normalize";
|
|
2
|
-
import { getAdminRealtimeDb } from "../../../../providers/db-firebase";
|
|
3
1
|
import { decryptPii } from "../../../../security/index";
|
|
4
2
|
import { sendNotification } from "../../../../features/admin/actions/notification-actions";
|
|
5
3
|
import { ORDER_MESSAGES } from "../handlers/messages";
|
|
@@ -58,19 +56,13 @@ export async function handleOrderStatusChange(input, ctx) {
|
|
|
58
56
|
userEmail,
|
|
59
57
|
orderWhatsappAddonPaid: after.whatsappNotifyAddon === true,
|
|
60
58
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
catch (rtdbError) {
|
|
71
|
-
void normalizeError(rtdbError);
|
|
72
|
-
ctx.logger.error("Realtime DB push failed (non-fatal)", rtdbError);
|
|
73
|
-
}
|
|
59
|
+
// The RTDB mirror that used to live here has been removed. `sendNotification`
|
|
60
|
+
// above is the real path (in-app + email + WhatsApp); the extra
|
|
61
|
+
// `notifications/{uid}` push was a duplicate that NOTHING read — no client
|
|
62
|
+
// subscription to that node has ever existed — and nothing pruned, so it
|
|
63
|
+
// accumulated one node per order transition per user forever. Its rule's
|
|
64
|
+
// `.type` validator also only allowed `info|success|warning|error`, none of
|
|
65
|
+
// which are the values written here (`order_confirmed`, `order_shipped`, …).
|
|
74
66
|
ctx.logger.info(`Order ${orderId} status → ${newStatus}`, { userId: after.userId });
|
|
75
67
|
}
|
|
76
68
|
catch (error) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { JobContext } from "../runtime/types";
|
|
2
|
+
/**
|
|
3
|
+
* 90 days.
|
|
4
|
+
*
|
|
5
|
+
* Long enough for a quarter-over-quarter comparison in the admin report, which
|
|
6
|
+
* is the longest range that screen offers, and short enough that the collection
|
|
7
|
+
* reaches a steady state instead of growing forever.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PAGE_VIEW_TTL_DAYS = 90;
|
|
10
|
+
export declare function runPageViewPrune(ctx: JobContext): Promise<void>;
|