@mohasinac/appkit 4.30.19 → 4.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_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 -87
- package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +16 -5
- package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -40
- 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/categories/data.js +15 -4
- 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/live/index.d.ts +0 -1
- package/dist/_internal/server/features/live/index.js +0 -1
- package/dist/_internal/server/features/products/data.js +15 -5
- 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/site-settings/adapters.d.ts +2 -2
- package/dist/_internal/server/features/site-settings/adapters.js +8 -5
- 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/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/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/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.d.ts +37 -20
- package/dist/client.js +36 -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/extend.d.ts +0 -9
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/realtime-session.js +7 -0
- 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/messages.d.ts +0 -8
- package/dist/errors/messages.js +0 -9
- 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/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/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/auth/actions/index.d.ts +0 -1
- package/dist/features/auth/actions/index.js +0 -1
- 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/Guards.js +2 -1
- package/dist/features/auth/components/LoginForm.d.ts +0 -2
- package/dist/features/auth/components/LoginForm.js +1 -3
- package/dist/features/auth/components/RegisterForm.js +62 -19
- 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/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/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/schemas/bundle-form.d.ts +142 -0
- package/dist/features/categories/schemas/bundle-form.js +149 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +5 -4
- package/dist/features/classified/components/ClassifiedDetailPageView.js +37 -16
- 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/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/homepage/components/MarketplaceHomepageView.js +24 -5
- package/dist/features/homepage/hooks/useBlogArticles.js +1 -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/lottery/schemas/config-write.d.ts +9 -5
- package/dist/features/lottery/schemas/config-write.js +69 -9
- 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/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/ProductDetailActions.js +0 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +7 -3
- package/dist/features/products/components/ProductDetailPageView.js +8 -5
- 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/products.repository.d.ts +5 -1
- package/dist/features/products/repository/products.repository.js +23 -1
- 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 +8 -7
- 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 -24
- 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/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/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/SellerDigitalCodesView.d.ts +14 -7
- package/dist/features/seller/components/SellerDigitalCodesView.js +4 -126
- 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/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/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/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 +275 -29
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +48 -12
- package/dist/http/api-handler.js +2 -1
- package/dist/index.d.ts +24 -103
- package/dist/index.js +33 -141
- 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 +6 -3
- package/dist/next/index.d.ts +7 -2
- package/dist/next/index.js +36 -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/db-firebase/admin.js +15 -1
- package/dist/providers/db-firebase/sieve.d.ts +22 -0
- 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/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/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
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: "It should be searchable — if I search for the maintenance toggle it
|
|
3
|
+
* should find it." A nav-only index cannot: the maintenance toggle is
|
|
4
|
+
* not a page, it is a control on tab ⓵ of an 1800-line settings screen,
|
|
5
|
+
* and the only thing naming it is a `<Toggle label="Maintenance mode">`
|
|
6
|
+
* inside that screen's JSX.
|
|
7
|
+
* WHAT: Track B — the declarative map of settings and toggles, each with the
|
|
8
|
+
* `?tab=`/`#anchor` that reaches the control itself.
|
|
9
|
+
*
|
|
10
|
+
* ## Why this is declared rather than derived
|
|
11
|
+
*
|
|
12
|
+
* Track A (`FieldUiMeta.index`) covers every schema-driven form for free. It
|
|
13
|
+
* yields **nothing** here, because `AdminSiteSettingsView` is not schema-driven
|
|
14
|
+
* — it is ~160 controls bound to individual `useState` calls, and it is on
|
|
15
|
+
* W8's tabbing list precisely because of that. Waiting for the derivation
|
|
16
|
+
* would mean the headline request — find the maintenance toggle — ships last.
|
|
17
|
+
*
|
|
18
|
+
* So this is a hand-written map, and it is the ONE in this plan that is
|
|
19
|
+
* defensible: there is no existing array it duplicates. `audit-nav-metadata`'s
|
|
20
|
+
* sibling check keeps its `tab` values inside `SiteSettingsTabId`, so the half
|
|
21
|
+
* that CAN drift is the half that is guarded.
|
|
22
|
+
*
|
|
23
|
+
* ## An anchor is CLAIMED, not assumed
|
|
24
|
+
*
|
|
25
|
+
* A deep link to `#setting-maintenance-mode` that matches no `id=` in the DOM
|
|
26
|
+
* scrolls nowhere and reads as the search having missed — so an entry opts in
|
|
27
|
+
* with `anchored: true`, and `audit-action-index` checks that the id exists.
|
|
28
|
+
* Everything else deep-links to its TAB, which is a smaller promise ("this
|
|
29
|
+
* lives on the Fees tab") and one the page can keep.
|
|
30
|
+
*
|
|
31
|
+
* The first run of that audit reported thirteen dead anchors, because this
|
|
32
|
+
* file declared twenty-two entries while the view carried nine ids. That is
|
|
33
|
+
* the rule doing its job on the commit that introduced it.
|
|
34
|
+
*
|
|
35
|
+
* EXPORTS: SITE_SETTINGS_ENTRIES, deriveSettingsEntries
|
|
36
|
+
*
|
|
37
|
+
* @tag domain:search
|
|
38
|
+
* @tag layer:shared
|
|
39
|
+
* @tag pattern:registry
|
|
40
|
+
* @tag access:isomorphic
|
|
41
|
+
* @tag consumers:action index
|
|
42
|
+
* @tag sideEffects:none
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* The controls worth finding.
|
|
46
|
+
*
|
|
47
|
+
* Deliberately not all ~160: a search result list is only useful while it is
|
|
48
|
+
* short, and "Hero title" is not something anyone hunts for — they open the
|
|
49
|
+
* About tab and see it. What is here is what someone arrives ALREADY looking
|
|
50
|
+
* for, usually in a hurry, and usually not knowing which tab it is filed under.
|
|
51
|
+
*/
|
|
52
|
+
export const SITE_SETTINGS_ENTRIES = [
|
|
53
|
+
{
|
|
54
|
+
slug: "maintenance-mode",
|
|
55
|
+
anchored: true,
|
|
56
|
+
label: "Maintenance mode",
|
|
57
|
+
description: "Take the site offline for visitors and show a message instead.",
|
|
58
|
+
keywords: ["offline", "down for maintenance", "close the site", "under construction"],
|
|
59
|
+
tab: "branding",
|
|
60
|
+
kind: "toggle",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
slug: "maintenance-message",
|
|
64
|
+
label: "Maintenance message",
|
|
65
|
+
description: "What visitors see while the site is in maintenance mode.",
|
|
66
|
+
keywords: ["offline message", "downtime notice"],
|
|
67
|
+
tab: "branding",
|
|
68
|
+
kind: "setting",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
slug: "announcement-bar",
|
|
72
|
+
anchored: true,
|
|
73
|
+
label: "Announcement bar",
|
|
74
|
+
description: "Show a message across the top of every page.",
|
|
75
|
+
keywords: ["banner", "notice", "top bar", "sale banner"],
|
|
76
|
+
tab: "announcement",
|
|
77
|
+
kind: "toggle",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
slug: "robots-noindex",
|
|
81
|
+
anchored: true,
|
|
82
|
+
label: "Hide from search engines",
|
|
83
|
+
description: "Stop Google indexing the site. Use carefully — recovery is slow.",
|
|
84
|
+
keywords: ["noindex", "google", "seo", "deindex", "hide from google"],
|
|
85
|
+
tab: "seo",
|
|
86
|
+
kind: "toggle",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
slug: "razorpay-enabled",
|
|
90
|
+
anchored: true,
|
|
91
|
+
label: "Online payments (Razorpay)",
|
|
92
|
+
description: "Accept cards and UPI through Razorpay. Off by default; manual payment is the default.",
|
|
93
|
+
keywords: ["card payments", "razorpay", "online payment", "gateway", "upi"],
|
|
94
|
+
tab: "fees",
|
|
95
|
+
kind: "toggle",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
slug: "manual-payment-enabled",
|
|
99
|
+
anchored: true,
|
|
100
|
+
label: "Manual UPI / bank transfer",
|
|
101
|
+
description: "Let buyers pay by transfer and upload proof for review.",
|
|
102
|
+
keywords: ["upi", "bank transfer", "manual payment", "proof"],
|
|
103
|
+
tab: "fees",
|
|
104
|
+
kind: "toggle",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
slug: "cod-enabled",
|
|
108
|
+
anchored: true,
|
|
109
|
+
label: "Cash on delivery",
|
|
110
|
+
description: "Let buyers pay when the parcel arrives.",
|
|
111
|
+
keywords: ["cod", "cash on delivery", "pay on delivery"],
|
|
112
|
+
tab: "fees",
|
|
113
|
+
kind: "toggle",
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
slug: "platform-commission",
|
|
117
|
+
label: "Platform commission",
|
|
118
|
+
description: "What the platform takes from each sale, and its cap.",
|
|
119
|
+
keywords: ["commission", "our cut", "platform fee", "take rate"],
|
|
120
|
+
tab: "fees",
|
|
121
|
+
kind: "setting",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
slug: "google-analytics",
|
|
125
|
+
label: "Google Analytics",
|
|
126
|
+
description: "The GA4 measurement id used for site traffic.",
|
|
127
|
+
keywords: ["ga4", "analytics id", "tracking", "measurement id", "google"],
|
|
128
|
+
tab: "integrations",
|
|
129
|
+
kind: "setting",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
slug: "facebook-pixel",
|
|
133
|
+
label: "Facebook Pixel",
|
|
134
|
+
description: "The Meta Pixel id used for ad attribution.",
|
|
135
|
+
keywords: ["meta pixel", "facebook", "ads tracking", "conversion"],
|
|
136
|
+
tab: "integrations",
|
|
137
|
+
kind: "setting",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
slug: "free-shipping-threshold",
|
|
141
|
+
label: "Free shipping threshold",
|
|
142
|
+
description: "The order value above which delivery is free.",
|
|
143
|
+
keywords: ["free delivery", "shipping over", "minimum for free shipping"],
|
|
144
|
+
tab: "shipping",
|
|
145
|
+
kind: "setting",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
slug: "bid-increment",
|
|
149
|
+
label: "Minimum bid increment",
|
|
150
|
+
description: "The smallest amount a new bid must beat the current one by.",
|
|
151
|
+
keywords: ["bidding step", "increment", "auction minimum"],
|
|
152
|
+
tab: "auction",
|
|
153
|
+
kind: "setting",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
slug: "anti-sniping",
|
|
157
|
+
label: "Anti-sniping extension",
|
|
158
|
+
description: "Extend an auction when a bid lands in its final seconds.",
|
|
159
|
+
keywords: ["sniping", "last second bid", "extend auction", "going going gone"],
|
|
160
|
+
tab: "auction",
|
|
161
|
+
kind: "setting",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
slug: "email-notifications",
|
|
165
|
+
anchored: true,
|
|
166
|
+
label: "Email notifications",
|
|
167
|
+
description: "Whether the platform sends email at all.",
|
|
168
|
+
keywords: ["email", "resend", "transactional email", "stop emails"],
|
|
169
|
+
tab: "notifications",
|
|
170
|
+
kind: "toggle",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
slug: "whatsapp-notifications",
|
|
174
|
+
anchored: true,
|
|
175
|
+
label: "WhatsApp notifications",
|
|
176
|
+
description: "Send order updates over WhatsApp.",
|
|
177
|
+
keywords: ["whatsapp", "order updates", "meta"],
|
|
178
|
+
tab: "notifications",
|
|
179
|
+
kind: "toggle",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
slug: "daily-digest",
|
|
183
|
+
anchored: true,
|
|
184
|
+
label: "Daily status digest",
|
|
185
|
+
description: "A once-a-day operations summary to the admin inbox.",
|
|
186
|
+
keywords: ["digest", "daily email", "summary", "ops report"],
|
|
187
|
+
tab: "notifications",
|
|
188
|
+
kind: "toggle",
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
slug: "emi-threshold",
|
|
192
|
+
label: "EMI eligibility threshold",
|
|
193
|
+
description: "The order value above which instalments are offered.",
|
|
194
|
+
keywords: ["instalments", "installments", "emi minimum", "pay monthly"],
|
|
195
|
+
tab: "emi",
|
|
196
|
+
kind: "setting",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
slug: "gst-rates",
|
|
200
|
+
label: "GST rates and HSN defaults",
|
|
201
|
+
description: "The tax rates applied to listings, and the fallback HSN code.",
|
|
202
|
+
keywords: ["tax", "gst", "hsn", "vat"],
|
|
203
|
+
tab: "gst",
|
|
204
|
+
kind: "setting",
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
slug: "gstin",
|
|
208
|
+
label: "GSTIN and registered business",
|
|
209
|
+
description: "The registration number and legal address printed on invoices.",
|
|
210
|
+
keywords: ["gstin", "tax number", "registered address", "legal name"],
|
|
211
|
+
tab: "legal",
|
|
212
|
+
kind: "setting",
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
slug: "enabled-listing-types",
|
|
216
|
+
label: "Enabled listing types",
|
|
217
|
+
description: "Which kinds of listing sellers may create.",
|
|
218
|
+
keywords: ["auctions off", "disable pre-orders", "listing types", "turn off"],
|
|
219
|
+
tab: "listings",
|
|
220
|
+
kind: "toggle",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
slug: "watermark",
|
|
224
|
+
label: "Photo watermark",
|
|
225
|
+
description: "The mark stamped onto uploaded product photos.",
|
|
226
|
+
keywords: ["watermark", "logo on photos", "protect images"],
|
|
227
|
+
tab: "watermark",
|
|
228
|
+
kind: "setting",
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
slug: "upload-limits",
|
|
232
|
+
label: "Upload and listing limits",
|
|
233
|
+
description: "Maximum file size, image count and per-account listing caps.",
|
|
234
|
+
keywords: ["max upload", "file size", "how many listings", "quota", "limit"],
|
|
235
|
+
tab: "limits",
|
|
236
|
+
kind: "setting",
|
|
237
|
+
},
|
|
238
|
+
];
|
|
239
|
+
/**
|
|
240
|
+
* The settings entries, as index entries.
|
|
241
|
+
*
|
|
242
|
+
* `deepLink` carries the whole query-and-hash — `?tab=fees#setting-cod-enabled`
|
|
243
|
+
* — rather than just the tab, because landing on the right tab and leaving the
|
|
244
|
+
* reader to find the control among forty others is most of the way to not
|
|
245
|
+
* having searched at all.
|
|
246
|
+
*/
|
|
247
|
+
export function deriveSettingsEntries(settingsHref, requiredPermission = "settings:read") {
|
|
248
|
+
return SITE_SETTINGS_ENTRIES.map((entry) => ({
|
|
249
|
+
id: `admin:${entry.kind}:${entry.slug}`,
|
|
250
|
+
kind: entry.kind,
|
|
251
|
+
portal: "admin",
|
|
252
|
+
label: entry.label,
|
|
253
|
+
description: entry.description,
|
|
254
|
+
keywords: entry.keywords,
|
|
255
|
+
href: settingsHref,
|
|
256
|
+
deepLink: entry.anchored
|
|
257
|
+
? `?tab=${entry.tab}#setting-${entry.slug}`
|
|
258
|
+
: `?tab=${entry.tab}`,
|
|
259
|
+
sectionPath: "Admin › System › Site Settings",
|
|
260
|
+
requiredPermission,
|
|
261
|
+
/*
|
|
262
|
+
* Above nav entries. Someone searching "maintenance" wants the toggle, not
|
|
263
|
+
* the settings page it lives on — and the page will also match, on its own
|
|
264
|
+
* description, so without this the container outranks the thing.
|
|
265
|
+
*/
|
|
266
|
+
weight: 20,
|
|
267
|
+
}));
|
|
268
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { NavPortal } from "../../../_internal/shared/features/layout/types";
|
|
2
|
+
/**
|
|
3
|
+
* What kind of thing an entry is.
|
|
4
|
+
*
|
|
5
|
+
* The distinction that matters is `nav`/`page` (somewhere you go) versus
|
|
6
|
+
* `setting`/`toggle`/`action` (something you change or do) — a search for
|
|
7
|
+
* "maintenance mode" wants the toggle, not the settings page it lives on, and
|
|
8
|
+
* only the kind tells them apart.
|
|
9
|
+
*/
|
|
10
|
+
export type ActionIndexKind = "nav" | "page" | "section" | "setting" | "toggle" | "action";
|
|
11
|
+
export interface ActionIndexEntry {
|
|
12
|
+
/** `{portal}:{kind}:{slug}` — also the overlay's React key. */
|
|
13
|
+
id: string;
|
|
14
|
+
kind: ActionIndexKind;
|
|
15
|
+
/** `public` for anything outside the three dashboards. */
|
|
16
|
+
portal: NavPortal | "public";
|
|
17
|
+
label: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
keywords?: string[];
|
|
20
|
+
href: string;
|
|
21
|
+
/**
|
|
22
|
+
* The `?tab=`/`#anchor` that reaches the exact control, when the href alone
|
|
23
|
+
* only reaches the page it lives on. This is what makes a search for
|
|
24
|
+
* "maintenance" land on the toggle rather than on Site Settings.
|
|
25
|
+
*/
|
|
26
|
+
deepLink?: string;
|
|
27
|
+
/** Breadcrumb for the result row — "Admin › System › Site Settings". */
|
|
28
|
+
sectionPath?: string;
|
|
29
|
+
requiredRole?: string;
|
|
30
|
+
requiredPermission?: string;
|
|
31
|
+
requiresTester?: boolean;
|
|
32
|
+
/** A lucide icon NAME. See the header for why this is not a component. */
|
|
33
|
+
iconName?: string;
|
|
34
|
+
/** Higher sorts first among equal-scoring matches. Default 0. */
|
|
35
|
+
weight?: number;
|
|
36
|
+
}
|
|
37
|
+
/** What an admin may change about a built-in entry. */
|
|
38
|
+
export interface ActionIndexOverride {
|
|
39
|
+
enabled?: boolean;
|
|
40
|
+
label?: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
keywords?: string[];
|
|
43
|
+
weight?: number;
|
|
44
|
+
group?: string;
|
|
45
|
+
order?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The stored control document, `actionIndex/global`.
|
|
49
|
+
*
|
|
50
|
+
* Deliberately NOT folded into `siteSettings`: that document is read publicly
|
|
51
|
+
* and edge-cached, and this one is projected by role.
|
|
52
|
+
*/
|
|
53
|
+
export interface ActionIndexControl {
|
|
54
|
+
entries: Record<string, ActionIndexOverride>;
|
|
55
|
+
/** Admin-authored entries, appended after the static base. */
|
|
56
|
+
custom: ActionIndexEntry[];
|
|
57
|
+
}
|
|
58
|
+
export declare const ACTION_INDEX_COLLECTION: "actionIndex";
|
|
59
|
+
export declare const ACTION_INDEX_DOC_ID: "global";
|
|
60
|
+
/**
|
|
61
|
+
* Apply an admin's overrides to the static base.
|
|
62
|
+
*
|
|
63
|
+
* Order is load-bearing: override, then DROP disabled, then append `custom`.
|
|
64
|
+
* Dropping last would let an admin disable an entry and have their own
|
|
65
|
+
* replacement for it disappear too, since a custom entry may legitimately
|
|
66
|
+
* reuse a built-in id to supersede it.
|
|
67
|
+
*/
|
|
68
|
+
export declare function mergeActionIndex(base: readonly ActionIndexEntry[], control: ActionIndexControl | undefined): ActionIndexEntry[];
|
|
69
|
+
/**
|
|
70
|
+
* Drop every entry the viewer may not reach.
|
|
71
|
+
*
|
|
72
|
+
* 🛑 Filtered SERVER-side, never in the browser. An entry the client is not
|
|
73
|
+
* allowed to act on is an entry it must not receive: the label alone is a site
|
|
74
|
+
* map of the admin panel, which is D8's whole point.
|
|
75
|
+
*/
|
|
76
|
+
export declare function projectActionIndexForViewer(entries: readonly ActionIndexEntry[], viewer: {
|
|
77
|
+
role?: string;
|
|
78
|
+
permissions?: readonly string[];
|
|
79
|
+
isTester?: boolean;
|
|
80
|
+
isAdmin?: boolean;
|
|
81
|
+
}): ActionIndexEntry[];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: "Searching 'maintenance' should land me on the maintenance toggle" —
|
|
3
|
+
* and `Search.tsx` has had a **complete** quick-links path the whole
|
|
4
|
+
* time. Both modes are built: the inline filter already matches
|
|
5
|
+
* `link.keywords` as well as the label, the overlay renders a labelled
|
|
6
|
+
* band, the keyboard index arithmetic accounts for it, and
|
|
7
|
+
* `constants/search.ts` even holds the band's label string.
|
|
8
|
+
*
|
|
9
|
+
* `grep "quickLinks="` returns **zero**. The one real mount passes nine
|
|
10
|
+
* props and not that one. A finished feature with no input.
|
|
11
|
+
*
|
|
12
|
+
* The reason nothing passed it is that there was nothing to pass: no
|
|
13
|
+
* single list of what a user can DO existed. Nav lived in three arrays,
|
|
14
|
+
* quick actions in a registry each page ignored, and settings nowhere at
|
|
15
|
+
* all.
|
|
16
|
+
* WHAT: `ActionIndexEntry` — one shape for every navigable or actionable
|
|
17
|
+
* surface, and the type the search, the command palette and (D7) the
|
|
18
|
+
* admin control plane all read.
|
|
19
|
+
*
|
|
20
|
+
* ## Why `iconName` is a string
|
|
21
|
+
*
|
|
22
|
+
* The index is serialised — into a Firestore control document, through an API
|
|
23
|
+
* route, into React Query's cache. A `LucideIcon` component reference survives
|
|
24
|
+
* none of those. The renderer resolves the name; the index carries data.
|
|
25
|
+
*
|
|
26
|
+
* ## Why `href` and every permission field are CODE-owned
|
|
27
|
+
*
|
|
28
|
+
* D7 lets an admin rename, re-describe, retag, reorder and hide an entry —
|
|
29
|
+
* that is content. It does not let them repoint a built-in entry's route or
|
|
30
|
+
* relax its permission, because those are routing and access control. An
|
|
31
|
+
* admin-authored entry supplies its own `href`, and that one is validated at
|
|
32
|
+
* SAVE time (see the write route): an audit that runs on CI would let a bad
|
|
33
|
+
* href reach the sidebar and 404 until the next run.
|
|
34
|
+
*
|
|
35
|
+
* EXPORTS: ActionIndexEntry, ActionIndexKind, ActionIndexControl,
|
|
36
|
+
* type ActionIndexOverride
|
|
37
|
+
*
|
|
38
|
+
* @tag domain:search
|
|
39
|
+
* @tag layer:shared
|
|
40
|
+
* @tag pattern:registry
|
|
41
|
+
* @tag access:isomorphic
|
|
42
|
+
* @tag consumers:Search,CommandPalette,action-index admin screen
|
|
43
|
+
* @tag sideEffects:none
|
|
44
|
+
*/
|
|
45
|
+
export const ACTION_INDEX_COLLECTION = "actionIndex";
|
|
46
|
+
export const ACTION_INDEX_DOC_ID = "global";
|
|
47
|
+
/**
|
|
48
|
+
* Apply an admin's overrides to the static base.
|
|
49
|
+
*
|
|
50
|
+
* Order is load-bearing: override, then DROP disabled, then append `custom`.
|
|
51
|
+
* Dropping last would let an admin disable an entry and have their own
|
|
52
|
+
* replacement for it disappear too, since a custom entry may legitimately
|
|
53
|
+
* reuse a built-in id to supersede it.
|
|
54
|
+
*/
|
|
55
|
+
export function mergeActionIndex(base, control) {
|
|
56
|
+
if (!control)
|
|
57
|
+
return [...base];
|
|
58
|
+
const merged = [];
|
|
59
|
+
for (const entry of base) {
|
|
60
|
+
const override = control.entries?.[entry.id];
|
|
61
|
+
if (override?.enabled === false)
|
|
62
|
+
continue;
|
|
63
|
+
merged.push(override
|
|
64
|
+
? {
|
|
65
|
+
...entry,
|
|
66
|
+
...(override.label !== undefined ? { label: override.label } : {}),
|
|
67
|
+
...(override.description !== undefined
|
|
68
|
+
? { description: override.description }
|
|
69
|
+
: {}),
|
|
70
|
+
...(override.keywords !== undefined ? { keywords: override.keywords } : {}),
|
|
71
|
+
...(override.weight !== undefined ? { weight: override.weight } : {}),
|
|
72
|
+
}
|
|
73
|
+
: entry);
|
|
74
|
+
}
|
|
75
|
+
return merged.concat(control.custom ?? []);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Drop every entry the viewer may not reach.
|
|
79
|
+
*
|
|
80
|
+
* 🛑 Filtered SERVER-side, never in the browser. An entry the client is not
|
|
81
|
+
* allowed to act on is an entry it must not receive: the label alone is a site
|
|
82
|
+
* map of the admin panel, which is D8's whole point.
|
|
83
|
+
*/
|
|
84
|
+
export function projectActionIndexForViewer(entries, viewer) {
|
|
85
|
+
return entries.filter((entry) => {
|
|
86
|
+
if (entry.requiresTester && !viewer.isTester && !viewer.isAdmin)
|
|
87
|
+
return false;
|
|
88
|
+
if (entry.requiredRole && entry.requiredRole !== viewer.role && !viewer.isAdmin) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
/*
|
|
92
|
+
* An admin bypasses permission checks by design — `isAdminUser` short-
|
|
93
|
+
* circuits every one of them elsewhere, and an index that disagreed would
|
|
94
|
+
* hide screens the admin can plainly open from the sidebar.
|
|
95
|
+
*/
|
|
96
|
+
if (entry.requiredPermission && !viewer.isAdmin) {
|
|
97
|
+
if (!viewer.permissions?.includes(entry.requiredPermission))
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ActionIndexEntry } from "./types";
|
|
2
|
+
export interface ActionIndexValidationIssue {
|
|
3
|
+
field: "href" | "deepLink" | "id";
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ActionIndexValidationContext {
|
|
7
|
+
/**
|
|
8
|
+
* Every route the app actually serves, as paths without a locale prefix.
|
|
9
|
+
* Built from the route map by the caller — a runtime cannot walk `src/app`.
|
|
10
|
+
*/
|
|
11
|
+
knownRoutes: ReadonlySet<string>;
|
|
12
|
+
/** Anchors a page is known to render, keyed by route. */
|
|
13
|
+
knownAnchors?: Readonly<Record<string, ReadonlySet<string>>>;
|
|
14
|
+
/** Tab ids a route accepts, keyed by route. Absent = `?tab=` not checked. */
|
|
15
|
+
knownTabs?: Readonly<Record<string, ReadonlySet<string>>>;
|
|
16
|
+
/** Ids already in use — a duplicate silently shadows an existing entry. */
|
|
17
|
+
existingIds?: ReadonlySet<string>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Normalise an href to the form `knownRoutes` is keyed by: no origin, no
|
|
21
|
+
* locale segment, no query, no hash, no trailing slash.
|
|
22
|
+
*
|
|
23
|
+
* The locale strip matters — an admin pasting the URL out of their address bar
|
|
24
|
+
* gets `/en/admin/orders`, and rejecting that would be technically correct and
|
|
25
|
+
* useless.
|
|
26
|
+
*/
|
|
27
|
+
export declare function normaliseRoute(href: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* The three checks, at save time.
|
|
30
|
+
*
|
|
31
|
+
* Returns every issue rather than the first, so an admin fixing a bad entry
|
|
32
|
+
* does not discover its second problem only after fixing the first.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateActionIndexEntry(entry: Pick<ActionIndexEntry, "id" | "href" | "deepLink">, ctx: ActionIndexValidationContext): ActionIndexValidationIssue[];
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: `audit-action-index` runs on CI. An admin-authored entry (D7) is written
|
|
3
|
+
* at RUNTIME — so a bad href reaches the sidebar and the search, 404s for
|
|
4
|
+
* everyone, and stays there until the next audit run notices. The master
|
|
5
|
+
* plan's own words: without save-time validation, "admins can create
|
|
6
|
+
* entries" is a self-service 404 generator.
|
|
7
|
+
* WHAT: The same three checks the audit performs, as a function the write route
|
|
8
|
+
* calls before persisting, returning a 400 instead of a broken link.
|
|
9
|
+
*
|
|
10
|
+
* ## Why the route cannot just run the audit
|
|
11
|
+
*
|
|
12
|
+
* The audit reads FILES — it greps `settings-entries.ts` and the settings view
|
|
13
|
+
* for `id=` literals. A route has neither at runtime. So the checkable facts
|
|
14
|
+
* have to be passed in: the set of real routes, and the set of known anchors.
|
|
15
|
+
* That is the whole reason this is a function taking a context rather than a
|
|
16
|
+
* copy of the audit.
|
|
17
|
+
*
|
|
18
|
+
* ## What it deliberately does NOT check
|
|
19
|
+
*
|
|
20
|
+
* Whether the target page renders anything useful. A route that exists and an
|
|
21
|
+
* anchor that exists are provable from data the server has; "is this a sensible
|
|
22
|
+
* place to send someone" is not, and a validator that pretends otherwise would
|
|
23
|
+
* reject legitimate entries and teach admins to work around it.
|
|
24
|
+
*
|
|
25
|
+
* EXPORTS: validateActionIndexEntry, type ActionIndexValidationContext,
|
|
26
|
+
* type ActionIndexValidationIssue
|
|
27
|
+
*
|
|
28
|
+
* @tag domain:search
|
|
29
|
+
* @tag layer:shared
|
|
30
|
+
* @tag pattern:none
|
|
31
|
+
* @tag access:isomorphic
|
|
32
|
+
* @tag consumers:/api/admin/action-index,audit-action-index
|
|
33
|
+
* @tag sideEffects:none
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Normalise an href to the form `knownRoutes` is keyed by: no origin, no
|
|
37
|
+
* locale segment, no query, no hash, no trailing slash.
|
|
38
|
+
*
|
|
39
|
+
* The locale strip matters — an admin pasting the URL out of their address bar
|
|
40
|
+
* gets `/en/admin/orders`, and rejecting that would be technically correct and
|
|
41
|
+
* useless.
|
|
42
|
+
*/
|
|
43
|
+
export function normaliseRoute(href) {
|
|
44
|
+
return (href
|
|
45
|
+
.replace(/^https?:\/\/[^/]+/, "")
|
|
46
|
+
.replace(/^\/[a-z]{2}(-[A-Z]{2})?(?=\/)/, "")
|
|
47
|
+
.replace(/[?#].*$/, "")
|
|
48
|
+
.replace(/\/+$/, "") || "/");
|
|
49
|
+
}
|
|
50
|
+
/** A dynamic route matches by shape: `/admin/orders/[id]` accepts any id. */
|
|
51
|
+
function matchesKnownRoute(route, knownRoutes) {
|
|
52
|
+
if (knownRoutes.has(route))
|
|
53
|
+
return true;
|
|
54
|
+
const parts = route.split("/");
|
|
55
|
+
for (const known of knownRoutes) {
|
|
56
|
+
const knownParts = known.split("/");
|
|
57
|
+
if (knownParts.length !== parts.length)
|
|
58
|
+
continue;
|
|
59
|
+
const ok = knownParts.every((segment, i) => segment === parts[i] || /^\[.+\]$/.test(segment));
|
|
60
|
+
if (ok)
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The three checks, at save time.
|
|
67
|
+
*
|
|
68
|
+
* Returns every issue rather than the first, so an admin fixing a bad entry
|
|
69
|
+
* does not discover its second problem only after fixing the first.
|
|
70
|
+
*/
|
|
71
|
+
export function validateActionIndexEntry(entry, ctx) {
|
|
72
|
+
const issues = [];
|
|
73
|
+
if (ctx.existingIds?.has(entry.id)) {
|
|
74
|
+
issues.push({
|
|
75
|
+
field: "id",
|
|
76
|
+
message: `"${entry.id}" is already used. A duplicate id silently shadows the existing entry, and an override written for one would land on the other.`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const route = normaliseRoute(entry.href);
|
|
80
|
+
if (!entry.href.trim()) {
|
|
81
|
+
issues.push({ field: "href", message: "Enter the page this should open." });
|
|
82
|
+
}
|
|
83
|
+
else if (!route.startsWith("/")) {
|
|
84
|
+
issues.push({
|
|
85
|
+
field: "href",
|
|
86
|
+
message: "Start the link with / — an index entry points at a page on this site.",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else if (!matchesKnownRoute(route, ctx.knownRoutes)) {
|
|
90
|
+
issues.push({
|
|
91
|
+
field: "href",
|
|
92
|
+
message: `No page is served at ${route}. Check the path — a link that 404s is worse than no entry at all.`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (entry.deepLink) {
|
|
96
|
+
const tab = entry.deepLink.match(/[?&]tab=([^&#]+)/)?.[1];
|
|
97
|
+
const anchor = entry.deepLink.match(/#(.+)$/)?.[1];
|
|
98
|
+
const tabsForRoute = ctx.knownTabs?.[route];
|
|
99
|
+
if (tab && tabsForRoute && !tabsForRoute.has(tab)) {
|
|
100
|
+
issues.push({
|
|
101
|
+
field: "deepLink",
|
|
102
|
+
message: `${route} has no "${tab}" tab. The page would open on its default tab, which reads as the search having missed rather than as a broken link.`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const anchorsForRoute = ctx.knownAnchors?.[route];
|
|
106
|
+
if (anchor && anchorsForRoute && !anchorsForRoute.has(anchor)) {
|
|
107
|
+
issues.push({
|
|
108
|
+
field: "deepLink",
|
|
109
|
+
message: `${route} renders nothing with id="${anchor}". The link would scroll nowhere.`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return issues;
|
|
114
|
+
}
|
|
@@ -8,8 +8,12 @@ export interface NavSuggestionRecord {
|
|
|
8
8
|
/**
|
|
9
9
|
* W1-19 — wired to `/api/search/suggestions` 2026-05-23. Fetches up to 20
|
|
10
10
|
* matches (5 per resource type) and surfaces them as typeahead rows.
|
|
11
|
+
*
|
|
12
|
+
* `minChars` is trailing and defaulted because this hook is part of appkit's
|
|
13
|
+
* public API (`appkit/src/index.ts`); existing 2-argument callers keep working
|
|
14
|
+
* and inherit the gate.
|
|
11
15
|
*/
|
|
12
|
-
export declare function useNavSuggestions(query: string, selectedType?: string, debounceMs?: number): {
|
|
16
|
+
export declare function useNavSuggestions(query: string, selectedType?: string, debounceMs?: number, minChars?: number): {
|
|
13
17
|
suggestions: NavSuggestionRecord[];
|
|
14
18
|
isLoading: boolean;
|
|
15
19
|
};
|
|
@@ -20,11 +20,28 @@ const SUGGESTION_TYPE_PARAM_MAP = {
|
|
|
20
20
|
blog: "blog",
|
|
21
21
|
events: "event",
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* The shortest query that reaches the network.
|
|
25
|
+
*
|
|
26
|
+
* 🛑 Entity suggestions cost a real Firestore round trip — the route reads
|
|
27
|
+
* products, categories, blog posts and events. Below this length the result is
|
|
28
|
+
* dominated by whatever happens to sort first, so the request buys noise at the
|
|
29
|
+
* price of two round trips per typed word, on every page, for every visitor.
|
|
30
|
+
*
|
|
31
|
+
* This gate is for the ENTITY band only. Action/nav suggestions match an
|
|
32
|
+
* in-memory index and should stay available from the first character — typing
|
|
33
|
+
* "s" and seeing "Settings" is genuinely useful and costs nothing.
|
|
34
|
+
*/
|
|
35
|
+
const DEFAULT_MIN_CHARS = 3;
|
|
23
36
|
/**
|
|
24
37
|
* W1-19 — wired to `/api/search/suggestions` 2026-05-23. Fetches up to 20
|
|
25
38
|
* matches (5 per resource type) and surfaces them as typeahead rows.
|
|
39
|
+
*
|
|
40
|
+
* `minChars` is trailing and defaulted because this hook is part of appkit's
|
|
41
|
+
* public API (`appkit/src/index.ts`); existing 2-argument callers keep working
|
|
42
|
+
* and inherit the gate.
|
|
26
43
|
*/
|
|
27
|
-
export function useNavSuggestions(query, selectedType, debounceMs = 250) {
|
|
44
|
+
export function useNavSuggestions(query, selectedType, debounceMs = 250, minChars = DEFAULT_MIN_CHARS) {
|
|
28
45
|
const [suggestions, setSuggestions] = useState([]);
|
|
29
46
|
const [isLoading, setIsLoading] = useState(false);
|
|
30
47
|
const timerRef = useRef(null);
|
|
@@ -35,7 +52,10 @@ export function useNavSuggestions(query, selectedType, debounceMs = 250) {
|
|
|
35
52
|
if (abortRef.current)
|
|
36
53
|
abortRef.current.abort();
|
|
37
54
|
const trimmed = query.trim();
|
|
38
|
-
|
|
55
|
+
// Below the gate is a deliberate no-op, not a failure: clear the rows and
|
|
56
|
+
// report not-loading, so the dropdown shows its action band rather than a
|
|
57
|
+
// spinner that never resolves.
|
|
58
|
+
if (trimmed.length < minChars) {
|
|
39
59
|
setSuggestions([]);
|
|
40
60
|
setIsLoading(false);
|
|
41
61
|
return;
|
|
@@ -76,6 +96,6 @@ export function useNavSuggestions(query, selectedType, debounceMs = 250) {
|
|
|
76
96
|
if (abortRef.current)
|
|
77
97
|
abortRef.current.abort();
|
|
78
98
|
};
|
|
79
|
-
}, [query, selectedType, debounceMs]);
|
|
99
|
+
}, [query, selectedType, debounceMs, minChars]);
|
|
80
100
|
return { suggestions, isLoading };
|
|
81
101
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { SIEVE_OP, sieveFilter } from "../../../utils/sieve-builder";
|
|
2
|
+
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
1
3
|
/**
|
|
2
4
|
* SearchRepository
|
|
3
5
|
*
|
|
@@ -25,7 +27,7 @@ export class SearchRepository {
|
|
|
25
27
|
filterParts.push(`condition==${query.condition}`);
|
|
26
28
|
// SB1-G Phase 4 — canonical listingType discriminator.
|
|
27
29
|
if (query.listingType) {
|
|
28
|
-
filterParts.push(
|
|
30
|
+
filterParts.push(sieveFilter(PRODUCT_FIELDS.LISTING_TYPE, SIEVE_OP.EQ, query.listingType));
|
|
29
31
|
}
|
|
30
32
|
if (query.inStock === true)
|
|
31
33
|
filterParts.push("availableQuantity>0");
|