@mohasinac/appkit 4.30.19 → 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/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 +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/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
|
@@ -281,7 +281,7 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
|
|
|
281
281
|
canFilter: boolean;
|
|
282
282
|
canSort: boolean;
|
|
283
283
|
};
|
|
284
|
-
"classified.meetupArea": {
|
|
284
|
+
"classified.meetupArea.city": {
|
|
285
285
|
canFilter: boolean;
|
|
286
286
|
canSort: boolean;
|
|
287
287
|
};
|
|
@@ -317,6 +317,10 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
|
|
|
317
317
|
canFilter: boolean;
|
|
318
318
|
canSort: boolean;
|
|
319
319
|
};
|
|
320
|
+
"liveItem.transport.method": {
|
|
321
|
+
canFilter: boolean;
|
|
322
|
+
canSort: boolean;
|
|
323
|
+
};
|
|
320
324
|
};
|
|
321
325
|
/**
|
|
322
326
|
* Virtual filter aliases — callers can pass `f=listingType==auction` for
|
|
@@ -594,7 +594,22 @@ ProductRepository.SIEVE_FIELDS = {
|
|
|
594
594
|
// silently skipped. The toggle did nothing at all until 2026-08-21.
|
|
595
595
|
shippingPaidBy: { canFilter: true, canSort: false },
|
|
596
596
|
searchTxt: { canFilter: true, canSort: false },
|
|
597
|
-
|
|
597
|
+
/*
|
|
598
|
+
* `classified.meetupArea.city`, NOT `classified.meetupArea`.
|
|
599
|
+
*
|
|
600
|
+
* `meetupArea` is an OBJECT (`{city, locality, pincode}`), so an equality
|
|
601
|
+
* on the parent could never match anything a user typed. The city facet
|
|
602
|
+
* emits `classified.meetupArea.city` (TYPE_FACET_FIELD), `findField` is an
|
|
603
|
+
* exact-key lookup, and `throwExceptions:false` drops what it cannot
|
|
604
|
+
* resolve — so the /classified city filter rendered, counted toward the
|
|
605
|
+
* badge, and changed nothing. Both halves of Root Cause #62 in one entry:
|
|
606
|
+
* an orphan key that matches no field, beside a real field with no entry.
|
|
607
|
+
*
|
|
608
|
+
* The schema's own docstring already named the right path ("drives the
|
|
609
|
+
* (listingType, classified.meetupArea.city, createdAt) index"), and the
|
|
610
|
+
* store-scoped index has always been on `.city`.
|
|
611
|
+
*/
|
|
612
|
+
"classified.meetupArea.city": { canFilter: true, canSort: false },
|
|
598
613
|
"classified.acceptsShipping": { canFilter: true, canSort: false },
|
|
599
614
|
"classified.negotiable": { canFilter: true, canSort: false },
|
|
600
615
|
"digitalCode.codeDeliveryMethod": { canFilter: true, canSort: false },
|
|
@@ -603,6 +618,13 @@ ProductRepository.SIEVE_FIELDS = {
|
|
|
603
618
|
"liveItem.sex": { canFilter: true, canSort: false },
|
|
604
619
|
"liveItem.jurisdictionAllowed": { canFilter: true, canSort: false },
|
|
605
620
|
"liveItem.cites": { canFilter: true, canSort: false },
|
|
621
|
+
/*
|
|
622
|
+
* The live-items transport facet. `StoreLiveItemsListing` has listed
|
|
623
|
+
* `liveTransportMethod` in its FILTER_KEYS — so ticking it inflated the
|
|
624
|
+
* active-filter badge — while the key had no TYPE_FACET_FIELD mapping and
|
|
625
|
+
* this field had no entry, so it never reached Firestore from either end.
|
|
626
|
+
*/
|
|
627
|
+
"liveItem.transport.method": { canFilter: true, canSort: false },
|
|
606
628
|
};
|
|
607
629
|
/**
|
|
608
630
|
* Virtual filter aliases — callers can pass `f=listingType==auction` for
|
|
@@ -64,11 +64,15 @@ export interface ProductClassifiedMeta {
|
|
|
64
64
|
locality?: string;
|
|
65
65
|
pincode?: string;
|
|
66
66
|
};
|
|
67
|
-
/** How the buyer reaches the seller (chat | phone | both). */
|
|
68
|
-
contactMethod?: "chat" | "phone" | "both";
|
|
69
67
|
/** Seller offers shipping in addition to meetup. */
|
|
70
68
|
acceptsShipping?: boolean;
|
|
71
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Price is negotiable — UI hints "₹X (negotiable)".
|
|
71
|
+
*
|
|
72
|
+
* Its sibling `allowOffers` is what actually gates the offer FLOOR on this
|
|
73
|
+
* type: unset means the buyer may still request to buy, but only at the
|
|
74
|
+
* asking price.
|
|
75
|
+
*/
|
|
72
76
|
negotiable?: boolean;
|
|
73
77
|
}
|
|
74
78
|
export interface ProductDigitalCodeMeta {
|
|
@@ -110,6 +110,9 @@ export const productFeatureFormSchema = z.object({
|
|
|
110
110
|
storeId: annotate(productFeatureAdminCreateSchema.shape.storeId, {
|
|
111
111
|
section: "scope", order: 4, row: "pair",
|
|
112
112
|
help: "Only used when scope is \"store\".",
|
|
113
|
+
// Hidden for a global feature, and paired with visibleValues at the
|
|
114
|
+
// payload so a store id chosen and then abandoned is not saved.
|
|
115
|
+
when: (v) => v.scope === "store",
|
|
113
116
|
}),
|
|
114
117
|
isActive: annotate(productFeatureAdminCreateSchema.shape.isActive, {
|
|
115
118
|
section: "visibility", sectionLabel: "Visibility", order: 1, row: "quarter",
|
|
@@ -5,7 +5,6 @@ import { useState } from "react";
|
|
|
5
5
|
import { Pencil, Trash2, ToggleLeft, ToggleRight } from "lucide-react";
|
|
6
6
|
import { Badge, BaseListingCard, Button, ConfirmDeleteModal, Div, IconButton, Row, Span, Stack, Text } from "../../../ui";
|
|
7
7
|
import { useLongPress } from "../../../react/hooks/useLongPress";
|
|
8
|
-
import { useFeatureFlags } from "../../../client/api/useFeatureFlags";
|
|
9
8
|
import { ACCOUNT_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
9
|
const CLS_TOGGLE_ON = "h-4 w-4 text-success dark:text-success";
|
|
11
10
|
const CLS_DELETE_BTN = "p-[var(--appkit-space-1-5)] text-[var(--appkit-color-text-muted)] hover:bg-error-surface dark:hover:bg-error-surface hover:text-error dark:hover:text-error transition-colors disabled:opacity-50";
|
|
@@ -102,12 +101,14 @@ export function CouponCard({ coupon, labels: labelsProp, onCopy, className = "",
|
|
|
102
101
|
const discountLabel = formatDiscount(n, labels);
|
|
103
102
|
const hasAdminActions = Boolean(onEdit || onToggleActive || onDelete);
|
|
104
103
|
// Show Claim by default on public surfaces; hide automatically when this
|
|
105
|
-
// card is being rendered with admin/CRUD actions wired.
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
104
|
+
// card is being rendered with admin/CRUD actions wired.
|
|
105
|
+
//
|
|
106
|
+
// This also consulted the `COUPONS` env feature flag, because the claim
|
|
107
|
+
// endpoint 404'd when it was off. Both flag systems were deleted 2026-08-29 —
|
|
108
|
+
// the flag was `true` in every environment, and the endpoint now answers
|
|
109
|
+
// unconditionally under its own auth — so the fetch that backed this check
|
|
110
|
+
// is gone too, along with a network request on every coupon card render.
|
|
111
|
+
const showClaim = hideClaim === undefined ? !hasAdminActions : !hideClaim;
|
|
111
112
|
const handleCopy = () => {
|
|
112
113
|
if (!n.code)
|
|
113
114
|
return;
|
|
@@ -138,6 +138,20 @@ export interface ScammerDocument extends BaseDocument {
|
|
|
138
138
|
* Admin records keep the uid; public page shows "Anonymous".
|
|
139
139
|
*/
|
|
140
140
|
reportedByAnon: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* The reporter ticked "I confirm this report is truthful and accurate".
|
|
143
|
+
*
|
|
144
|
+
* 🛑 The form has always REQUIRED this — a `z.literal(true)` in
|
|
145
|
+
* `scamReportFormSchema` and a hard gate on submit — and never sent it. On a
|
|
146
|
+
* surface where a false report gets someone listed publicly and has account
|
|
147
|
+
* consequences for the reporter, the attestation is precisely the record you
|
|
148
|
+
* want to keep, and it existed only as a client-side gate that any direct
|
|
149
|
+
* call to the route bypassed entirely.
|
|
150
|
+
*
|
|
151
|
+
* Optional on the document so the rows written before 2026-08-29 stay valid;
|
|
152
|
+
* absent means "not recorded", which is honest, and is NOT the same as false.
|
|
153
|
+
*/
|
|
154
|
+
reporterAttested?: boolean;
|
|
141
155
|
status: ScammerStatus;
|
|
142
156
|
/** UID of admin/employee who verified or rejected. */
|
|
143
157
|
verifiedBy?: string;
|
|
@@ -342,7 +356,7 @@ export declare const SCAMMER_INDEXED_FIELDS: readonly ["status", "scamType", "sc
|
|
|
342
356
|
export declare const DEFAULT_SCAMMER_DATA: Partial<ScammerDocument>;
|
|
343
357
|
/** Max pending submissions per user — prevents spam flooding. */
|
|
344
358
|
export declare const MAX_PENDING_SCAMMER_REPORTS_PER_USER = 5;
|
|
345
|
-
export type ScammerCreateInput = Pick<ScammerDocument, "displayNames" | "phones" | "upiIds" | "emails" | "socialMedia" | "scamType" | "scamPlatform" | "description" | "amountLost" | "itemInvolved" | "evidence" | "reportedBy" | "reportedByAnon">;
|
|
359
|
+
export type ScammerCreateInput = Pick<ScammerDocument, "displayNames" | "phones" | "upiIds" | "emails" | "socialMedia" | "scamType" | "scamPlatform" | "description" | "amountLost" | "itemInvolved" | "evidence" | "reportedBy" | "reportedByAnon" | "reporterAttested">;
|
|
346
360
|
export type ScammerAdminUpdateInput = Partial<Pick<ScammerDocument, "status" | "verifiedBy" | "verifiedAt" | "verificationNote" | "relatedScammerIds" | "mergedFromIds" | "tags" | "displayNames" | "phones" | "upiIds" | "emails" | "socialMedia" | "isContested">>;
|
|
347
361
|
export declare const SCAMMER_FIELDS: {
|
|
348
362
|
readonly ID: "id";
|
|
@@ -5,7 +5,7 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
5
5
|
upiIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
6
6
|
emails: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
7
7
|
scamType: z.ZodString;
|
|
8
|
-
scamPlatform: z.
|
|
8
|
+
scamPlatform: z.ZodString;
|
|
9
9
|
amountLost: z.ZodOptional<z.ZodNumber>;
|
|
10
10
|
itemInvolved: z.ZodOptional<z.ZodString>;
|
|
11
11
|
description: z.ZodString;
|
|
@@ -15,9 +15,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
15
15
|
displayName: string;
|
|
16
16
|
description: string;
|
|
17
17
|
scamType: string;
|
|
18
|
+
scamPlatform: string;
|
|
18
19
|
agreed: true;
|
|
19
20
|
reportedByAnon?: boolean | undefined;
|
|
20
|
-
scamPlatform?: string | undefined;
|
|
21
21
|
amountLost?: number | undefined;
|
|
22
22
|
itemInvolved?: string | undefined;
|
|
23
23
|
phones?: string[] | undefined;
|
|
@@ -27,9 +27,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
27
27
|
displayName: string;
|
|
28
28
|
description: string;
|
|
29
29
|
scamType: string;
|
|
30
|
+
scamPlatform: string;
|
|
30
31
|
agreed: true;
|
|
31
32
|
reportedByAnon?: boolean | undefined;
|
|
32
|
-
scamPlatform?: string | undefined;
|
|
33
33
|
amountLost?: number | undefined;
|
|
34
34
|
itemInvolved?: string | undefined;
|
|
35
35
|
phones?: string[] | undefined;
|
|
@@ -39,9 +39,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
39
39
|
displayName: string;
|
|
40
40
|
description: string;
|
|
41
41
|
scamType: string;
|
|
42
|
+
scamPlatform: string;
|
|
42
43
|
agreed: true;
|
|
43
44
|
reportedByAnon?: boolean | undefined;
|
|
44
|
-
scamPlatform?: string | undefined;
|
|
45
45
|
amountLost?: number | undefined;
|
|
46
46
|
itemInvolved?: string | undefined;
|
|
47
47
|
phones?: string[] | undefined;
|
|
@@ -51,9 +51,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
51
51
|
displayName: string;
|
|
52
52
|
description: string;
|
|
53
53
|
scamType: string;
|
|
54
|
+
scamPlatform: string;
|
|
54
55
|
agreed: true;
|
|
55
56
|
reportedByAnon?: boolean | undefined;
|
|
56
|
-
scamPlatform?: string | undefined;
|
|
57
57
|
amountLost?: number | undefined;
|
|
58
58
|
itemInvolved?: string | undefined;
|
|
59
59
|
phones?: string[] | undefined;
|
|
@@ -45,8 +45,17 @@ export const scamReportFormSchema = z
|
|
|
45
45
|
scamType: annotate(z.string().trim().min(1, "Choose what kind of scam this was."), {
|
|
46
46
|
section: "what", sectionLabel: "What happened", sectionRequired: true, quick: true, order: 1, row: "pair",
|
|
47
47
|
}),
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
/*
|
|
49
|
+
* Required, matching `POST /api/scams/reports` (`z.string().min(1)`).
|
|
50
|
+
*
|
|
51
|
+
* It was `.optional()` here, so a report with no platform passed every
|
|
52
|
+
* client check and came back a 400. The page papered over it with a
|
|
53
|
+
* hand-written `if (!form.scamType || !form.scamPlatform)` guard — which is
|
|
54
|
+
* the same drift as the address postal rule: two rules for one field, and
|
|
55
|
+
* the looser one is the client's.
|
|
56
|
+
*/
|
|
57
|
+
scamPlatform: annotate(z.string().trim().min(1, "Choose where this happened.").max(120), {
|
|
58
|
+
section: "what", order: 2, row: "pair", kind: "select",
|
|
50
59
|
}),
|
|
51
60
|
/*
|
|
52
61
|
* Coerced and bounded. It arrives from a text input and is rendered on the
|
|
@@ -62,10 +71,16 @@ export const scamReportFormSchema = z
|
|
|
62
71
|
itemInvolved: annotate(z.string().trim().max(200).optional(), {
|
|
63
72
|
section: "what", order: 4, row: "pair",
|
|
64
73
|
}),
|
|
74
|
+
/*
|
|
75
|
+
* 100, matching the route. It was 30 here, so a 31-character description
|
|
76
|
+
* satisfied every client check and came back a 400 — and the page's own
|
|
77
|
+
* hand-written guard used 100, which is the number that was actually
|
|
78
|
+
* enforced. Two rules, three values.
|
|
79
|
+
*/
|
|
65
80
|
description: annotate(z
|
|
66
81
|
.string()
|
|
67
82
|
.trim()
|
|
68
|
-
.min(
|
|
83
|
+
.min(100, "Describe what happened in at least 100 characters — this is the evidence.")
|
|
69
84
|
.max(5000, "Keep the description under 5000 characters."), { section: "what", order: 5, row: "full", kind: "textarea" }),
|
|
70
85
|
reportedByAnon: annotate(z.boolean().optional(), {
|
|
71
86
|
section: "declare", sectionLabel: "Declaration", order: 1, row: "quarter",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { NavPortal, SidebarNavGroup } from "../../../_internal/shared/features/layout/types";
|
|
2
|
+
import type { ActionIndexEntry } from "./types";
|
|
3
|
+
/** A nav group set, with the portal it belongs to. */
|
|
4
|
+
export interface PortalNavGroups {
|
|
5
|
+
portal: NavPortal;
|
|
6
|
+
/** Human name for the breadcrumb — "Admin", "Seller", "Account". */
|
|
7
|
+
portalLabel: string;
|
|
8
|
+
groups: readonly SidebarNavGroup[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Every sidebar entry, as an index entry.
|
|
12
|
+
*
|
|
13
|
+
* `sectionPath` is the breadcrumb a result row shows — "Admin › System › Site
|
|
14
|
+
* Settings" — and it is the reason the group title is worth carrying: two
|
|
15
|
+
* portals both have "Orders", and without the path a result list shows the
|
|
16
|
+
* same word twice with no way to tell which one you want.
|
|
17
|
+
*/
|
|
18
|
+
export declare function deriveNavEntries(sources: readonly PortalNavGroups[]): ActionIndexEntry[];
|
|
19
|
+
/** One quick action, as the registry stores it. */
|
|
20
|
+
export interface QuickActionSource {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
keywords?: string[];
|
|
25
|
+
href?: string;
|
|
26
|
+
routeKey?: string;
|
|
27
|
+
iconKey?: string;
|
|
28
|
+
requiredPermission?: string;
|
|
29
|
+
requiredRole?: string;
|
|
30
|
+
requiresAuth?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Quick actions, as index entries.
|
|
34
|
+
*
|
|
35
|
+
* 🛑 An action with no resolvable href is DROPPED, not indexed with a blank
|
|
36
|
+
* one. `DASHBOARD_QUICK_ACTION_META`'s `routeKey` is dead — every page
|
|
37
|
+
* re-declares its own `*_QUICK_ACTION_HREFS` map instead — so a registry entry
|
|
38
|
+
* may genuinely have nowhere to go. Indexing it anyway produces a search
|
|
39
|
+
* result that does nothing when clicked, which is worse than not finding it.
|
|
40
|
+
*/
|
|
41
|
+
export declare function deriveQuickActionEntries(actions: readonly QuickActionSource[], portal: NavPortal, portalLabel: string, hrefFor: (action: QuickActionSource) => string | undefined): ActionIndexEntry[];
|
|
42
|
+
/**
|
|
43
|
+
* Assemble the static base.
|
|
44
|
+
*
|
|
45
|
+
* Deduplicated by id, first writer winning, so a quick action that shares an
|
|
46
|
+
* id with a nav entry does not produce two rows for one destination — and the
|
|
47
|
+
* nav entry, being passed first, is the one that survives.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildActionIndexBase(...groups: readonly ActionIndexEntry[][]): ActionIndexEntry[];
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: An index of "everything a user can do" is worth having exactly once and
|
|
3
|
+
* worth maintaining never. Hand-writing 260 entries beside the nav arrays
|
|
4
|
+
* they duplicate produces the drift this plan has spent every wave
|
|
5
|
+
* undoing — ten hand-written enumerations of one union (Root Cause #61),
|
|
6
|
+
* three option arrays that had each silently lost a value (W3c), fifteen
|
|
7
|
+
* postal rules (W5).
|
|
8
|
+
* WHAT: Derive the static base FROM the nav groups and the quick-action
|
|
9
|
+
* registry, so an entry cannot exist without its nav item and cannot
|
|
10
|
+
* go stale when one is renamed.
|
|
11
|
+
*
|
|
12
|
+
* W6 is what makes this possible: every nav item now carries a `description`,
|
|
13
|
+
* `keywords` and a derived `id`. Before that there was nothing to derive
|
|
14
|
+
* from — which is the actual reason `Search.tsx`'s finished `quickLinks` path
|
|
15
|
+
* was never wired to anything.
|
|
16
|
+
*
|
|
17
|
+
* EXPORTS: deriveNavEntries, deriveQuickActionEntries, buildActionIndexBase
|
|
18
|
+
*
|
|
19
|
+
* @tag domain:search
|
|
20
|
+
* @tag layer:shared
|
|
21
|
+
* @tag pattern:none
|
|
22
|
+
* @tag access:isomorphic
|
|
23
|
+
* @tag consumers:action-index route,CommandPalette
|
|
24
|
+
* @tag sideEffects:none
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Every sidebar entry, as an index entry.
|
|
28
|
+
*
|
|
29
|
+
* `sectionPath` is the breadcrumb a result row shows — "Admin › System › Site
|
|
30
|
+
* Settings" — and it is the reason the group title is worth carrying: two
|
|
31
|
+
* portals both have "Orders", and without the path a result list shows the
|
|
32
|
+
* same word twice with no way to tell which one you want.
|
|
33
|
+
*/
|
|
34
|
+
export function deriveNavEntries(sources) {
|
|
35
|
+
const entries = [];
|
|
36
|
+
for (const { portal, portalLabel, groups } of sources) {
|
|
37
|
+
for (const group of groups) {
|
|
38
|
+
for (const item of group.items) {
|
|
39
|
+
entries.push({
|
|
40
|
+
/*
|
|
41
|
+
* The nav item's own derived id, reused rather than re-derived. It
|
|
42
|
+
* is already `nav-{portal}-{slug}`, so prefixing the kind keeps the
|
|
43
|
+
* index's `{portal}:{kind}:{slug}` convention without inventing a
|
|
44
|
+
* second identity for the same thing.
|
|
45
|
+
*/
|
|
46
|
+
id: item.id ?? `${portal}:nav:${item.href}`,
|
|
47
|
+
kind: "nav",
|
|
48
|
+
portal,
|
|
49
|
+
label: item.label,
|
|
50
|
+
description: item.description,
|
|
51
|
+
keywords: item.keywords,
|
|
52
|
+
href: item.href,
|
|
53
|
+
sectionPath: `${portalLabel} › ${group.title}`,
|
|
54
|
+
...(item.requiredPermission
|
|
55
|
+
? { requiredPermission: item.requiredPermission }
|
|
56
|
+
: {}),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return entries;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Quick actions, as index entries.
|
|
65
|
+
*
|
|
66
|
+
* 🛑 An action with no resolvable href is DROPPED, not indexed with a blank
|
|
67
|
+
* one. `DASHBOARD_QUICK_ACTION_META`'s `routeKey` is dead — every page
|
|
68
|
+
* re-declares its own `*_QUICK_ACTION_HREFS` map instead — so a registry entry
|
|
69
|
+
* may genuinely have nowhere to go. Indexing it anyway produces a search
|
|
70
|
+
* result that does nothing when clicked, which is worse than not finding it.
|
|
71
|
+
*/
|
|
72
|
+
export function deriveQuickActionEntries(actions, portal, portalLabel, hrefFor) {
|
|
73
|
+
const entries = [];
|
|
74
|
+
for (const action of actions) {
|
|
75
|
+
const href = action.href ?? hrefFor(action);
|
|
76
|
+
if (!href)
|
|
77
|
+
continue;
|
|
78
|
+
entries.push({
|
|
79
|
+
id: `${portal}:action:${action.id}`,
|
|
80
|
+
kind: "action",
|
|
81
|
+
portal,
|
|
82
|
+
label: action.label,
|
|
83
|
+
description: action.description,
|
|
84
|
+
keywords: action.keywords,
|
|
85
|
+
href,
|
|
86
|
+
sectionPath: `${portalLabel} › Quick actions`,
|
|
87
|
+
...(action.iconKey ? { iconName: action.iconKey } : {}),
|
|
88
|
+
...(action.requiredPermission
|
|
89
|
+
? { requiredPermission: action.requiredPermission }
|
|
90
|
+
: {}),
|
|
91
|
+
...(action.requiredRole ? { requiredRole: action.requiredRole } : {}),
|
|
92
|
+
/*
|
|
93
|
+
* A quick action is a shortcut to something already reachable from the
|
|
94
|
+
* nav, so it ranks BELOW the nav entry for the same screen. Searching
|
|
95
|
+
* "orders" should land on Orders, not on "Create an order".
|
|
96
|
+
*/
|
|
97
|
+
weight: -10,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return entries;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Assemble the static base.
|
|
104
|
+
*
|
|
105
|
+
* Deduplicated by id, first writer winning, so a quick action that shares an
|
|
106
|
+
* id with a nav entry does not produce two rows for one destination — and the
|
|
107
|
+
* nav entry, being passed first, is the one that survives.
|
|
108
|
+
*/
|
|
109
|
+
export function buildActionIndexBase(...groups) {
|
|
110
|
+
const seen = new Set();
|
|
111
|
+
const out = [];
|
|
112
|
+
for (const list of groups) {
|
|
113
|
+
for (const entry of list) {
|
|
114
|
+
if (seen.has(entry.id))
|
|
115
|
+
continue;
|
|
116
|
+
seen.add(entry.id);
|
|
117
|
+
out.push(entry);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { BaseRepository } from "../../../providers/db-firebase";
|
|
2
|
+
import { type ActionIndexControl, type ActionIndexEntry, type ActionIndexOverride } from "./types";
|
|
3
|
+
/** The stored document. `id` is always `ACTION_INDEX_DOC_ID`. */
|
|
4
|
+
export interface ActionIndexDocument extends ActionIndexControl {
|
|
5
|
+
id: string;
|
|
6
|
+
updatedAt?: Date;
|
|
7
|
+
updatedBy?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class ActionIndexRepository extends BaseRepository<ActionIndexDocument> {
|
|
10
|
+
constructor();
|
|
11
|
+
/**
|
|
12
|
+
* The control document, or an empty one.
|
|
13
|
+
*
|
|
14
|
+
* 🛑 Returns EMPTY rather than null, and that is load-bearing:
|
|
15
|
+
* `mergeActionIndex` treats a missing control as "no overrides", so a site
|
|
16
|
+
* that has never opened the admin screen still gets the full static base.
|
|
17
|
+
* A null here would make every caller write the same `?? EMPTY` and one of
|
|
18
|
+
* them would forget, blanking the search.
|
|
19
|
+
*/
|
|
20
|
+
getControl(): Promise<ActionIndexControl>;
|
|
21
|
+
/**
|
|
22
|
+
* Overwrite one entry's override.
|
|
23
|
+
*
|
|
24
|
+
* Merged into the stored map rather than replacing it, because the admin
|
|
25
|
+
* screen edits one row at a time and a whole-map write would drop every
|
|
26
|
+
* other override made in a different browser tab — the lost-update shape
|
|
27
|
+
* that `mergeLotteryConfig` exists to prevent on the lottery side.
|
|
28
|
+
*/
|
|
29
|
+
setOverride(entryId: string, override: ActionIndexOverride, actorUid: string): Promise<void>;
|
|
30
|
+
/** Drop an override, returning the entry to whatever the code says. */
|
|
31
|
+
clearOverride(entryId: string, actorUid: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Replace the admin-authored entries.
|
|
34
|
+
*
|
|
35
|
+
* Whole-array, unlike `setOverride`: the custom list is edited as a list in
|
|
36
|
+
* one screen, and its ids are chosen by the admin rather than derived, so
|
|
37
|
+
* there is no stable key to merge on.
|
|
38
|
+
*/
|
|
39
|
+
setCustomEntries(custom: ActionIndexEntry[], actorUid: string): Promise<void>;
|
|
40
|
+
private write;
|
|
41
|
+
}
|
|
42
|
+
export declare const actionIndexRepository: ActionIndexRepository;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: D7 — the action index is a Firestore CONTROL PLANE over a static TS
|
|
3
|
+
* base, not a second copy of it. An admin can hide, rename, re-describe,
|
|
4
|
+
* retag, reorder and author entries; the code keeps `kind`, `href` and
|
|
5
|
+
* every permission field for built-ins, because renaming a toggle is
|
|
6
|
+
* content and repointing a built-in route is routing.
|
|
7
|
+
* WHAT: The `actionIndex/global` singleton and its repository.
|
|
8
|
+
*
|
|
9
|
+
* ## Deliberately NOT folded into `siteSettings`
|
|
10
|
+
*
|
|
11
|
+
* That document is read by an unauthenticated, edge-cached route
|
|
12
|
+
* (`GET /api/site-settings`, `s-maxage=600`). This one is projected by role and
|
|
13
|
+
* must never be served from a shared cache — putting them together is one
|
|
14
|
+
* refactor away from publishing the admin site map, which is exactly the class
|
|
15
|
+
* of defect Root Cause #70 records.
|
|
16
|
+
*
|
|
17
|
+
* ## One document, ~85 KB
|
|
18
|
+
*
|
|
19
|
+
* ~380 entries at full size, comfortably inside Firestore's 1 MB ceiling, and
|
|
20
|
+
* one read rather than a query. The read path caches for 5 minutes and is
|
|
21
|
+
* fetched **once per session per portal** — a read per keystroke would exhaust
|
|
22
|
+
* the 50k/day free tier at roughly thirty sessions, and typeahead matching has
|
|
23
|
+
* to be sub-16ms anyway, which a network hop is not.
|
|
24
|
+
*
|
|
25
|
+
* EXPORTS: ActionIndexRepository, actionIndexRepository
|
|
26
|
+
*
|
|
27
|
+
* @tag domain:search
|
|
28
|
+
* @tag layer:repository
|
|
29
|
+
* @tag pattern:singleton-doc
|
|
30
|
+
* @tag access:server
|
|
31
|
+
* @tag consumers:/api/admin/action-index,/api/action-index
|
|
32
|
+
* @tag sideEffects:firestore
|
|
33
|
+
*/
|
|
34
|
+
import { BaseRepository } from "../../../providers/db-firebase";
|
|
35
|
+
import { ACTION_INDEX_COLLECTION, ACTION_INDEX_DOC_ID, } from "./types";
|
|
36
|
+
/** What an empty control plane looks like — no overrides, no custom entries. */
|
|
37
|
+
const EMPTY_CONTROL = { entries: {}, custom: [] };
|
|
38
|
+
export class ActionIndexRepository extends BaseRepository {
|
|
39
|
+
constructor() {
|
|
40
|
+
super(ACTION_INDEX_COLLECTION);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The control document, or an empty one.
|
|
44
|
+
*
|
|
45
|
+
* 🛑 Returns EMPTY rather than null, and that is load-bearing:
|
|
46
|
+
* `mergeActionIndex` treats a missing control as "no overrides", so a site
|
|
47
|
+
* that has never opened the admin screen still gets the full static base.
|
|
48
|
+
* A null here would make every caller write the same `?? EMPTY` and one of
|
|
49
|
+
* them would forget, blanking the search.
|
|
50
|
+
*/
|
|
51
|
+
async getControl() {
|
|
52
|
+
const doc = await this.findById(ACTION_INDEX_DOC_ID);
|
|
53
|
+
if (!doc)
|
|
54
|
+
return EMPTY_CONTROL;
|
|
55
|
+
return { entries: doc.entries ?? {}, custom: doc.custom ?? [] };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Overwrite one entry's override.
|
|
59
|
+
*
|
|
60
|
+
* Merged into the stored map rather than replacing it, because the admin
|
|
61
|
+
* screen edits one row at a time and a whole-map write would drop every
|
|
62
|
+
* other override made in a different browser tab — the lost-update shape
|
|
63
|
+
* that `mergeLotteryConfig` exists to prevent on the lottery side.
|
|
64
|
+
*/
|
|
65
|
+
async setOverride(entryId, override, actorUid) {
|
|
66
|
+
const control = await this.getControl();
|
|
67
|
+
const next = {
|
|
68
|
+
...control,
|
|
69
|
+
entries: { ...control.entries, [entryId]: override },
|
|
70
|
+
};
|
|
71
|
+
await this.write(next, actorUid);
|
|
72
|
+
}
|
|
73
|
+
/** Drop an override, returning the entry to whatever the code says. */
|
|
74
|
+
async clearOverride(entryId, actorUid) {
|
|
75
|
+
const control = await this.getControl();
|
|
76
|
+
const entries = { ...control.entries };
|
|
77
|
+
delete entries[entryId];
|
|
78
|
+
await this.write({ ...control, entries }, actorUid);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Replace the admin-authored entries.
|
|
82
|
+
*
|
|
83
|
+
* Whole-array, unlike `setOverride`: the custom list is edited as a list in
|
|
84
|
+
* one screen, and its ids are chosen by the admin rather than derived, so
|
|
85
|
+
* there is no stable key to merge on.
|
|
86
|
+
*/
|
|
87
|
+
async setCustomEntries(custom, actorUid) {
|
|
88
|
+
const control = await this.getControl();
|
|
89
|
+
await this.write({ ...control, custom }, actorUid);
|
|
90
|
+
}
|
|
91
|
+
async write(control, actorUid) {
|
|
92
|
+
const payload = { ...control, updatedAt: new Date(), updatedBy: actorUid };
|
|
93
|
+
const existing = await this.findById(ACTION_INDEX_DOC_ID);
|
|
94
|
+
if (existing)
|
|
95
|
+
await this.update(ACTION_INDEX_DOC_ID, payload);
|
|
96
|
+
else
|
|
97
|
+
await this.createWithId(ACTION_INDEX_DOC_ID, payload);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export const actionIndexRepository = new ActionIndexRepository();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SiteSettingsTabId } from "../../admin/constants/site-settings-tabs";
|
|
2
|
+
import type { ActionIndexEntry } from "./types";
|
|
3
|
+
/** One control on the site-settings screen. */
|
|
4
|
+
export interface SettingsIndexSource {
|
|
5
|
+
/** Slug — becomes both the entry id and the `#anchor`. */
|
|
6
|
+
slug: string;
|
|
7
|
+
label: string;
|
|
8
|
+
description: string;
|
|
9
|
+
keywords: string[];
|
|
10
|
+
tab: SiteSettingsTabId;
|
|
11
|
+
/** `toggle` for an on/off switch, `setting` for a value. */
|
|
12
|
+
kind: "toggle" | "setting";
|
|
13
|
+
/**
|
|
14
|
+
* Whether the view renders `id="setting-{slug}"` for this control.
|
|
15
|
+
*
|
|
16
|
+
* 🛑 Opt-in, and audited. An anchor that matches no `id=` in the DOM scrolls
|
|
17
|
+
* nowhere and reads as the search having missed — so claiming one you have
|
|
18
|
+
* not added is worse than not claiming it. An entry without one deep-links
|
|
19
|
+
* to its TAB, which is truthful: "this lives on the Fees tab" is a smaller
|
|
20
|
+
* promise than "here is the control", and it is one the page can keep.
|
|
21
|
+
*/
|
|
22
|
+
anchored?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The controls worth finding.
|
|
26
|
+
*
|
|
27
|
+
* Deliberately not all ~160: a search result list is only useful while it is
|
|
28
|
+
* short, and "Hero title" is not something anyone hunts for — they open the
|
|
29
|
+
* About tab and see it. What is here is what someone arrives ALREADY looking
|
|
30
|
+
* for, usually in a hurry, and usually not knowing which tab it is filed under.
|
|
31
|
+
*/
|
|
32
|
+
export declare const SITE_SETTINGS_ENTRIES: readonly SettingsIndexSource[];
|
|
33
|
+
/**
|
|
34
|
+
* The settings entries, as index entries.
|
|
35
|
+
*
|
|
36
|
+
* `deepLink` carries the whole query-and-hash — `?tab=fees#setting-cod-enabled`
|
|
37
|
+
* — rather than just the tab, because landing on the right tab and leaving the
|
|
38
|
+
* reader to find the control among forty others is most of the way to not
|
|
39
|
+
* having searched at all.
|
|
40
|
+
*/
|
|
41
|
+
export declare function deriveSettingsEntries(settingsHref: string, requiredPermission?: string): ActionIndexEntry[];
|