@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
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
|
-
import { useState } from "react";
|
|
4
|
+
import React, { useState } from "react";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { Alert,
|
|
7
|
-
import { Form } from "../../../ui/components/Form";
|
|
6
|
+
import { Alert, Div, Heading, Row, SiteMark, Stack, Text } from "../../../ui";
|
|
8
7
|
import { FieldInput } from "../../../ui/forms/FieldInput";
|
|
9
8
|
import { FieldCheckbox } from "../../../ui/forms/FieldCheckbox";
|
|
10
9
|
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
|
|
10
|
+
import { FormShellContext, useFormShellState } from "../../../ui/forms/FormShell";
|
|
11
11
|
import { registerSchema, registerPasswordSchema } from "../schemas";
|
|
12
12
|
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
13
|
+
import { SectionForm, useSectionFormNav } from "../../shell/SectionForm";
|
|
13
14
|
const registerClientSchema = registerSchema.extend({
|
|
14
15
|
displayName: z.string().min(1, "Enter your name"),
|
|
15
16
|
confirmPassword: registerPasswordSchema,
|
|
@@ -20,8 +21,35 @@ const registerClientSchema = registerSchema.extend({
|
|
|
20
21
|
message: "Passwords do not match",
|
|
21
22
|
path: ["confirmPassword"],
|
|
22
23
|
});
|
|
24
|
+
/*
|
|
25
|
+
* Hand-authored rather than derived from the schema, which is the exception
|
|
26
|
+
* here and not the rule.
|
|
27
|
+
*
|
|
28
|
+
* Two reasons the generator is the wrong tool for this one form. `labels` is a
|
|
29
|
+
* PUBLIC prop of a published component — every string below is consumer
|
|
30
|
+
* overridable, and moving them into schema annotations would silently drop that
|
|
31
|
+
* override for anyone outside this repo. And `renderPasswordStrength` /
|
|
32
|
+
* `renderTermsLink` are POSITIONAL slots: a meter that must sit under the
|
|
33
|
+
* password box, a terms line that must sit under the checkbox. The generator
|
|
34
|
+
* places fields, not arbitrary nodes between them.
|
|
35
|
+
*
|
|
36
|
+
* One section, `required: true` — so it renders open, without a collapse
|
|
37
|
+
* control, i.e. exactly the flat list it has always been. A five-field signup
|
|
38
|
+
* form has nothing to group; what it gains here is the error→section
|
|
39
|
+
* attribution that makes `<FormErrorSummary>` able to jump to a field.
|
|
40
|
+
*/
|
|
41
|
+
function buildSections(labels, renderPasswordStrength, renderTermsLink) {
|
|
42
|
+
return [
|
|
43
|
+
{
|
|
44
|
+
id: "account",
|
|
45
|
+
label: labels.title ?? "Create Account",
|
|
46
|
+
required: true,
|
|
47
|
+
fields: ["displayName", "email", "password", "confirmPassword", "acceptTerms"],
|
|
48
|
+
render: ({ values, onChange }) => (_jsxs(_Fragment, { children: [_jsx(FieldInput, { name: "displayName", label: labels.displayNameLabel ?? "Full name", type: "text", autoComplete: "name", required: true, placeholder: labels.displayNamePlaceholder ?? "Your name", value: values.displayName, onChange: (v) => onChange({ displayName: v }) }), _jsx(FieldInput, { name: "email", label: labels.emailLabel ?? "Email address", type: "email", autoComplete: "username", required: true, placeholder: labels.emailPlaceholder ?? "you@example.com", value: values.email, onChange: (v) => onChange({ email: v }) }), _jsxs(Div, { children: [_jsx(FieldInput, { name: "password", label: labels.passwordLabel ?? "Password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.passwordPlaceholder ?? "••••••••", value: values.password, onChange: (v) => onChange({ password: v }) }), renderPasswordStrength?.(values.password)] }), _jsx(FieldInput, { name: "confirmPassword", label: labels.confirmPasswordLabel ?? "Confirm password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.confirmPasswordPlaceholder ?? "••••••••", value: values.confirmPassword, onChange: (v) => onChange({ confirmPassword: v }) }), _jsx(FieldCheckbox, { name: "acceptTerms", label: `${labels.acceptTermsLabel ?? "I accept the terms"}`, checked: values.acceptTerms, onChange: (c) => onChange({ acceptTerms: c }) }), renderTermsLink && (_jsx(Text, { size: "xs", variant: "secondary", children: renderTermsLink() }))] })),
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
}
|
|
23
52
|
export function RegisterForm({ onSubmit, isLoading = false, error, success, labels = {}, renderSocialButtons, renderLoginLink, renderTermsLink, renderPasswordStrength, className = "", }) {
|
|
24
|
-
const { showToast } = useToast();
|
|
25
53
|
const [values, setValues] = useState({
|
|
26
54
|
email: "",
|
|
27
55
|
password: "",
|
|
@@ -29,19 +57,34 @@ export function RegisterForm({ onSubmit, isLoading = false, error, success, labe
|
|
|
29
57
|
displayName: "",
|
|
30
58
|
acceptTerms: false,
|
|
31
59
|
});
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
60
|
+
const sections = React.useMemo(() => buildSections(labels, renderPasswordStrength, renderTermsLink), [labels, renderPasswordStrength, renderTermsLink]);
|
|
61
|
+
const nav = useSectionFormNav(sections, values, { scope: "auth:register" });
|
|
62
|
+
const { shellCtx, setFieldError, clearErrors } = useFormShellState(registerClientSchema, {
|
|
63
|
+
sections: nav.sectionMeta,
|
|
64
|
+
onGoToSection: nav.goToSection,
|
|
65
|
+
fieldToSectionIndex: nav.fieldToSectionIndex,
|
|
66
|
+
});
|
|
67
|
+
const handleSubmit = async () => {
|
|
68
|
+
clearErrors();
|
|
69
|
+
const parsed = registerClientSchema.safeParse(values);
|
|
70
|
+
if (!parsed.success) {
|
|
71
|
+
applyZodIssues(parsed.error.issues, setFieldError);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
await onSubmit(values);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
/*
|
|
79
|
+
* On a field, not a toast. "Registration failed" told the user nothing
|
|
80
|
+
* and discarded whatever the caller actually objected to — a taken
|
|
81
|
+
* email, a rejected password — which is the one thing they can act on.
|
|
82
|
+
*/
|
|
83
|
+
const normalized = normalizeError(err);
|
|
84
|
+
setFieldError("email", normalized.message || "Registration failed");
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Row, { justify: "center", className: "mb-3", children: _jsx(SiteMark, { size: "lg" }) }), _jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Create Account" }), (labels.subtitle || renderLoginLink) && (_jsxs(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: [labels.subtitle ?? "Already have an account?", " ", renderLoginLink?.()] }))] }), success && (_jsx(Alert, { variant: "success", compact: true, children: success })), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), _jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: values, onChange: (partial) => setValues((prev) => Object.assign({}, prev, partial)), onSubmit: handleSubmit, schema: registerClientSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: isLoading, submitLabel: isLoading
|
|
88
|
+
? (labels.submittingLabel ?? "Creating account…")
|
|
89
|
+
: (labels.submitLabel ?? "Create account") })] }), renderSocialButtons?.()] }) }));
|
|
47
90
|
}
|
|
@@ -30,7 +30,7 @@ export function ResetPasswordView({ oobCode, onSubmit, isLoading = false, error,
|
|
|
30
30
|
}
|
|
31
31
|
catch (err) {
|
|
32
32
|
void normalizeError(err);
|
|
33
|
-
showToast(
|
|
33
|
+
showToast("Password reset failed", "error");
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
}
|
|
@@ -315,6 +315,9 @@ export function useRegister(options) {
|
|
|
315
315
|
// fail registration — the user can re-request verification later.
|
|
316
316
|
try {
|
|
317
317
|
await authProvider.sendEmailVerification();
|
|
318
|
+
// Non-fatal by design: the account already exists, and the user can
|
|
319
|
+
// re-request verification from Settings at any time. Firebase surfaces
|
|
320
|
+
// its own send failures; failing registration here would be worse.
|
|
318
321
|
}
|
|
319
322
|
catch (err) {
|
|
320
323
|
void normalizeError(err);
|
|
@@ -11,7 +11,33 @@
|
|
|
11
11
|
* Permission naming: admin:resource:action
|
|
12
12
|
* Ban action naming: plain verb phrase — write_reviews, place_bids, etc.
|
|
13
13
|
*/
|
|
14
|
-
export type Permission = "admin:dashboard:view" | "admin:users:read" | "admin:users:write" | "admin:users:delete" | "admin:user-bans:read" | "admin:user-bans:write" | "admin:products:read" | "admin:products:write" | "admin:products:delete" | "admin:orders:read" | "admin:orders:write" | "admin:returns:read" | "admin:returns:write" | "admin:stores:read" | "admin:stores:write" | "admin:store-addresses:read" | "admin:addresses:read" | "admin:addresses:write" | "admin:analytics:view" | "admin:payouts:read" | "admin:payouts:write" | "admin:audit-log:read" | "admin:categories:read" | "admin:categories:write" | "admin:categories:delete" | "admin:brands:read" | "admin:brands:write" | "admin:brands:delete" | "admin:coupons:read" | "admin:coupons:write" | "admin:coupons:delete" | "admin:deals:read" | "admin:deals:write" | "admin:featured:read" | "admin:featured:write" | "admin:reviews:read" | "admin:reviews:write" | "admin:reviews:delete" | "admin:blog:read" | "admin:blog:write" | "admin:blog:delete" | "admin:blog:publish" | "admin:bids:read" | "admin:bids:write" | "admin:offers:read" | "admin:offers:write" | "admin:media:read" | "admin:media:write" | "admin:media:delete" | "admin:site:read" | "admin:site:write" | "admin:settings:write" | "admin:navigation:read" | "admin:navigation:write" | "admin:sections:read" | "admin:sections:write" | "admin:carousel:read" | "admin:carousel:write" | "admin:carousel:delete" | "admin:ads:read" | "admin:ads:write" | "admin:ads:delete" | "admin:faqs:read" | "admin:faqs:write" | "admin:faqs:delete" | "admin:tester-checklist:read" | "admin:tester-checklist:write" | "admin:tester-checklist:delete" | "admin:tester-feedback:read" | "admin:tester-feedback:write" | "admin:newsletter:read" | "admin:newsletter:write" | "admin:contact:read"
|
|
14
|
+
export type Permission = "admin:dashboard:view" | "admin:users:read" | "admin:users:write" | "admin:users:delete" | "admin:user-bans:read" | "admin:user-bans:write" | "admin:products:read" | "admin:products:write" | "admin:products:delete" | "admin:orders:read" | "admin:orders:write" | "admin:returns:read" | "admin:returns:write" | "admin:stores:read" | "admin:stores:write" | "admin:store-addresses:read" | "admin:addresses:read" | "admin:addresses:write" | "admin:analytics:view" | "admin:payouts:read" | "admin:payouts:write" | "admin:audit-log:read" | "admin:categories:read" | "admin:categories:write" | "admin:categories:delete" | "admin:brands:read" | "admin:brands:write" | "admin:brands:delete" | "admin:coupons:read" | "admin:coupons:write" | "admin:coupons:delete" | "admin:deals:read" | "admin:deals:write" | "admin:featured:read" | "admin:featured:write" | "admin:reviews:read" | "admin:reviews:write" | "admin:reviews:delete" | "admin:blog:read" | "admin:blog:write" | "admin:blog:delete" | "admin:blog:publish" | "admin:bids:read" | "admin:bids:write" | "admin:offers:read" | "admin:offers:write" | "admin:media:read" | "admin:media:write" | "admin:media:delete" | "admin:site:read" | "admin:site:write" | "admin:settings:write" | "admin:navigation:read" | "admin:navigation:write" | "admin:sections:read" | "admin:sections:write" | "admin:carousel:read" | "admin:carousel:write" | "admin:carousel:delete" | "admin:ads:read" | "admin:ads:write" | "admin:ads:delete" | "admin:faqs:read" | "admin:faqs:write" | "admin:faqs:delete" | "admin:tester-checklist:read" | "admin:tester-checklist:write" | "admin:tester-checklist:delete" | "admin:tester-feedback:read" | "admin:tester-feedback:write" | "admin:newsletter:read" | "admin:newsletter:write" | "admin:contact:read"
|
|
15
|
+
/** Destructive/mutating contact-submission actions. Added 2026-08-29 —
|
|
16
|
+
* `DELETE /api/admin/contact-submissions/[id]` declared `admin:contact:delete`,
|
|
17
|
+
* which is not a member of this union and therefore could never be granted. */
|
|
18
|
+
| "admin:contact:write" | "admin:moderation:read" | "admin:roles:read"
|
|
19
|
+
/**
|
|
20
|
+
* Skip OTP + payment when placing an order. Replaces
|
|
21
|
+
* `siteSettings.featureFlags.adminCheckoutBypass`: a capability granted to a
|
|
22
|
+
* person belongs in the permission system, not in a site-wide toggle that is
|
|
23
|
+
* either on for every admin or off for all of them.
|
|
24
|
+
*
|
|
25
|
+
* Named `admin:checkout:bypass` rather than the bare `checkout:bypass` so it
|
|
26
|
+
* matches the `admin:resource:action` convention every other member follows
|
|
27
|
+
* and is picked up by PERMISSION_DOMAINS prefix matching.
|
|
28
|
+
*/
|
|
29
|
+
| "admin:checkout:bypass"
|
|
30
|
+
/**
|
|
31
|
+
* Lets a `tester` reach `/admin/**` with real read/write access.
|
|
32
|
+
*
|
|
33
|
+
* Replaces the `canTestAdmin` boolean. Not a second role: the hierarchy is
|
|
34
|
+
* linear, so there is no level meaning "seller-level, plus read admin" that
|
|
35
|
+
* would not also outrank moderator and employee. Granted through the same
|
|
36
|
+
* `permissions[]` array employees already use, so there is one grant
|
|
37
|
+
* mechanism — and revoking it is a permission change, which the admin audit
|
|
38
|
+
* log already records.
|
|
39
|
+
*/
|
|
40
|
+
| "tester:admin-surfaces" | "admin:events:read" | "admin:events:write" | "admin:events:delete" | "admin:event-entries:read" | "admin:event-entries:write" | "admin:support-tickets:read" | "admin:support-tickets:write" | "admin:shipments:read" | "admin:shipments:write" | "admin:catalogue:read" | "admin:catalogue:write" | "admin:grouped-listings:read" | "admin:grouped-listings:write" | "admin:support-tickets:assign" | "admin:support-tickets:close" | "admin:scammers:read" | "admin:scammers:write" | "admin:scammers:verify" | "admin:scammers:delete" | "admin:sessions:read" | "admin:sessions:delete" | "admin:notifications:read" | "admin:notifications:write" | "admin:carts:read" | "admin:wishlists:read" | "admin:feature-flags:read" | "admin:feature-flags:write" | "admin:copilot:view" | "admin:team:read" | "admin:team:write" | "admin:maintenance:view-server-errors" | "admin:maintenance:view-client-errors" | "admin:maintenance:view-function-errors" | "admin:maintenance:view-cloud-logs" | "admin:maintenance:view-payment-rollbacks" | "admin:maintenance:run-analysis" | "admin:maintenance:purge-errors";
|
|
15
41
|
/**
|
|
16
42
|
* Granular user actions that can be soft-banned individually.
|
|
17
43
|
* A user may have multiple simultaneous soft bans of different types.
|
|
@@ -13,22 +13,90 @@ type RoleCarrier = {
|
|
|
13
13
|
role?: string | null;
|
|
14
14
|
} | null | undefined;
|
|
15
15
|
export declare const isAdminUser: (input: RoleCarrier) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* True for sellers AND testers.
|
|
18
|
+
*
|
|
19
|
+
* A QA tester owns a real store — `becomeSeller`/`createStore` auto-approve it —
|
|
20
|
+
* so every seller-facing surface must admit them. Making `tester` a peer role
|
|
21
|
+
* of `seller` rather than a boolean is what removed the `isTester` flag; this
|
|
22
|
+
* predicate is where that peerage is expressed.
|
|
23
|
+
*/
|
|
16
24
|
export declare const isSellerUser: (input: RoleCarrier) => boolean;
|
|
25
|
+
/** Strictly the `seller` role — for the rare case that must EXCLUDE testers. */
|
|
26
|
+
export declare const isStrictSellerUser: (input: RoleCarrier) => boolean;
|
|
17
27
|
export declare const isModeratorUser: (input: RoleCarrier) => boolean;
|
|
18
28
|
export declare const isEmployeeUser: (input: RoleCarrier) => boolean;
|
|
19
29
|
export declare const isBuyerUser: (input: RoleCarrier) => boolean;
|
|
20
30
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
31
|
+
* ── Tester identity: role first, legacy booleans second ──────────────────────
|
|
32
|
+
*
|
|
33
|
+
* `isTester` / `canTestAdmin` are being replaced by the `tester` role and the
|
|
34
|
+
* `tester:admin-surfaces` permission. Both predicates below read the NEW shape
|
|
35
|
+
* first and fall back to the OLD one, which is what makes the code safe to ship
|
|
36
|
+
* before the data migration: a tester whose document still says
|
|
37
|
+
* `{ role: "seller", isTester: true }` keeps working unchanged, and one already
|
|
38
|
+
* migrated to `{ role: "tester" }` works too.
|
|
39
|
+
*
|
|
40
|
+
* ⛔ Do NOT delete the fallbacks until the backfill has run and been verified.
|
|
41
|
+
* Removing them early locks every existing tester out of the Tester Hub and out
|
|
42
|
+
* of the auto-approved store the QA program depends on.
|
|
27
43
|
*/
|
|
28
|
-
type
|
|
44
|
+
type TesterCarrier = {
|
|
29
45
|
role?: string | null;
|
|
30
46
|
isTester?: boolean;
|
|
31
47
|
canTestAdmin?: boolean;
|
|
48
|
+
permissions?: readonly string[] | null;
|
|
32
49
|
} | null | undefined;
|
|
50
|
+
/** The `tester` role, or the legacy `isTester` flag. */
|
|
51
|
+
export declare const isTesterUser: (input: TesterCarrier) => boolean;
|
|
52
|
+
/**
|
|
53
|
+
* May this tester reach `/admin/**`?
|
|
54
|
+
*
|
|
55
|
+
* The `tester:admin-surfaces` permission, or the legacy `canTestAdmin` flag.
|
|
56
|
+
* Granted through the same `permissions[]` array employees already use, so it
|
|
57
|
+
* is one grant mechanism rather than a bespoke boolean — and revoking it is a
|
|
58
|
+
* permission change, which the admin audit log already records.
|
|
59
|
+
*/
|
|
60
|
+
export declare const canTestAdminSurfaces: (input: TesterCarrier) => boolean;
|
|
61
|
+
/**
|
|
62
|
+
* True for real admins OR a tester cleared for admin surfaces. Used at the two
|
|
63
|
+
* RBAC chokepoints — the API route guard (`createRouteHandler`) and the admin
|
|
64
|
+
* layout guard (`makeAdminSectionLayout`) — so a cleared tester gets real
|
|
65
|
+
* read/write access to `/admin/**` without holding the admin role.
|
|
66
|
+
*
|
|
67
|
+
* Callers must supply the role/permissions from a live Firestore read; none of
|
|
68
|
+
* these are present in the session-cookie JWT claims (only `role` is).
|
|
69
|
+
*/
|
|
70
|
+
type EffectiveAdminCarrier = TesterCarrier;
|
|
33
71
|
export declare const isEffectiveAdminUser: (input: EffectiveAdminCarrier) => boolean;
|
|
72
|
+
/**
|
|
73
|
+
* ── Is this account blocked? ─────────────────────────────────────────────────
|
|
74
|
+
*
|
|
75
|
+
* 🛑 There are TWO ban fields on `UserDocument` and, until 2026-08-29, no ban
|
|
76
|
+
* path wrote the one every guard reads.
|
|
77
|
+
*
|
|
78
|
+
* `disabled` — required, and read by EVERY server-side guard:
|
|
79
|
+
* auth-server, api-handler, authorization.requireActiveUser,
|
|
80
|
+
* Guards.requireActiveAccount, SessionContext.
|
|
81
|
+
* Its only writers, `userRepository.disable()/enable()`, had
|
|
82
|
+
* ZERO callers.
|
|
83
|
+
* `isDisabled` — optional, and what `hardBanCascade` and the bulk-suspend
|
|
84
|
+
* route actually set, alongside the Firebase Auth record.
|
|
85
|
+
*
|
|
86
|
+
* The consequence was that **no ban terminated an existing session.** A hard
|
|
87
|
+
* ban set Firebase Auth `disabled: true`, which blocks a fresh login — and
|
|
88
|
+
* nothing else. Every authenticated request from a session cookie the user
|
|
89
|
+
* already held kept passing, because each guard consulted a Firestore field
|
|
90
|
+
* the ban never touched. Silent, and invisible to anyone testing a ban by
|
|
91
|
+
* logging out first.
|
|
92
|
+
*
|
|
93
|
+
* This predicate reads BOTH so already-banned accounts are enforced from the
|
|
94
|
+
* moment it ships, without waiting on a backfill. Every write path now sets
|
|
95
|
+
* both fields too; `isDisabled` is removed once the data is reconciled.
|
|
96
|
+
*/
|
|
97
|
+
type BanCarrier = {
|
|
98
|
+
disabled?: boolean | null;
|
|
99
|
+
isDisabled?: boolean | null;
|
|
100
|
+
} | null | undefined;
|
|
101
|
+
export declare const isAccountDisabled: (input: BanCarrier) => boolean;
|
|
34
102
|
export {};
|
|
@@ -2,8 +2,34 @@ function normalizeRole(input) {
|
|
|
2
2
|
return (input?.role ?? "user");
|
|
3
3
|
}
|
|
4
4
|
export const isAdminUser = (input) => normalizeRole(input) === "admin";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* True for sellers AND testers.
|
|
7
|
+
*
|
|
8
|
+
* A QA tester owns a real store — `becomeSeller`/`createStore` auto-approve it —
|
|
9
|
+
* so every seller-facing surface must admit them. Making `tester` a peer role
|
|
10
|
+
* of `seller` rather than a boolean is what removed the `isTester` flag; this
|
|
11
|
+
* predicate is where that peerage is expressed.
|
|
12
|
+
*/
|
|
13
|
+
export const isSellerUser = (input) => {
|
|
14
|
+
const role = normalizeRole(input);
|
|
15
|
+
return role === "seller" || role === "tester";
|
|
16
|
+
};
|
|
17
|
+
/** Strictly the `seller` role — for the rare case that must EXCLUDE testers. */
|
|
18
|
+
export const isStrictSellerUser = (input) => normalizeRole(input) === "seller";
|
|
6
19
|
export const isModeratorUser = (input) => normalizeRole(input) === "moderator";
|
|
7
20
|
export const isEmployeeUser = (input) => normalizeRole(input) === "employee";
|
|
8
21
|
export const isBuyerUser = (input) => normalizeRole(input) === "user";
|
|
9
|
-
|
|
22
|
+
/** The `tester` role, or the legacy `isTester` flag. */
|
|
23
|
+
export const isTesterUser = (input) => normalizeRole(input) === "tester" || input?.isTester === true;
|
|
24
|
+
/**
|
|
25
|
+
* May this tester reach `/admin/**`?
|
|
26
|
+
*
|
|
27
|
+
* The `tester:admin-surfaces` permission, or the legacy `canTestAdmin` flag.
|
|
28
|
+
* Granted through the same `permissions[]` array employees already use, so it
|
|
29
|
+
* is one grant mechanism rather than a bespoke boolean — and revoking it is a
|
|
30
|
+
* permission change, which the admin audit log already records.
|
|
31
|
+
*/
|
|
32
|
+
export const canTestAdminSurfaces = (input) => Boolean(input?.permissions?.includes("tester:admin-surfaces")) ||
|
|
33
|
+
input?.canTestAdmin === true;
|
|
34
|
+
export const isEffectiveAdminUser = (input) => isAdminUser(input) || (isTesterUser(input) && canTestAdminSurfaces(input));
|
|
35
|
+
export const isAccountDisabled = (input) => input?.disabled === true || input?.isDisabled === true;
|
|
@@ -140,6 +140,28 @@ export interface UserDocument extends BaseDocument {
|
|
|
140
140
|
* stay unique). Absent/missing IDs default to collapsed for a section the
|
|
141
141
|
* user has never interacted with. See useCollapsedSections. */
|
|
142
142
|
collapsedSections?: string[];
|
|
143
|
+
/**
|
|
144
|
+
* Which sections are OPEN, namespaced per surface: `scope -> openIds[]`.
|
|
145
|
+
*
|
|
146
|
+
* Supersedes `collapsedSections`, and the polarity flip is the point.
|
|
147
|
+
* Storing what is COLLAPSED cannot express "section 1 open by default"
|
|
148
|
+
* without a sentinel, and an unseen scope has to be guessed at; storing
|
|
149
|
+
* what is OPEN makes a never-seen scope default correctly *by absence*.
|
|
150
|
+
*
|
|
151
|
+
* Namespaced because the flat list was global: every page's ids shared one
|
|
152
|
+
* array and were kept apart only by a naming convention. The scope key is
|
|
153
|
+
* `"{portal}:{surface}"` — deliberately NOT a hash of the section ids,
|
|
154
|
+
* which would change the moment a section is added and silently reset
|
|
155
|
+
* every user's layout.
|
|
156
|
+
*
|
|
157
|
+
* `collapsedSections` is still written during the migration window; see
|
|
158
|
+
* `useSectionState`'s one-way dual-read.
|
|
159
|
+
*/
|
|
160
|
+
sectionState?: Record<string, string[]>;
|
|
161
|
+
/** Preferred density for list surfaces that offer a view switch.
|
|
162
|
+
* Was written by the route, the API type and useDataViewMode while being
|
|
163
|
+
* absent from this interface — declared 2026-08-29. */
|
|
164
|
+
dataViewMode?: "table" | "grid" | "list";
|
|
143
165
|
/** Which side CTA panels/drawers/sidebars dock to and where close buttons
|
|
144
166
|
* sit. Absent/"right" = default (right-docked). "left" = left-hand mode. */
|
|
145
167
|
handMode?: "left" | "right";
|
|
@@ -26,9 +26,9 @@ export declare const loginSchema: z.ZodObject<{
|
|
|
26
26
|
*/
|
|
27
27
|
export declare const registerPasswordSchema: z.ZodString;
|
|
28
28
|
export declare const registerSchema: z.ZodObject<{
|
|
29
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
29
30
|
email: z.ZodString;
|
|
30
31
|
password: z.ZodString;
|
|
31
|
-
displayName: z.ZodOptional<z.ZodString>;
|
|
32
32
|
}, "strip", z.ZodTypeAny, {
|
|
33
33
|
email: string;
|
|
34
34
|
password: string;
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
export * from "./firestore";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { ERROR_MESSAGES } from "../../../errors/messages";
|
|
4
|
+
import { annotate } from "../../shell/field-ui-meta";
|
|
4
5
|
// --- Form schemas -------------------------------------------------------------
|
|
6
|
+
/*
|
|
7
|
+
* The `annotate()` calls below add UI metadata to a WeakMap keyed on the schema
|
|
8
|
+
* object; they return the same schema and change nothing about parsing. That is
|
|
9
|
+
* what makes them safe on `registerSchema`, which the register ROUTE also
|
|
10
|
+
* parses — the server never reads the registry.
|
|
11
|
+
*
|
|
12
|
+
* Every auth form is a single required section. That is not a section in any
|
|
13
|
+
* interesting sense, and it is not meant to be: `sectionRequired` renders the
|
|
14
|
+
* panel open and non-collapsible, so the page looks exactly as it does today
|
|
15
|
+
* and gains the pinned mobile action bar, which is the whole reason to put a
|
|
16
|
+
* short form through <SectionForm> at all.
|
|
17
|
+
*/
|
|
5
18
|
/**
|
|
6
19
|
* Login form schema — use with react-hook-form + zodResolver.
|
|
7
20
|
*
|
|
@@ -10,8 +23,13 @@ import { ERROR_MESSAGES } from "../../../errors/messages";
|
|
|
10
23
|
* const form = useForm({ resolver: zodResolver(loginSchema) });
|
|
11
24
|
*/
|
|
12
25
|
export const loginSchema = z.object({
|
|
13
|
-
email: z.string().email(),
|
|
14
|
-
|
|
26
|
+
email: annotate(z.string().email(), {
|
|
27
|
+
section: "basics", sectionLabel: "Sign in", sectionRequired: true,
|
|
28
|
+
order: 1, row: "full", inputType: "email", label: "Email",
|
|
29
|
+
}),
|
|
30
|
+
password: annotate(z.string().min(6), {
|
|
31
|
+
section: "basics", order: 2, row: "full", inputType: "password", label: "Password",
|
|
32
|
+
}),
|
|
15
33
|
});
|
|
16
34
|
/**
|
|
17
35
|
* Matches the server-side complexity rule enforced in
|
|
@@ -27,16 +45,34 @@ export const registerPasswordSchema = z
|
|
|
27
45
|
.regex(/[a-z]/, ERROR_MESSAGES.PASSWORD.NO_LOWERCASE)
|
|
28
46
|
.regex(/[0-9]/, ERROR_MESSAGES.PASSWORD.NO_NUMBER);
|
|
29
47
|
export const registerSchema = z.object({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
displayName: annotate(z.string().min(1).optional(), {
|
|
49
|
+
section: "basics", sectionLabel: "Create your account", sectionRequired: true,
|
|
50
|
+
order: 1, row: "full", label: "Display name",
|
|
51
|
+
}),
|
|
52
|
+
email: annotate(z.string().email(), {
|
|
53
|
+
section: "basics", order: 2, row: "full", inputType: "email", label: "Email",
|
|
54
|
+
}),
|
|
55
|
+
password: annotate(registerPasswordSchema, {
|
|
56
|
+
section: "basics", order: 3, row: "full", inputType: "password", label: "Password",
|
|
57
|
+
help: "At least 8 characters, with an uppercase letter, a lowercase letter and a number.",
|
|
58
|
+
}),
|
|
33
59
|
});
|
|
34
60
|
export const forgotPasswordSchema = z.object({
|
|
35
|
-
email: z.string().email(),
|
|
61
|
+
email: annotate(z.string().email(), {
|
|
62
|
+
section: "basics", sectionLabel: "Reset your password", sectionRequired: true,
|
|
63
|
+
order: 1, row: "full", inputType: "email", label: "Email",
|
|
64
|
+
}),
|
|
36
65
|
});
|
|
37
66
|
export const resetPasswordSchema = z.object({
|
|
38
|
-
|
|
39
|
-
|
|
67
|
+
// Carried in the reset link, never typed. `derived` keeps it out of the
|
|
68
|
+
// rendered form; the schema still requires it, because the SUBMIT needs it.
|
|
69
|
+
token: annotate(z.string().min(1), {
|
|
70
|
+
section: "basics", order: 1, derived: true, tier: "t2-server",
|
|
71
|
+
}),
|
|
72
|
+
password: annotate(z.string().min(6), {
|
|
73
|
+
section: "basics", sectionLabel: "Choose a new password", sectionRequired: true,
|
|
74
|
+
order: 2, row: "full", inputType: "password", label: "New password",
|
|
75
|
+
}),
|
|
40
76
|
});
|
|
41
77
|
// --- Auth user schema ---------------------------------------------------------
|
|
42
78
|
export const userRoleSchema = z.enum([
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `tester` sits at the SAME level as `seller` (see ROLE_HIERARCHY in
|
|
3
|
+
* security/authorization.ts) because a QA tester owns a real store and must
|
|
4
|
+
* pass every seller gate — the Tester QA Program depends on it.
|
|
5
|
+
*
|
|
6
|
+
* It replaces the `isTester` boolean. The companion `canTestAdmin` boolean
|
|
7
|
+
* became the `tester:admin-surfaces` permission rather than a second role,
|
|
8
|
+
* because this hierarchy is LINEAR: there is no level that means
|
|
9
|
+
* "seller-level, plus read admin" without also meaning admin.
|
|
10
|
+
*
|
|
11
|
+
* Both booleans are still read during the migration window — see
|
|
12
|
+
* `isTesterUser` / `canTestAdminSurfaces` in ./role-predicates.
|
|
13
|
+
*/
|
|
14
|
+
export type UserRole = "user" | "seller" | "tester" | "moderator" | "employee" | "admin";
|
|
2
15
|
export interface AuthUser {
|
|
3
16
|
id: string;
|
|
4
17
|
email?: string;
|
|
@@ -25,7 +25,15 @@ export async function GET(request) {
|
|
|
25
25
|
try {
|
|
26
26
|
const url = new URL(request.url);
|
|
27
27
|
const page = numParam(url, "page", 1);
|
|
28
|
-
|
|
28
|
+
/*
|
|
29
|
+
* `pageSize` is canonical; `perPage` is still read because this is a public
|
|
30
|
+
* GET and a link built before 2026-08-31 carries the old spelling. Ignoring
|
|
31
|
+
* it would silently serve the default page size with a 200 — the shape of
|
|
32
|
+
* failure this codebase keeps paying for.
|
|
33
|
+
*/
|
|
34
|
+
const pageSize = url.searchParams.has("perPage")
|
|
35
|
+
? numParam(url, "perPage", 20)
|
|
36
|
+
: numParam(url, "pageSize", 20);
|
|
29
37
|
const sort = param(url, "sort") ?? "-publishedAt";
|
|
30
38
|
const parts = [];
|
|
31
39
|
const category = param(url, "category");
|
|
@@ -52,7 +60,7 @@ export async function GET(request) {
|
|
|
52
60
|
// `blogRepository.listAll` rather than the generic `db.getRepository(...)`:
|
|
53
61
|
// only the feature repository knows how to push a searchTxt clause down and
|
|
54
62
|
// AND-refine the remaining terms.
|
|
55
|
-
const result = await blogRepository.listAll({ filters, sorts: sort, page, pageSize
|
|
63
|
+
const result = await blogRepository.listAll({ filters, sorts: sort, page, pageSize }, q ? { search: q } : undefined);
|
|
56
64
|
const meta = {
|
|
57
65
|
total: result.total,
|
|
58
66
|
page: result.page,
|
|
@@ -106,7 +106,7 @@ export function BlogIndexListing({ initialData }) {
|
|
|
106
106
|
tags: table.get(TABLE_KEYS.TAGS) || undefined,
|
|
107
107
|
sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
|
|
108
108
|
page: table.getNumber(TABLE_KEYS.PAGE, 1),
|
|
109
|
-
|
|
109
|
+
pageSize: table.getNumber(TABLE_KEYS.PAGE_SIZE, PAGE_SIZE),
|
|
110
110
|
};
|
|
111
111
|
const { posts, totalPages, isLoading } = useBlogPosts(params, { initialData });
|
|
112
112
|
const currentPage = table.getNumber("page", 1);
|
|
@@ -11,8 +11,8 @@ export function useBlogPosts(params = {}, opts) {
|
|
|
11
11
|
searchParams.set("tags", params.tags);
|
|
12
12
|
if (params.page)
|
|
13
13
|
searchParams.set("page", String(params.page));
|
|
14
|
-
if (params.
|
|
15
|
-
searchParams.set("
|
|
14
|
+
if (params.pageSize)
|
|
15
|
+
searchParams.set("pageSize", String(params.pageSize));
|
|
16
16
|
if (params.sort)
|
|
17
17
|
searchParams.set("sort", params.sort);
|
|
18
18
|
if (params.featured !== undefined)
|
|
@@ -72,10 +72,12 @@ class BlogRepository extends BaseRepository {
|
|
|
72
72
|
.collection(this.collection)
|
|
73
73
|
.doc(id)
|
|
74
74
|
.update({ [BLOG_POST_FIELDS.VIEWS]: increment(1) });
|
|
75
|
+
// Fire-and-forget analytics: a lost view increment changes nothing that
|
|
76
|
+
// renders and cannot usefully be retried, so it must never fail the blog
|
|
77
|
+
// post read that triggered it — the count is approximate by design.
|
|
75
78
|
}
|
|
76
79
|
catch (_err) {
|
|
77
80
|
void normalizeError(_err);
|
|
78
|
-
// Fire-and-forget by design.
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
async findRelated(category, excludeId, limit = 3) {
|
|
@@ -458,6 +458,8 @@ export declare const blogListParamsSchema: z.ZodObject<{
|
|
|
458
458
|
tags: z.ZodOptional<z.ZodString>;
|
|
459
459
|
q: z.ZodOptional<z.ZodString>;
|
|
460
460
|
page: z.ZodOptional<z.ZodNumber>;
|
|
461
|
+
pageSize: z.ZodOptional<z.ZodNumber>;
|
|
462
|
+
/** Accepted, never emitted — a link built before the 2026-08-31 rename. */
|
|
461
463
|
perPage: z.ZodOptional<z.ZodNumber>;
|
|
462
464
|
sort: z.ZodOptional<z.ZodString>;
|
|
463
465
|
featured: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -466,6 +468,7 @@ export declare const blogListParamsSchema: z.ZodObject<{
|
|
|
466
468
|
page?: number | undefined;
|
|
467
469
|
q?: string | undefined;
|
|
468
470
|
perPage?: number | undefined;
|
|
471
|
+
pageSize?: number | undefined;
|
|
469
472
|
category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
|
|
470
473
|
featured?: boolean | undefined;
|
|
471
474
|
tags?: string | undefined;
|
|
@@ -474,6 +477,7 @@ export declare const blogListParamsSchema: z.ZodObject<{
|
|
|
474
477
|
page?: number | undefined;
|
|
475
478
|
q?: string | undefined;
|
|
476
479
|
perPage?: number | undefined;
|
|
480
|
+
pageSize?: number | undefined;
|
|
477
481
|
category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
|
|
478
482
|
featured?: boolean | undefined;
|
|
479
483
|
tags?: string | undefined;
|
|
@@ -90,6 +90,8 @@ export const blogListParamsSchema = z.object({
|
|
|
90
90
|
tags: z.string().optional(),
|
|
91
91
|
q: z.string().optional(),
|
|
92
92
|
page: z.coerce.number().optional(),
|
|
93
|
+
pageSize: z.coerce.number().optional(),
|
|
94
|
+
/** Accepted, never emitted — a link built before the 2026-08-31 rename. */
|
|
93
95
|
perPage: z.coerce.number().optional(),
|
|
94
96
|
sort: z.string().optional(),
|
|
95
97
|
featured: z.coerce.boolean().optional(),
|
|
@@ -14,6 +14,7 @@ import { getDefaultCurrency } from "../../../core/baseline-resolver";
|
|
|
14
14
|
import { CART_LANE, activeLane, canAddNewItems, } from "../../../_internal/shared/checkout/lanes";
|
|
15
15
|
import { CART_MAX_ITEMS } from "../../../constants/limits";
|
|
16
16
|
import { getExpandedDecrements, validateCartItemStock, } from "../../../_internal/server/features/checkout/bundle-expansion";
|
|
17
|
+
import { safeRead } from "../../../errors/safe-read";
|
|
17
18
|
/**
|
|
18
19
|
* Product documents don't always carry a denormalized `storeName` — the
|
|
19
20
|
* create routes only set it when the caller explicitly passes one, so
|
|
@@ -26,7 +27,9 @@ import { getExpandedDecrements, validateCartItemStock, } from "../../../_interna
|
|
|
26
27
|
async function resolveStoreName(storeId, candidate) {
|
|
27
28
|
if (candidate)
|
|
28
29
|
return candidate;
|
|
29
|
-
const store = await storeRepository.findById(storeId)
|
|
30
|
+
const store = await safeRead(() => storeRepository.findById(storeId), {
|
|
31
|
+
route: "/cart", key: "stores.findById", fallback: null,
|
|
32
|
+
});
|
|
30
33
|
return store?.storeName ?? storeId;
|
|
31
34
|
}
|
|
32
35
|
/**
|
|
@@ -102,7 +105,9 @@ export async function addGroupLineToCart(userId, input) {
|
|
|
102
105
|
const cart = await assertCanAddNewItems(userId);
|
|
103
106
|
// One batched read for the whole selection — capped at 20 above, so well
|
|
104
107
|
// inside the per-request Firestore budget.
|
|
105
|
-
const docs = await Promise.all(members.map((m) => productRepository.findById(m.productId)
|
|
108
|
+
const docs = await Promise.all(members.map((m) => safeRead(() => productRepository.findById(m.productId), {
|
|
109
|
+
route: "/cart", key: "products.findById", fallback: null,
|
|
110
|
+
})));
|
|
106
111
|
const productById = new Map(members.map((m, i) => [m.productId, docs[i]]));
|
|
107
112
|
const { getListingRule } = await import("../../../_internal/shared/checkout/rules");
|
|
108
113
|
const { ProductStatusValues } = await import("../../products/schemas/firestore");
|
|
@@ -209,14 +214,18 @@ export async function addGroupLineToCart(userId, input) {
|
|
|
209
214
|
/** Is this product listed on that `groupedListings` document? */
|
|
210
215
|
async function isGroupedListingMember(groupId, productId) {
|
|
211
216
|
const { groupedListingsRepository } = await import("../../grouped/repository/grouped-listings.repository");
|
|
212
|
-
const group = await groupedListingsRepository.findById(groupId)
|
|
217
|
+
const group = await safeRead(() => groupedListingsRepository.findById(groupId), {
|
|
218
|
+
route: "/cart", key: "groupedListings.findById", fallback: null,
|
|
219
|
+
});
|
|
213
220
|
return Boolean(group?.productIds?.includes(productId));
|
|
214
221
|
}
|
|
215
222
|
/** Display name for the line. Falls back to the parent product's group title. */
|
|
216
223
|
async function resolveGroupTitle(groupId, groupSource, sample) {
|
|
217
224
|
if (groupSource === "grouped-listing") {
|
|
218
225
|
const { groupedListingsRepository } = await import("../../grouped/repository/grouped-listings.repository");
|
|
219
|
-
const group = await groupedListingsRepository.findById(groupId)
|
|
226
|
+
const group = await safeRead(() => groupedListingsRepository.findById(groupId), {
|
|
227
|
+
route: "/cart", key: "groupedListings.title", fallback: null,
|
|
228
|
+
});
|
|
220
229
|
if (group?.title)
|
|
221
230
|
return group.title;
|
|
222
231
|
}
|