@mohasinac/appkit 4.53.0 → 4.53.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/scaffolds/AppShell.js +1 -1
- package/dist/_internal/client/theme/ThemeProvider.js +15 -1
- package/dist/_internal/server/features/auctions/og.d.ts +3 -1
- package/dist/_internal/server/features/auctions/og.js +3 -1
- package/dist/_internal/server/features/bundles/actions.js +1 -1
- package/dist/_internal/server/features/business-documents/download.d.ts +41 -0
- package/dist/_internal/server/features/business-documents/download.js +91 -0
- package/dist/_internal/server/features/categories/adapters.d.ts +1 -1
- package/dist/_internal/server/features/categories/adapters.js +10 -0
- package/dist/_internal/server/features/categories/price-guidance-resolve.d.ts +66 -0
- package/dist/_internal/server/features/categories/price-guidance-resolve.js +138 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +14 -0
- package/dist/_internal/server/features/checkout/actions.js +106 -12
- package/dist/_internal/server/features/checkout/bundle-expansion.js +17 -0
- package/dist/_internal/server/features/checkout/data.d.ts +22 -0
- package/dist/_internal/server/features/checkout/group-rule-revalidation.d.ts +34 -0
- package/dist/_internal/server/features/checkout/group-rule-revalidation.js +124 -0
- package/dist/_internal/server/features/checkout/supplier-identity.d.ts +55 -0
- package/dist/_internal/server/features/checkout/supplier-identity.js +81 -0
- package/dist/_internal/server/features/classified/metadata.js +1 -1
- package/dist/_internal/server/features/classified/og.d.ts +3 -1
- package/dist/_internal/server/features/classified/og.js +3 -1
- package/dist/_internal/server/features/digital-code/metadata.js +1 -1
- package/dist/_internal/server/features/digital-code/og.d.ts +3 -1
- package/dist/_internal/server/features/digital-code/og.js +3 -1
- package/dist/_internal/server/features/digital-code/pool.d.ts +19 -4
- package/dist/_internal/server/features/digital-code/pool.js +19 -4
- package/dist/_internal/server/features/live/metadata.js +1 -1
- package/dist/_internal/server/features/live/og.d.ts +3 -1
- package/dist/_internal/server/features/live/og.js +3 -1
- package/dist/_internal/server/features/messages/actions.js +1 -1
- package/dist/_internal/server/features/parts/og.js +1 -1
- package/dist/_internal/server/features/pre-orders/og.d.ts +3 -1
- package/dist/_internal/server/features/pre-orders/og.js +3 -1
- package/dist/_internal/server/features/price-index/market.d.ts +57 -0
- package/dist/_internal/server/features/price-index/market.js +120 -0
- package/dist/_internal/server/features/price-index/search.d.ts +18 -0
- package/dist/_internal/server/features/price-index/search.js +47 -7
- package/dist/_internal/server/features/prize-draws/og.d.ts +3 -1
- package/dist/_internal/server/features/prize-draws/og.js +3 -1
- package/dist/_internal/server/features/products/group-members.d.ts +26 -0
- package/dist/_internal/server/features/products/group-members.js +7 -1
- package/dist/_internal/server/features/products/list-public.d.ts +12 -0
- package/dist/_internal/server/features/products/list-public.js +10 -1
- package/dist/_internal/server/features/products/listing-tab-counts.d.ts +25 -1
- package/dist/_internal/server/features/products/listing-tab-counts.js +128 -10
- package/dist/_internal/server/features/products/og.d.ts +3 -1
- package/dist/_internal/server/features/products/og.js +3 -1
- package/dist/_internal/server/features/search/scoped-sources.d.ts +26 -0
- package/dist/_internal/server/features/search/scoped-sources.js +252 -0
- package/dist/_internal/server/features/search/search-everything.d.ts +57 -0
- package/dist/_internal/server/features/search/search-everything.js +70 -20
- package/dist/_internal/server/features/seo/sitemap.js +6 -0
- package/dist/_internal/server/features/shared/listing-adapters.js +4 -2
- package/dist/_internal/server/features/sticker-trace/adapters.d.ts +11 -0
- package/dist/_internal/server/features/sticker-trace/adapters.js +1 -0
- package/dist/_internal/server/features/sticker-trace/promote-to-listings.d.ts +26 -0
- package/dist/_internal/server/features/sticker-trace/promote-to-listings.js +264 -0
- package/dist/_internal/server/features/sticker-trace/sheet-actions.d.ts +3 -1
- package/dist/_internal/server/features/sticker-trace/sheet-actions.js +32 -0
- package/dist/_internal/server/features/sticker-trace/storage.d.ts +20 -1
- package/dist/_internal/server/features/sticker-trace/storage.js +20 -1
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +16 -0
- package/dist/_internal/server/features/stores/index.d.ts +1 -0
- package/dist/_internal/server/features/stores/index.js +1 -0
- package/dist/_internal/server/features/stores/tabs.d.ts +30 -0
- package/dist/_internal/server/features/stores/tabs.js +124 -0
- package/dist/_internal/shared/checkout/rules/_defaults.js +0 -2
- package/dist/_internal/shared/checkout/rules/_limits.d.ts +14 -1
- package/dist/_internal/shared/checkout/rules/_limits.js +14 -1
- package/dist/_internal/shared/checkout/rules/_registry.js +2 -0
- package/dist/_internal/shared/checkout/rules/auction.rule.js +9 -1
- package/dist/_internal/shared/checkout/rules/bundle.rule.js +13 -0
- package/dist/_internal/shared/checkout/rules/index.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/index.js +9 -0
- package/dist/_internal/shared/checkout/rules/offer.rule.js +0 -1
- package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +26 -0
- package/dist/_internal/shared/checkout/rules/payment-gate.js +133 -0
- package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +29 -8
- package/dist/_internal/shared/checkout/rules/spares.rule.d.ts +16 -0
- package/dist/_internal/shared/checkout/rules/spares.rule.js +5 -0
- package/dist/_internal/shared/checkout/rules/types.d.ts +26 -2
- package/dist/_internal/shared/config/schema.d.ts +122 -0
- package/dist/_internal/shared/features/cart/schema.js +3 -2
- package/dist/_internal/shared/features/products/group-rules.d.ts +75 -0
- package/dist/_internal/shared/features/products/group-rules.js +142 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +24 -0
- package/dist/_internal/shared/listing-types/_registry.js +39 -1
- package/dist/_internal/shared/listing-types/art/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/config.js +2 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/auction/config.js +2 -0
- package/dist/_internal/shared/listing-types/availability.js +48 -0
- package/dist/_internal/shared/listing-types/capabilities.js +19 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/config.js +2 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +2 -0
- package/dist/_internal/shared/listing-types/feature-flags.js +1 -0
- package/dist/_internal/shared/listing-types/live/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/config.js +2 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +2 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +2 -0
- package/dist/_internal/shared/listing-types/spares/config.d.ts +74 -0
- package/dist/_internal/shared/listing-types/spares/config.js +60 -0
- package/dist/_internal/shared/listing-types/spares/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/spares/ctas.js +4 -0
- package/dist/_internal/shared/listing-types/spares/og.js +1 -0
- package/dist/_internal/shared/listing-types/spares/schema.d.ts +2 -0
- package/dist/_internal/shared/listing-types/spares/schema.js +3 -0
- package/dist/_internal/shared/listing-types/spares/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/spares/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/standard/config.js +2 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +2 -0
- package/dist/_internal/shared/search/match.d.ts +11 -0
- package/dist/_internal/shared/search/match.js +30 -3
- package/dist/_internal/shared/search/to-groups.d.ts +52 -0
- package/dist/_internal/shared/search/to-groups.js +141 -0
- package/dist/client.d.ts +8 -2
- package/dist/client.js +4 -1
- package/dist/constants/api-endpoints.d.ts +46 -15
- package/dist/constants/api-endpoints.js +35 -5
- package/dist/constants/cache-invalidation.js +3 -1
- package/dist/constants/field-names.d.ts +6 -0
- package/dist/constants/field-names.js +6 -0
- package/dist/features/about/components/PolicyPageView.d.ts +22 -2
- package/dist/features/about/components/PolicyPageView.js +14 -2
- package/dist/features/about/components/PublicProfileView.js +2 -1
- package/dist/features/about/components/ShippingPolicyView.js +42 -2
- package/dist/features/account/components/NotificationBell.js +1 -1
- package/dist/features/account/hooks/useProfileStats.js +5 -1
- package/dist/features/addresses/repository/addresses.repository.d.ts +16 -0
- package/dist/features/addresses/repository/addresses.repository.js +21 -0
- package/dist/features/admin/actions/admin-actions.js +16 -1
- package/dist/features/admin/components/AdminCategoryEditorView.js +19 -2
- package/dist/features/admin/components/AdminNavigationView.js +1 -1
- package/dist/features/admin/components/AdminPageHeader.js +1 -1
- package/dist/features/admin/components/AdminProductEditorView.js +17 -2
- package/dist/features/admin/components/AdminSidebar.js +1 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +39 -8
- package/dist/features/admin/components/AdminSparesView.d.ts +12 -0
- package/dist/features/admin/components/AdminSparesView.js +30 -0
- package/dist/features/admin/components/AdminUsersView.js +9 -7
- package/dist/features/admin/components/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- package/dist/features/admin/components/sections/adminSectionsTypes.js +7 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +3 -0
- package/dist/features/admin/constants/filter-tabs.js +1 -0
- package/dist/features/admin/schemas/firestore.d.ts +26 -2
- package/dist/features/auctions/components/AuctionDetailPageView.js +12 -18
- package/dist/features/auth/repository/user.repository.d.ts +13 -0
- package/dist/features/auth/repository/user.repository.js +28 -0
- package/dist/features/auth/schemas/firestore.d.ts +2 -0
- package/dist/features/auth/schemas/firestore.js +2 -0
- package/dist/features/business-documents/repository/business-documents.repository.d.ts +58 -0
- package/dist/features/business-documents/repository/business-documents.repository.js +54 -0
- package/dist/features/business-documents/schemas/firestore.d.ts +105 -0
- package/dist/features/business-documents/schemas/firestore.js +75 -0
- package/dist/features/cart/actions/cart-actions.js +83 -6
- package/dist/features/cart/repository/cart.repository.js +32 -4
- package/dist/features/cart/schemas/firestore.d.ts +20 -0
- package/dist/features/cart/schemas/index.d.ts +2 -2
- package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +1 -1
- package/dist/features/categories/api/[id]/price-guidance/route.d.ts +42 -0
- package/dist/features/categories/api/[id]/price-guidance/route.js +90 -0
- package/dist/features/categories/components/BrandDetailPageView.js +32 -8
- package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -1
- package/dist/features/categories/components/BrandDetailTabs.js +28 -3
- package/dist/features/categories/components/BundleCollage.js +3 -2
- package/dist/features/categories/components/BundleItemsPicker.js +3 -4
- package/dist/features/categories/components/CategoriesIndexListing.js +23 -8
- package/dist/features/categories/components/CategoryDetailPageView.js +157 -20
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +7 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +41 -7
- package/dist/features/categories/components/CategoryGrid.js +10 -1
- package/dist/features/categories/components/PriceGuidanceHint.d.ts +29 -0
- package/dist/features/categories/components/PriceGuidanceHint.js +61 -0
- package/dist/features/categories/components/SubcategoryCardRow.d.ts +27 -0
- package/dist/features/categories/components/SubcategoryCardRow.js +98 -0
- package/dist/features/categories/constants/category-tabs.d.ts +20 -25
- package/dist/features/categories/constants/category-tabs.js +22 -39
- package/dist/features/categories/repository/categories.repository.d.ts +12 -0
- package/dist/features/categories/repository/categories.repository.js +20 -0
- package/dist/features/categories/schemas/firestore.d.ts +22 -1
- package/dist/features/categories/schemas/firestore.js +10 -0
- package/dist/features/categories/schemas/index.d.ts +6 -0
- package/dist/features/categories/schemas/index.js +2 -0
- package/dist/features/categories/types/index.d.ts +8 -0
- package/dist/features/categories/utils/taxonomy-visual.d.ts +16 -3
- package/dist/features/categories/utils/taxonomy-visual.js +26 -10
- package/dist/features/classified/components/ClassifiedDetailPageView.js +14 -10
- package/dist/features/contact/components/ContactPageView.d.ts +33 -1
- package/dist/features/contact/components/ContactPageView.js +58 -28
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +14 -10
- package/dist/features/grouped/components/GroupedListingDetailView.js +9 -1
- package/dist/features/grouped/schemas/firestore.d.ts +5 -0
- package/dist/features/homepage/actions/homepage-section-actions.d.ts +3 -3
- package/dist/features/homepage/components/CollectionCardsSection.d.ts +28 -6
- package/dist/features/homepage/components/CollectionCardsSection.js +43 -8
- package/dist/features/homepage/components/HowItWorksSection.d.ts +23 -6
- package/dist/features/homepage/components/HowItWorksSection.js +4 -4
- package/dist/features/homepage/components/PromoGrid.d.ts +31 -2
- package/dist/features/homepage/components/PromoGrid.js +12 -3
- package/dist/features/homepage/components/TestimonialsCarousel.d.ts +34 -2
- package/dist/features/homepage/components/TestimonialsCarousel.js +1 -1
- package/dist/features/homepage/components/index.d.ts +0 -20
- package/dist/features/homepage/components/index.js +0 -11
- package/dist/features/homepage/index.d.ts +0 -2
- package/dist/features/homepage/index.js +0 -2
- package/dist/features/homepage/lib/section-renderer.d.ts +9 -0
- package/dist/features/homepage/lib/section-renderer.js +202 -58
- package/dist/features/homepage/repository/homepage-sections.repository.d.ts +35 -3
- package/dist/features/homepage/repository/homepage-sections.repository.js +58 -8
- package/dist/features/homepage/schemas/firestore.d.ts +106 -3
- package/dist/features/homepage/schemas/firestore.js +32 -2
- package/dist/features/homepage/schemas/index.d.ts +36 -7
- package/dist/features/homepage/schemas/index.js +39 -27
- package/dist/features/homepage/server.d.ts +0 -3
- package/dist/features/homepage/server.js +0 -3
- package/dist/features/homepage/types/index.d.ts +0 -47
- package/dist/features/homepage/types/index.js +49 -0
- package/dist/features/layout/AppLayoutShell.js +1 -1
- package/dist/features/layout/BottomNavItem.js +1 -1
- package/dist/features/layout/BottomSheet.js +1 -1
- package/dist/features/layout/TitleBarLayout.js +1 -1
- package/dist/features/live/components/LiveItemDetailPageView.js +14 -10
- package/dist/features/media/hooks/useMedia.js +17 -0
- package/dist/features/media/repository/media-assets.repository.d.ts +46 -0
- package/dist/features/media/repository/media-assets.repository.js +31 -0
- package/dist/features/media/trust-signals.d.ts +85 -0
- package/dist/features/media/trust-signals.js +126 -0
- package/dist/features/parts/components/PartDetailPageView.d.ts +14 -1
- package/dist/features/parts/components/PartDetailPageView.js +2 -2
- package/dist/features/parts/components/PartsIndexPageView.d.ts +11 -2
- package/dist/features/parts/components/PartsIndexPageView.js +2 -2
- package/dist/features/parts/components/PartsSearchBox.d.ts +9 -0
- package/dist/features/parts/components/PartsSearchBox.js +57 -0
- package/dist/features/parts/schemas/firestore.d.ts +149 -3
- package/dist/features/parts/schemas/firestore.js +184 -0
- package/dist/features/payments/repository/payout.repository.d.ts +15 -0
- package/dist/features/payments/repository/payout.repository.js +21 -0
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +12 -18
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +1 -1
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +24 -1
- package/dist/features/price-index/components/PriceIndexToolView.js +299 -17
- package/dist/features/price-index/lookup-target.d.ts +48 -0
- package/dist/features/price-index/lookup-target.js +107 -0
- package/dist/features/products/components/AvailabilityScopeHint.d.ts +22 -0
- package/dist/features/products/components/AvailabilityScopeHint.js +27 -0
- package/dist/features/products/components/CompareOverlay.d.ts +20 -2
- package/dist/features/products/components/CompareOverlay.js +23 -3
- package/dist/features/products/components/CustomSectionTabContent.d.ts +31 -0
- package/dist/features/products/components/CustomSectionTabContent.js +29 -3
- package/dist/features/products/components/GroupMemberPicker.d.ts +19 -1
- package/dist/features/products/components/GroupMemberPicker.js +46 -8
- package/dist/features/products/components/GroupSettingsPanel.d.ts +11 -1
- package/dist/features/products/components/GroupSettingsPanel.js +92 -3
- package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
- package/dist/features/products/components/PrizeDrawEntryActions.d.ts +15 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +70 -8
- package/dist/features/products/components/ProductDetailPageView.js +81 -17
- package/dist/features/products/components/ProductForm.d.ts +13 -1
- package/dist/features/products/components/ProductForm.js +16 -2
- package/dist/features/products/components/ProductsIndexListing.d.ts +16 -1
- package/dist/features/products/components/ProductsIndexListing.js +3 -3
- package/dist/features/products/components/ShowGroupSection.js +15 -2
- package/dist/features/products/components/SparesListView.d.ts +7 -0
- package/dist/features/products/components/SparesListView.js +39 -0
- package/dist/features/products/components/SublistingCarouselSection.js +2 -1
- package/dist/features/products/constants/action-defs.d.ts +1 -0
- package/dist/features/products/constants/action-defs.js +1 -0
- package/dist/features/products/repository/products.repository.d.ts +99 -0
- package/dist/features/products/repository/products.repository.js +240 -18
- package/dist/features/products/schemas/firestore.d.ts +97 -4
- package/dist/features/products/schemas/firestore.js +104 -0
- package/dist/features/products/schemas/index.d.ts +10 -19
- package/dist/features/products/schemas/index.js +23 -6
- package/dist/features/products/types/index.d.ts +45 -1
- package/dist/features/products/utils/group-member-image.d.ts +46 -0
- package/dist/features/products/utils/group-member-image.js +4 -0
- package/dist/features/products/utils/listing-badge-variant.js +4 -0
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/scams/components/ScamProfileView.js +1 -1
- package/dist/features/search/components/EntityTypeahead.d.ts +44 -0
- package/dist/features/search/components/EntityTypeahead.js +92 -0
- package/dist/features/search/components/Search.d.ts +12 -1
- package/dist/features/search/components/Search.js +47 -3
- package/dist/features/search/components/SearchFacetRail.d.ts +11 -0
- package/dist/features/search/components/SearchFacetRail.js +92 -0
- package/dist/features/search/components/SearchResultsClient.d.ts +33 -0
- package/dist/features/search/components/SearchResultsClient.js +103 -0
- package/dist/features/search/components/SearchResultsPageView.d.ts +15 -1
- package/dist/features/search/components/SearchResultsPageView.js +2 -2
- package/dist/features/search/components/index.d.ts +0 -6
- package/dist/features/search/components/index.js +0 -3
- package/dist/features/search/hooks/useNavSuggestions.d.ts +13 -0
- package/dist/features/search/hooks/useScopedSearch.d.ts +34 -0
- package/dist/features/search/hooks/useScopedSearch.js +114 -0
- package/dist/features/search/hooks/useSiteSearch.d.ts +26 -0
- package/dist/features/search/hooks/useSiteSearch.js +149 -0
- package/dist/features/search/index.d.ts +0 -4
- package/dist/features/search/index.js +0 -4
- package/dist/features/search/manifest.d.ts +12 -0
- package/dist/features/search/manifest.js +17 -2
- package/dist/features/search/server.d.ts +0 -3
- package/dist/features/search/server.js +0 -3
- package/dist/features/seller/components/SellerListingTypeView.d.ts +1 -1
- package/dist/features/seller/components/SellerListingTypeView.js +20 -0
- package/dist/features/seller/components/SellerPayoutSettingsView.js +35 -14
- package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
- package/dist/features/seller/components/SellerProductShell.js +2 -1
- package/dist/features/seller/components/SellerProductsView.js +3 -2
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/seller/components/SellerSparesView.d.ts +17 -0
- package/dist/features/seller/components/SellerSparesView.js +6 -0
- package/dist/features/seller/components/index.d.ts +2 -0
- package/dist/features/seller/components/index.js +1 -0
- package/dist/features/shell/CommandPalette.js +1 -1
- package/dist/features/sticker-trace/schemas/firestore.d.ts +19 -0
- package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
- package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +7 -82
- package/dist/features/stores/components/StoreHeader.js +1 -2
- package/dist/features/stores/components/StoreSparesPageView.d.ts +27 -0
- package/dist/features/stores/components/StoreSparesPageView.js +48 -0
- package/dist/features/stores/schemas/firestore.d.ts +19 -0
- package/dist/features/tester/seed-data/authored/buying__spares-and-groups.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__spares-and-groups.js +96 -0
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +392 -45
- package/dist/features/tester/seed-data/authored/content-discovery__category-tabs-franchise-bundles.js +54 -32
- package/dist/features/tester/seed-data/authored/content-discovery__parts-database.js +3 -2
- package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.js +73 -0
- package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.js +340 -0
- package/dist/features/tester/seed-data/authored/index.js +16 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.d.ts +1 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -0
- package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.js +77 -0
- package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.js +58 -0
- package/dist/features/tester/seed-data/authored/tools__sticker-tracer.js +5 -4
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +384 -18
- package/dist/index.d.ts +17 -57
- package/dist/index.js +35 -96
- package/dist/next/routing/route-map.d.ts +42 -1
- package/dist/next/routing/route-map.js +45 -0
- package/dist/providers/db-firebase/base.repository.d.ts +22 -0
- package/dist/providers/db-firebase/base.repository.js +36 -0
- package/dist/schemas/registry.d.ts +15 -6
- package/dist/security/blind-index-tokens.d.ts +54 -0
- package/dist/security/blind-index-tokens.js +152 -0
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/settings-encryption.d.ts +35 -0
- package/dist/security/settings-encryption.js +91 -0
- package/dist/seed/_helpers/brand-rows.js +38 -14
- package/dist/seed/_helpers/category-forest.js +73 -7
- package/dist/seed/_helpers/category-images-generated.d.ts +25 -6
- package/dist/seed/_helpers/category-images-generated.js +81 -6
- package/dist/seed/_helpers/category-models.d.ts +1 -1
- package/dist/seed/_helpers/category-models.js +7 -1
- package/dist/seed/_helpers/category-parts-glossary-map.js +8 -0
- package/dist/seed/_helpers/category-parts.d.ts +1 -1
- package/dist/seed/_helpers/category-parts.js +50 -2
- package/dist/seed/_helpers/character-features.d.ts +1 -1
- package/dist/seed/_helpers/character-features.js +2 -1
- package/dist/seed/_helpers/franchise-rows.js +10 -2
- package/dist/seed/_helpers/media.d.ts +47 -0
- package/dist/seed/_helpers/media.js +34 -0
- package/dist/seed/authenticity-knowledge-seed-data.d.ts +49 -5
- package/dist/seed/authenticity-knowledge-seed-data.js +10001 -549
- package/dist/seed/categories-seed-data.js +124 -20
- package/dist/seed/grouped-listings-seed-data.js +16 -0
- package/dist/seed/homepage-sections-seed-data.js +94 -5
- package/dist/seed/index.d.ts +2 -3
- package/dist/seed/index.js +2 -2
- package/dist/seed/manifest.js +3 -0
- package/dist/seed/media-assets-seed-data.d.ts +1 -1
- package/dist/seed/media-assets-seed-data.js +746 -196
- package/dist/seed/model-composition-seed-data.js +122 -6
- package/dist/seed/part-glossary-seed-data.d.ts +1 -1
- package/dist/seed/part-glossary-seed-data.js +37219 -463
- package/dist/seed/products-art-seed-data.js +7 -18
- package/dist/seed/products-auctions-seed-data.js +56 -47
- package/dist/seed/products-beyblade-x-seed-data.d.ts +2 -0
- package/dist/seed/products-beyblade-x-seed-data.js +476 -0
- package/dist/seed/products-catalogue-seed-data.d.ts +5 -5
- package/dist/seed/products-catalogue-seed-data.js +1500 -1240
- package/dist/seed/products-classifieds-seed-data.js +9 -27
- package/dist/seed/products-digital-codes-seed-data.js +9 -18
- package/dist/seed/products-live-items-seed-data.js +5 -13
- package/dist/seed/products-preorders-seed-data.js +67 -33
- package/dist/seed/products-prize-draws-seed-data.js +8 -17
- package/dist/seed/products-spares-seed-data.d.ts +2 -0
- package/dist/seed/products-spares-seed-data.js +368 -0
- package/dist/seed/products-standard-seed-data.js +39 -126
- package/dist/seed/products-stickers-seed-data.js +59 -15
- package/dist/seed/site-settings-seed-data.js +8 -0
- package/dist/seo/json-ld.d.ts +25 -0
- package/dist/seo/json-ld.js +28 -0
- package/dist/server-entry.d.ts +3 -1
- package/dist/server-entry.js +2 -2
- package/dist/server.d.ts +11 -9
- package/dist/server.js +35 -25
- package/dist/styles.css +202 -1
- package/dist/tailwind-input.css +31 -17
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Accordion.js +1 -1
- package/dist/ui/components/Breadcrumb.js +1 -1
- package/dist/ui/components/DetailPageGallery.js +0 -2
- package/dist/ui/components/DetailPageHero.js +0 -1
- package/dist/ui/components/DetailPageTabs.js +1 -1
- package/dist/ui/components/Divider.js +3 -3
- package/dist/ui/components/GatedPrice.js +5 -1
- package/dist/ui/components/HorizontalScroller.js +1 -1
- package/dist/ui/components/ListingLayout.js +3 -3
- package/dist/ui/components/MediaTabs.style.css +81 -0
- package/dist/ui/components/Pagination.js +1 -1
- package/dist/ui/components/PendingValue.d.ts +45 -0
- package/dist/ui/components/PendingValue.js +7 -0
- package/dist/ui/components/Radio.style.css +119 -0
- package/dist/ui/components/RatingDisplay.js +2 -2
- package/dist/ui/components/SettingsSection.js +0 -2
- package/dist/ui/components/SiteLogo.js +3 -0
- package/dist/ui/components/SiteMark.js +3 -0
- package/dist/ui/components/SortDropdown.js +1 -1
- package/dist/ui/components/SpecTable.d.ts +58 -0
- package/dist/ui/components/SpecTable.js +8 -0
- package/dist/ui/components/StarRating.js +1 -3
- package/dist/ui/components/SummaryCard.js +1 -1
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/icons/icon-registry.d.ts +1 -0
- package/dist/ui/icons/icon-registry.js +9 -1
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +2 -0
- package/dist/utils/search-txt-query.d.ts +12 -0
- package/dist/utils/search-txt-query.js +12 -0
- package/package.json +1 -1
- package/scripts/dedupe-peer-deps.mjs +18 -0
- package/scripts/seed-cli.mjs +250 -35
- package/scripts/verify-css-build.mjs +118 -37
- package/dist/_internal/server/features/search/actions.d.ts +0 -7
- package/dist/_internal/server/features/search/actions.js +0 -21
- package/dist/_internal/server/features/search/data.d.ts +0 -15
- package/dist/_internal/server/features/search/data.js +0 -18
- package/dist/_internal/server/features/search/index.d.ts +0 -3
- package/dist/_internal/server/features/search/index.js +0 -2
- package/dist/features/homepage/api/[id]/route.d.ts +0 -23
- package/dist/features/homepage/api/[id]/route.js +0 -103
- package/dist/features/homepage/api/route.d.ts +0 -14
- package/dist/features/homepage/api/route.js +0 -140
- package/dist/features/homepage/api/sections/[id]/route.d.ts +0 -26
- package/dist/features/homepage/api/sections/[id]/route.js +0 -95
- package/dist/features/homepage/api/sections/route.d.ts +0 -13
- package/dist/features/homepage/api/sections/route.js +0 -82
- package/dist/features/homepage/components/AdvertisementBanner.d.ts +0 -14
- package/dist/features/homepage/components/AdvertisementBanner.js +0 -35
- package/dist/features/homepage/components/BeforeAfterCard.d.ts +0 -9
- package/dist/features/homepage/components/BeforeAfterCard.js +0 -73
- package/dist/features/homepage/components/FeaturedResultsSection.d.ts +0 -17
- package/dist/features/homepage/components/FeaturedResultsSection.js +0 -17
- package/dist/features/homepage/components/HeroBanner.d.ts +0 -7
- package/dist/features/homepage/components/HeroBanner.js +0 -43
- package/dist/features/homepage/components/HeroSection.d.ts +0 -7
- package/dist/features/homepage/components/HeroSection.js +0 -6
- package/dist/features/homepage/components/HomepageSkeleton.d.ts +0 -2
- package/dist/features/homepage/components/HomepageSkeleton.js +0 -21
- package/dist/features/homepage/components/HomepageView.d.ts +0 -25
- package/dist/features/homepage/components/HomepageView.js +0 -5
- package/dist/features/homepage/components/NewsletterBanner.d.ts +0 -23
- package/dist/features/homepage/components/NewsletterBanner.js +0 -37
- package/dist/features/homepage/components/SiteFeaturesSection.d.ts +0 -14
- package/dist/features/homepage/components/SiteFeaturesSection.js +0 -11
- package/dist/features/homepage/components/TrustBadges.d.ts +0 -6
- package/dist/features/homepage/components/TrustBadges.js +0 -46
- package/dist/features/homepage/components/TrustIndicatorsSection.d.ts +0 -12
- package/dist/features/homepage/components/TrustIndicatorsSection.js +0 -11
- package/dist/features/homepage/hooks/useHomepage.d.ts +0 -11
- package/dist/features/homepage/hooks/useHomepage.js +0 -17
- package/dist/features/homepage/hooks/useHomepageSections.d.ts +0 -2
- package/dist/features/homepage/hooks/useHomepageSections.js +0 -10
- package/dist/features/homepage/repository/homepage.repository.d.ts +0 -11
- package/dist/features/homepage/repository/homepage.repository.js +0 -25
- package/dist/features/search/actions/index.d.ts +0 -1
- package/dist/features/search/actions/index.js +0 -1
- package/dist/features/search/actions/search-actions.d.ts +0 -18
- package/dist/features/search/actions/search-actions.js +0 -43
- package/dist/features/search/api/route.d.ts +0 -22
- package/dist/features/search/api/route.js +0 -128
- package/dist/features/search/columns/index.d.ts +0 -17
- package/dist/features/search/columns/index.js +0 -49
- package/dist/features/search/components/SearchFiltersRow.d.ts +0 -22
- package/dist/features/search/components/SearchFiltersRow.js +0 -21
- package/dist/features/search/components/SearchResultsSection.d.ts +0 -53
- package/dist/features/search/components/SearchResultsSection.js +0 -35
- package/dist/features/search/components/SearchView.d.ts +0 -27
- package/dist/features/search/components/SearchView.js +0 -8
- package/dist/features/search/hooks/useSearch.d.ts +0 -21
- package/dist/features/search/hooks/useSearch.js +0 -33
- package/dist/features/search/repository/search.repository.d.ts +0 -14
- package/dist/features/search/repository/search.repository.js +0 -59
- package/dist/features/search/schemas/index.d.ts +0 -70
- package/dist/features/search/schemas/index.js +0 -36
- package/dist/features/search/types/index.d.ts +0 -48
- package/dist/features/stores/components/StoreScopedSearch.d.ts +0 -17
- package/dist/features/stores/components/StoreScopedSearch.js +0 -27
- package/dist/seed/defaults/homepage-sections.d.ts +0 -2
- package/dist/seed/defaults/homepage-sections.js +0 -60
- package/dist/seed/factories/homepage-section.factory.d.ts +0 -10
- package/dist/seed/factories/homepage-section.factory.js +0 -13
- /package/dist/{features/search/types/index.js → _internal/shared/listing-types/spares/og.d.ts} +0 -0
|
@@ -2,6 +2,74 @@
|
|
|
2
2
|
* Products Firestore Document Types & Constants
|
|
3
3
|
*/
|
|
4
4
|
import { generateProductId, generateAuctionId, generatePreOrderId, } from "../../../utils/id-generators.js";
|
|
5
|
+
/**
|
|
6
|
+
* Whatever a document, a request body or a seed row happens to hold, as
|
|
7
|
+
* `ProductImage[]`.
|
|
8
|
+
*
|
|
9
|
+
* 🛑 This is called on the WRITE path (`ProductRepository.create`/`update`)
|
|
10
|
+
* **and** on the read path (`mapDoc`), and both are required. Root Cause #101
|
|
11
|
+
* is exactly this shape: `mapDoc` backfilled `categorySlugs` on read while the
|
|
12
|
+
* write schema stripped it, so the field was present on every object anyone
|
|
13
|
+
* inspected and absent from the stored document — invisible to every query,
|
|
14
|
+
* and latent until a human created a listing.
|
|
15
|
+
*
|
|
16
|
+
* Accepts `string`, `ProductImage`, and a mixed array of both, because a
|
|
17
|
+
* partially-migrated collection genuinely contains all three.
|
|
18
|
+
*/
|
|
19
|
+
export function toProductImages(input) {
|
|
20
|
+
if (!Array.isArray(input))
|
|
21
|
+
return [];
|
|
22
|
+
const out = [];
|
|
23
|
+
for (const entry of input) {
|
|
24
|
+
if (typeof entry === "string") {
|
|
25
|
+
if (entry)
|
|
26
|
+
out.push({ url: entry });
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (entry && typeof entry === "object") {
|
|
30
|
+
/*
|
|
31
|
+
* `Partial<ProductImage>`, NOT `{ url?: unknown }` — one narrowing
|
|
32
|
+
* instead of two casts, and no `unknown` in a local binding
|
|
33
|
+
* (`audit-unknown-leakage` flagged the earlier spelling, correctly: an
|
|
34
|
+
* `unknown` here defeats the checking on every member read below it).
|
|
35
|
+
* Every member is still validated before use, so the assertion is
|
|
36
|
+
* narrowing a shape we are about to prove rather than asserting a fact.
|
|
37
|
+
*/
|
|
38
|
+
const e = entry;
|
|
39
|
+
if (typeof e.url !== "string" || !e.url)
|
|
40
|
+
continue;
|
|
41
|
+
/*
|
|
42
|
+
* 🛑 EVERY optional member is carried through, not just the two A6
|
|
43
|
+
* added. This runs on the WRITE path, so a member it forgets is a
|
|
44
|
+
* member it DESTROYS on the next save — and `thumbnailUrl`/`order`
|
|
45
|
+
* predate this function. Adding a field to `ProductImage` means adding
|
|
46
|
+
* it here; the alternative is a normaliser that quietly truncates the
|
|
47
|
+
* type it claims to normalise.
|
|
48
|
+
*/
|
|
49
|
+
out.push({
|
|
50
|
+
url: e.url,
|
|
51
|
+
...(e.alt ? { alt: e.alt } : {}),
|
|
52
|
+
...(e.thumbnailUrl ? { thumbnailUrl: e.thumbnailUrl } : {}),
|
|
53
|
+
...(typeof e.order === "number" ? { order: e.order } : {}),
|
|
54
|
+
...(typeof e.width === "number" ? { width: e.width } : {}),
|
|
55
|
+
...(typeof e.height === "number" ? { height: e.height } : {}),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Just the URLs, in order — for the many call sites that only ever needed a
|
|
63
|
+
* `string[]` (a lightbox's source list, an OG image pick, a media-abort
|
|
64
|
+
* cleanup list).
|
|
65
|
+
*
|
|
66
|
+
* This is a PROJECTION, computed on demand, not a stored mirror — so it cannot
|
|
67
|
+
* drift from `images` the way a denormalised second field would (Root Cause
|
|
68
|
+
* #42). Takes `unknown` so it also works on a legacy `string[]`.
|
|
69
|
+
*/
|
|
70
|
+
export function productImageUrls(input) {
|
|
71
|
+
return toProductImages(input).map((i) => i.url);
|
|
72
|
+
}
|
|
5
73
|
export const MAX_CUSTOM_FIELDS = 50;
|
|
6
74
|
export const MAX_CUSTOM_SECTIONS = 3;
|
|
7
75
|
/** Runtime-accessible product status values — use instead of bare string literals. */
|
|
@@ -171,6 +239,15 @@ export const PRODUCT_PUBLIC_FIELDS = [
|
|
|
171
239
|
"isPromoted",
|
|
172
240
|
"isOnSale",
|
|
173
241
|
"isSold",
|
|
242
|
+
/* Public because the PDP has to SAY "made to order, printed when you order"
|
|
243
|
+
* instead of a stock line, and the group picker has to uncap that member's
|
|
244
|
+
* stepper. Both are client-side reads. */
|
|
245
|
+
"madeToOrder",
|
|
246
|
+
/* Public: provenance a buyer should see — "this is one cut off that sheet" —
|
|
247
|
+
* and neither value is a path or a private id. The sheet document itself is
|
|
248
|
+
* admin-only; this is just its id, same shape as `sourceCatalogueItemId`. */
|
|
249
|
+
"sourceStickerSheetId",
|
|
250
|
+
"sourceStickerPartOrdinal",
|
|
174
251
|
"slug",
|
|
175
252
|
"seoTitle",
|
|
176
253
|
"seoDescription",
|
|
@@ -184,6 +261,14 @@ export const PRODUCT_PUBLIC_FIELDS = [
|
|
|
184
261
|
"groupParentSlug",
|
|
185
262
|
"groupChildSlugs",
|
|
186
263
|
"groupTitle",
|
|
264
|
+
/* The group's own rules. Public because the PICKER enforces them too — a
|
|
265
|
+
* server-only rule reads as a broken button — and none of them is sensitive:
|
|
266
|
+
* they are merchandising limits a buyer is about to be told anyway. */
|
|
267
|
+
"groupMinMembers",
|
|
268
|
+
"groupMaxMembers",
|
|
269
|
+
"groupMaxUnitsPerMember",
|
|
270
|
+
"groupRequiredMemberIds",
|
|
271
|
+
"groupDiscountPercent",
|
|
187
272
|
"createdAt",
|
|
188
273
|
];
|
|
189
274
|
export const PRODUCT_UPDATABLE_FIELDS = [
|
|
@@ -250,6 +335,17 @@ export const PRODUCT_UPDATABLE_FIELDS = [
|
|
|
250
335
|
"preOrderCancellable",
|
|
251
336
|
"isOnSale",
|
|
252
337
|
"isSold",
|
|
338
|
+
/* 🛑 Listed here or an update STRIPS it — the write schema has no
|
|
339
|
+
* `.passthrough()`, so a field absent from this list is dropped before it
|
|
340
|
+
* reaches Firestore while the write still returns 200 (Root Cause #101). The
|
|
341
|
+
* seller checkbox would appear to save and change nothing. */
|
|
342
|
+
"madeToOrder",
|
|
343
|
+
/* Updatable so `promoteStickerSheetToListings` can set them through the same
|
|
344
|
+
* `create`/`update` path every other write uses — and so a re-promote can
|
|
345
|
+
* correct them — rather than needing a raw collection write that would also
|
|
346
|
+
* skip `deriveTaxonomy`. */
|
|
347
|
+
"sourceStickerSheetId",
|
|
348
|
+
"sourceStickerPartOrdinal",
|
|
253
349
|
"allowShipBeforeEmiComplete",
|
|
254
350
|
/*
|
|
255
351
|
* B3 — these three were MISSING, so the fields existed on the document
|
|
@@ -272,6 +368,14 @@ export const PRODUCT_UPDATABLE_FIELDS = [
|
|
|
272
368
|
"groupParentSlug",
|
|
273
369
|
"groupChildSlugs",
|
|
274
370
|
"groupTitle",
|
|
371
|
+
/* The group's own rules. Public because the PICKER enforces them too — a
|
|
372
|
+
* server-only rule reads as a broken button — and none of them is sensitive:
|
|
373
|
+
* they are merchandising limits a buyer is about to be told anyway. */
|
|
374
|
+
"groupMinMembers",
|
|
375
|
+
"groupMaxMembers",
|
|
376
|
+
"groupMaxUnitsPerMember",
|
|
377
|
+
"groupRequiredMemberIds",
|
|
378
|
+
"groupDiscountPercent",
|
|
275
379
|
"barcodeId",
|
|
276
380
|
];
|
|
277
381
|
// ── SB-UNI-N 2026-05-15 — Digital-code pool subcollection ───────────────────
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./firestore";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import type { ListingType } from "../types/index";
|
|
3
4
|
export declare const productImageSchema: z.ZodObject<{
|
|
4
5
|
url: z.ZodString;
|
|
5
6
|
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
@@ -46,19 +47,6 @@ export declare const printMetaSchema: z.ZodObject<{
|
|
|
46
47
|
finish?: string | undefined;
|
|
47
48
|
editionSize?: number | undefined;
|
|
48
49
|
}>;
|
|
49
|
-
/**
|
|
50
|
-
* Base Zod schema for a product item.
|
|
51
|
-
* Apps can extend this to add their own fields:
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* import { productItemSchema } from "@mohasinac/feat-products";
|
|
55
|
-
*
|
|
56
|
-
* const myProductSchema = productItemSchema.extend({
|
|
57
|
-
* brand: z.string(),
|
|
58
|
-
* auctionEndDate: z.string().optional(),
|
|
59
|
-
* });
|
|
60
|
-
* type MyProduct = z.infer<typeof myProductSchema>;
|
|
61
|
-
*/
|
|
62
50
|
export declare const productItemSchema: z.ZodObject<{
|
|
63
51
|
id: z.ZodString;
|
|
64
52
|
title: z.ZodString;
|
|
@@ -130,7 +118,8 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
130
118
|
sellerAvatar: z.ZodOptional<z.ZodString>;
|
|
131
119
|
status: z.ZodEnum<["draft", "published", "in_review", "archived"]>;
|
|
132
120
|
condition: z.ZodOptional<z.ZodEnum<["new", "like_new", "good", "fair", "poor", "used", "refurbished", "broken", "graded"]>>;
|
|
133
|
-
|
|
121
|
+
madeToOrder: z.ZodOptional<z.ZodBoolean>;
|
|
122
|
+
listingType: z.ZodOptional<z.ZodEnum<[ListingType, ...ListingType[]]>>;
|
|
134
123
|
maxPerUser: z.ZodOptional<z.ZodNumber>;
|
|
135
124
|
partOfBundleIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
136
125
|
partOfBundleTitles: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
@@ -337,7 +326,7 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
337
326
|
} | undefined;
|
|
338
327
|
createdAt?: string | undefined;
|
|
339
328
|
updatedAt?: string | undefined;
|
|
340
|
-
listingType?:
|
|
329
|
+
listingType?: ListingType | undefined;
|
|
341
330
|
category?: string | undefined;
|
|
342
331
|
categorySlug?: string | undefined;
|
|
343
332
|
storeId?: string | undefined;
|
|
@@ -359,6 +348,7 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
359
348
|
description?: string | undefined;
|
|
360
349
|
subcategory?: string | undefined;
|
|
361
350
|
availableQuantity?: number | undefined;
|
|
351
|
+
madeToOrder?: boolean | undefined;
|
|
362
352
|
mainImage?: string | undefined;
|
|
363
353
|
images?: string[] | undefined;
|
|
364
354
|
featured?: boolean | undefined;
|
|
@@ -476,7 +466,7 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
476
466
|
} | undefined;
|
|
477
467
|
createdAt?: string | undefined;
|
|
478
468
|
updatedAt?: string | undefined;
|
|
479
|
-
listingType?:
|
|
469
|
+
listingType?: ListingType | undefined;
|
|
480
470
|
category?: string | undefined;
|
|
481
471
|
categorySlug?: string | undefined;
|
|
482
472
|
storeId?: string | undefined;
|
|
@@ -498,6 +488,7 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
498
488
|
description?: string | undefined;
|
|
499
489
|
subcategory?: string | undefined;
|
|
500
490
|
availableQuantity?: number | undefined;
|
|
491
|
+
madeToOrder?: boolean | undefined;
|
|
501
492
|
mainImage?: string | undefined;
|
|
502
493
|
images?: string[] | undefined;
|
|
503
494
|
featured?: boolean | undefined;
|
|
@@ -612,7 +603,7 @@ export declare const productListParamsSchema: z.ZodObject<{
|
|
|
612
603
|
minPrice: z.ZodOptional<z.ZodNumber>;
|
|
613
604
|
maxPrice: z.ZodOptional<z.ZodNumber>;
|
|
614
605
|
inStock: z.ZodOptional<z.ZodBoolean>;
|
|
615
|
-
listingType: z.ZodOptional<z.ZodEnum<[
|
|
606
|
+
listingType: z.ZodOptional<z.ZodEnum<[ListingType, ...ListingType[]]>>;
|
|
616
607
|
storeId: z.ZodOptional<z.ZodString>;
|
|
617
608
|
sort: z.ZodOptional<z.ZodString>;
|
|
618
609
|
page: z.ZodOptional<z.ZodNumber>;
|
|
@@ -623,7 +614,7 @@ export declare const productListParamsSchema: z.ZodObject<{
|
|
|
623
614
|
status?: "published" | "draft" | "in_review" | "archived" | undefined;
|
|
624
615
|
page?: number | undefined;
|
|
625
616
|
q?: string | undefined;
|
|
626
|
-
listingType?:
|
|
617
|
+
listingType?: ListingType | undefined;
|
|
627
618
|
category?: string | undefined;
|
|
628
619
|
storeId?: string | undefined;
|
|
629
620
|
featured?: boolean | undefined;
|
|
@@ -637,7 +628,7 @@ export declare const productListParamsSchema: z.ZodObject<{
|
|
|
637
628
|
status?: "published" | "draft" | "in_review" | "archived" | undefined;
|
|
638
629
|
page?: number | undefined;
|
|
639
630
|
q?: string | undefined;
|
|
640
|
-
listingType?:
|
|
631
|
+
listingType?: ListingType | undefined;
|
|
641
632
|
category?: string | undefined;
|
|
642
633
|
storeId?: string | undefined;
|
|
643
634
|
featured?: boolean | undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from "./firestore.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { mediaFieldSchema } from "../../media/types/index.js";
|
|
4
|
+
import { ALL_LISTING_TYPES } from "../../../_internal/shared/listing-types/feature-flags.js";
|
|
4
5
|
import { PRIZE_DRAW_DURATION_DAYS_MIN, PRIZE_DRAW_DURATION_DAYS_MAX, } from "../../../_internal/shared/checkout/rules/_limits.js";
|
|
5
6
|
// --- Sub-schemas --------------------------------------------------------------
|
|
6
7
|
export const productImageSchema = z.object({
|
|
@@ -35,6 +36,25 @@ export const printMetaSchema = z.object({
|
|
|
35
36
|
* });
|
|
36
37
|
* type MyProduct = z.infer<typeof myProductSchema>;
|
|
37
38
|
*/
|
|
39
|
+
/**
|
|
40
|
+
* The `ListingType` union as a Zod enum, DERIVED.
|
|
41
|
+
*
|
|
42
|
+
* 🛑 Two hand-written copies of this lived in this file and BOTH had drifted to
|
|
43
|
+
* seven of nine — `art` and `stickers` were missing from the item schema and
|
|
44
|
+
* from the query schema. Consequence: `productItemSchema.parse()` stripped
|
|
45
|
+
* `listingType` off every art and sticker row (the field is `.optional()`, so a
|
|
46
|
+
* value outside the enum is a parse FAILURE, and a caller using `safeParse` or
|
|
47
|
+
* `.partial()` loses it silently), and `?listingType=art` on any route
|
|
48
|
+
* validating against the query schema was rejected as an invalid value.
|
|
49
|
+
*
|
|
50
|
+
* `z.enum` needs a non-empty literal tuple, hence the cast — `ALL_LISTING_TYPES`
|
|
51
|
+
* is derived from `Record<ListingType, true>`, so its CONTENTS are
|
|
52
|
+
* compile-checked against the union even though its length is not.
|
|
53
|
+
*
|
|
54
|
+
* Registered in `audit-listing-type-tab-coverage`'s STAYS_DERIVED so nobody
|
|
55
|
+
* re-expands it into a literal for readability.
|
|
56
|
+
*/
|
|
57
|
+
const LISTING_TYPE_ENUM = z.enum(ALL_LISTING_TYPES);
|
|
38
58
|
export const productItemSchema = z.object({
|
|
39
59
|
id: z.string(),
|
|
40
60
|
title: z.string(),
|
|
@@ -78,9 +98,8 @@ export const productItemSchema = z.object({
|
|
|
78
98
|
])
|
|
79
99
|
.optional(),
|
|
80
100
|
// SB1-G Phase 4 — canonical discriminator (legacy `isAuction`/`isPreOrder` booleans removed).
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.optional(),
|
|
101
|
+
madeToOrder: z.boolean().optional(),
|
|
102
|
+
listingType: LISTING_TYPE_ENUM.optional(),
|
|
84
103
|
// SB1-C — prize-draw + bundle fields. All optional.
|
|
85
104
|
maxPerUser: z.number().int().positive().optional(),
|
|
86
105
|
partOfBundleIds: z.array(z.string()).optional(),
|
|
@@ -229,9 +248,7 @@ export const productListParamsSchema = z.object({
|
|
|
229
248
|
minPrice: z.coerce.number().optional(),
|
|
230
249
|
maxPrice: z.coerce.number().optional(),
|
|
231
250
|
inStock: z.coerce.boolean().optional(),
|
|
232
|
-
listingType:
|
|
233
|
-
.enum(["standard", "auction", "pre-order", "prize-draw", "classified", "digital-code", "live"])
|
|
234
|
-
.optional(),
|
|
251
|
+
listingType: LISTING_TYPE_ENUM.optional(),
|
|
235
252
|
storeId: z.string().optional(),
|
|
236
253
|
sort: z.string().optional(),
|
|
237
254
|
page: z.coerce.number().optional(),
|
|
@@ -11,12 +11,34 @@ export type ProductCondition = "new" | "like_new" | "good" | "fair" | "poor" | "
|
|
|
11
11
|
* accepts legacy alias tokens (`preorder` → `pre-order`, `product` → `standard`)
|
|
12
12
|
* but stored values are always one of these five.
|
|
13
13
|
*/
|
|
14
|
-
export type ListingType = "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | "art" | "stickers";
|
|
14
|
+
export type ListingType = "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | "art" | "stickers" | "spares";
|
|
15
|
+
/**
|
|
16
|
+
* One gallery image — THE single definition, used by both the Firestore
|
|
17
|
+
* document and the client shape.
|
|
18
|
+
*
|
|
19
|
+
* 🛑 This interface already existed here, barrel-exported as public API, with
|
|
20
|
+
* ZERO consumers anywhere and `Product.images` declared `string[]` beside it:
|
|
21
|
+
* a speculative type describing a shape nothing produced or read. Plan A6
|
|
22
|
+
* resurrected it rather than declaring a second `ProductImage` in
|
|
23
|
+
* `schemas/firestore.ts`, which is what the first draft did — two same-named
|
|
24
|
+
* types in one package is a defect even when both look plausible, and the
|
|
25
|
+
* barrel's choice between them is invisible at every call site (Root Cause
|
|
26
|
+
* #36). `schemas/firestore.ts` imports this one.
|
|
27
|
+
*
|
|
28
|
+
* `alt` is the point: measured across the fifteen-site competitor crawl,
|
|
29
|
+
* per-image alt coverage is ~0% (0/151, 0/306, 0/317; tcgrepublic's 37/63 is
|
|
30
|
+
* the best anyone manages). `width`/`height` let `next/image` reserve space so
|
|
31
|
+
* a gallery does not reflow as each image lands — optional because we do not
|
|
32
|
+
* always know them at write time, and a guessed aspect ratio is worse than
|
|
33
|
+
* none.
|
|
34
|
+
*/
|
|
15
35
|
export interface ProductImage {
|
|
16
36
|
url: string;
|
|
17
37
|
thumbnailUrl?: string;
|
|
18
38
|
alt?: string;
|
|
19
39
|
order?: number;
|
|
40
|
+
width?: number;
|
|
41
|
+
height?: number;
|
|
20
42
|
}
|
|
21
43
|
export interface ProductSeo {
|
|
22
44
|
title?: string;
|
|
@@ -78,6 +100,28 @@ export interface ProductItem {
|
|
|
78
100
|
sublistingIcon?: string;
|
|
79
101
|
isOnSale?: boolean;
|
|
80
102
|
isSold?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Printed or built when ordered, so stock does not gate it. Per-LISTING, not
|
|
105
|
+
* implied by `listingType` — a seller can hold a finite run of pre-cut sheets
|
|
106
|
+
* and also offer a print-on-demand one.
|
|
107
|
+
*
|
|
108
|
+
* On the client this drives three things: the PDP says "made to order"
|
|
109
|
+
* instead of a stock line, the group picker uncaps that member's stepper, and
|
|
110
|
+
* `baseAvailable` keeps the row in the default scope at quantity 0.
|
|
111
|
+
*/
|
|
112
|
+
madeToOrder?: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Self-assembled-selection rules, present only on a group PARENT. See
|
|
115
|
+
* `_internal/shared/features/products/group-rules.ts` for the shape, the
|
|
116
|
+
* resolver and the single enforcement point — these fields exist here only
|
|
117
|
+
* so the admin editor (`GroupSettingsPanel`'s `groupRules` prop) can seed its
|
|
118
|
+
* form from the product it is already showing, with no second fetch.
|
|
119
|
+
*/
|
|
120
|
+
groupMinMembers?: number;
|
|
121
|
+
groupMaxMembers?: number;
|
|
122
|
+
groupMaxUnitsPerMember?: number;
|
|
123
|
+
groupRequiredMemberIds?: string[];
|
|
124
|
+
groupDiscountPercent?: number;
|
|
81
125
|
inStock?: boolean;
|
|
82
126
|
stockCount?: number;
|
|
83
127
|
rating?: number;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ProductImage } from "../schemas/firestore";
|
|
2
|
+
/**
|
|
3
|
+
* The thumbnail for one product-group member, from whichever shape the caller
|
|
4
|
+
* supplied.
|
|
5
|
+
*
|
|
6
|
+
* ── 🛑 WHY `image` IS TRIED FIRST ──────────────────────────────────────────
|
|
7
|
+
*
|
|
8
|
+
* `toPublicGroupMember` is an ALLOW-LIST projection: it emits the singular
|
|
9
|
+
* `image` (`images[0].url ?? mainImage`) and no `images[]` array at all. So a
|
|
10
|
+
* component reading only `images[0]` resolves `undefined` for every member
|
|
11
|
+
* served by the public endpoint and renders a placeholder tile.
|
|
12
|
+
*
|
|
13
|
+
* That was live on every product-group page — all seven BX-53 members showed
|
|
14
|
+
* a grey box while their images sat in Storage serving HTTP 200. Nothing
|
|
15
|
+
* errors, because an empty `src` is a legal `MediaImage`.
|
|
16
|
+
*
|
|
17
|
+
* The `images[0]` fallback stays for callers that pass a raw document rather
|
|
18
|
+
* than the projection.
|
|
19
|
+
*
|
|
20
|
+
* ── 🛑 WHY ITS OWN MODULE, AND NOT EITHER COMPONENT ────────────────────────
|
|
21
|
+
*
|
|
22
|
+
* `ShowGroupSection` renders `GroupMemberPicker`, so the import edge between
|
|
23
|
+
* them already runs one way. Putting the shared helper in either file and
|
|
24
|
+
* importing it from the other closes a CYCLE — which is exactly what
|
|
25
|
+
* `audit-import-cycles` caught when the first version of this fix exported it
|
|
26
|
+
* from `ShowGroupSection`.
|
|
27
|
+
*
|
|
28
|
+
* A helper used by two components belongs beside neither (Root Cause #18:
|
|
29
|
+
* import from the module that DEFINES the symbol). One definition, two
|
|
30
|
+
* importers, no edge between them — and one rule reachable by copy-paste is
|
|
31
|
+
* one that gets copied wrong (Root Cause #75), which is how the two spellings
|
|
32
|
+
* diverged in the first place.
|
|
33
|
+
*/
|
|
34
|
+
export interface GroupMemberImageSource {
|
|
35
|
+
/** The public projection's single thumbnail. */
|
|
36
|
+
image?: string;
|
|
37
|
+
/**
|
|
38
|
+
* A raw document's gallery. Only read when `image` is absent.
|
|
39
|
+
*
|
|
40
|
+
* 🛑 `ProductImage[]` since A6 — a raw document holds objects, a legacy one
|
|
41
|
+
* holds strings, and the public projection holds neither. `productImageUrls`
|
|
42
|
+
* is the one normaliser that spans all three.
|
|
43
|
+
*/
|
|
44
|
+
images?: readonly (string | ProductImage)[];
|
|
45
|
+
}
|
|
46
|
+
export declare function resolveGroupMemberImage(m: GroupMemberImageSource): string;
|
|
@@ -15,6 +15,10 @@ import { isListingType } from "./listing-type.js";
|
|
|
15
15
|
*/
|
|
16
16
|
export const LISTING_BADGE_VARIANT = {
|
|
17
17
|
standard: "default",
|
|
18
|
+
// Shares `primary` with art/stickers: all three are "a thing we make or
|
|
19
|
+
// salvage ourselves" rather than a mechanism (auction) or a promise
|
|
20
|
+
// (pre-order), and the badge LABEL is what distinguishes them.
|
|
21
|
+
spares: "primary",
|
|
18
22
|
auction: "warning",
|
|
19
23
|
"pre-order": "secondary",
|
|
20
24
|
"prize-draw": "primary",
|
|
@@ -29,7 +29,7 @@ export function ReviewCard({ review, context = "general", className = "" }) {
|
|
|
29
29
|
const showProductLink = context !== "listing" && !!productHref;
|
|
30
30
|
const showProfileLink = !!profileHref;
|
|
31
31
|
const hasFooter = showStoreLink || showProductLink || showProfileLink;
|
|
32
|
-
return (_jsxs(Stack, { padding: "5", shadow: "hover-md", className: `group h-full border border-neutral-200 transition ${className}`, rounded: "xl", surface: "default", children: [_jsxs(Link, { href: reviewHref, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Row, { align: "start", gap: "3", children: [review.userAvatar ? (_jsx(Div, { className: "relative h-9 w-9 flex-shrink-0", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: review.userAvatar, alt: displayName, size: "avatar" }) })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-9 w-9 flex-shrink-0 bg-neutral-200 text-[var(--appkit-color-text-muted)]", align: "center", justify: "center", rounded: "full", children: initials })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Span, { color: "inverse", weight: "medium", className: "text-[var(--appkit-color-text)] dark:", children: displayName }), review.verified && (_jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "medium", rounded: "full", padding: "pill-xs", children: "Verified" })), date && (_jsx(Span, { size: "xs", color: "muted", children: date }))] }), _jsx(Div, { className: "mt-1", children: _jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }) })] })] }), review.title && (_jsx(Heading, { color: "inverse", level: 4, className: "mt-3 text-[var(--appkit-color-text)] dark:", weight: "semibold", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "mt-2 text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mt-3", children: review.images.map((img, i) => (_jsx(Div, { className: "relative h-16 w-16 flex-shrink-0 border border-neutral-100", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: img, alt: `Review image ${i + 1}`, size: "thumbnail" }) }, i))) })), (review.helpfulCount ?? 0) > 0 && (_jsxs(Text, { className: "mt-3", color: "faint", size: "xs", children: [review.helpfulCount, " found this helpful"] }))] }), hasFooter && (_jsxs(Stack, { gap: "xs", className: "mt-3 border-t border-neutral-100", padding: "t-sm", children: [showStoreLink && (_jsxs(Link, { href: String(ROUTES.PUBLIC.STORE_DETAIL(review.storeSlug)), className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-primary hover:underline", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83C\uDFEA" }), _jsx(Span, { truncate: true, children: review.storeName })] })), showProductLink && (_jsxs(Link, { href: productHref, className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)]
|
|
32
|
+
return (_jsxs(Stack, { padding: "5", shadow: "hover-md", className: `group h-full border border-neutral-200 transition ${className}`, rounded: "xl", surface: "default", children: [_jsxs(Link, { href: reviewHref, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Row, { align: "start", gap: "3", children: [review.userAvatar ? (_jsx(Div, { className: "relative h-9 w-9 flex-shrink-0", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: review.userAvatar, alt: displayName, size: "avatar" }) })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-9 w-9 flex-shrink-0 bg-neutral-200 text-[var(--appkit-color-text-muted)]", align: "center", justify: "center", rounded: "full", children: initials })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Span, { color: "inverse", weight: "medium", className: "text-[var(--appkit-color-text)] dark:", children: displayName }), review.verified && (_jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "medium", rounded: "full", padding: "pill-xs", children: "Verified" })), date && (_jsx(Span, { size: "xs", color: "muted", children: date }))] }), _jsx(Div, { className: "mt-1", children: _jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }) })] })] }), review.title && (_jsx(Heading, { color: "inverse", level: 4, className: "mt-3 text-[var(--appkit-color-text)] dark:", weight: "semibold", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "mt-2 text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mt-3", children: review.images.map((img, i) => (_jsx(Div, { className: "relative h-16 w-16 flex-shrink-0 border border-neutral-100", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: img, alt: `Review image ${i + 1}`, size: "thumbnail" }) }, i))) })), (review.helpfulCount ?? 0) > 0 && (_jsxs(Text, { className: "mt-3", color: "faint", size: "xs", children: [review.helpfulCount, " found this helpful"] }))] }), hasFooter && (_jsxs(Stack, { gap: "xs", className: "mt-3 border-t border-neutral-100", padding: "t-sm", children: [showStoreLink && (_jsxs(Link, { href: String(ROUTES.PUBLIC.STORE_DETAIL(review.storeSlug)), className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-primary hover:underline", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83C\uDFEA" }), _jsx(Span, { truncate: true, children: review.storeName })] })), showProductLink && (_jsxs(Link, { href: productHref, className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), _jsx(Span, { truncate: true, children: review.productTitle ?? "View Product" }), _jsx(Span, { "aria-hidden": "true", className: "ml-auto text-primary group-hover:translate-x-0.5 transition-transform", children: "\u2192" })] })), showProfileLink && (_jsxs(Link, { href: profileHref, className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDC64" }), _jsx(Span, { truncate: true, children: displayName })] }))] }))] }));
|
|
33
33
|
}
|
|
34
34
|
export function ReviewsList({ reviews, context = "general", isLoading, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No reviews yet", }) {
|
|
35
35
|
if (isLoading) {
|
|
@@ -51,7 +51,7 @@ export function ScamProfileView({ scammer, isAuthenticated, incidents = [], comm
|
|
|
51
51
|
const registryHref = String(ROUTES.PUBLIC.SCAMS);
|
|
52
52
|
const contestLoginHref = `${String(ROUTES.AUTH.LOGIN)}?redirect=${encodeURIComponent(`/scams/${scammer.id}`)}`;
|
|
53
53
|
const scamTypeDef = getScamType(scammer.scamType);
|
|
54
|
-
return (_jsxs(Main, { children: [_jsx(Div, { className: "border-b
|
|
54
|
+
return (_jsxs(Main, { children: [_jsx(Div, { className: "border-b", children: _jsx(Container, { size: "xl", padding: "y-sm", children: _jsx(Breadcrumb, { items: [
|
|
55
55
|
{ label: "Home", href: "/" },
|
|
56
56
|
{ label: "Scam Registry", href: registryHref },
|
|
57
57
|
{ label: scammer.displayNames[0] ?? "Profile" },
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface EntityTypeaheadPick {
|
|
3
|
+
/** The canonical entity's title, as the index holds it. */
|
|
4
|
+
readonly title: string;
|
|
5
|
+
/** Its destination, which encodes the slug. */
|
|
6
|
+
readonly href: string;
|
|
7
|
+
/** The index type it came from. */
|
|
8
|
+
readonly typeKey: string;
|
|
9
|
+
/**
|
|
10
|
+
* 🛑 What the USER typed, verbatim. See the header — a consumer that
|
|
11
|
+
* analyses the resolved title instead of this is throwing away the signal
|
|
12
|
+
* it was built to detect.
|
|
13
|
+
*/
|
|
14
|
+
readonly rawQuery: string;
|
|
15
|
+
}
|
|
16
|
+
export interface EntityTypeaheadProps {
|
|
17
|
+
/** Index type keys to search. `["model"]`, `["category"]`, `["part"]`… */
|
|
18
|
+
types: readonly string[];
|
|
19
|
+
onPick: (pick: EntityTypeaheadPick) => void;
|
|
20
|
+
/** `siteSettings.search?.indexEnabled`. Read as `!== false`. */
|
|
21
|
+
indexEnabled?: boolean;
|
|
22
|
+
label: string;
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
/** Mirrors the field the consumer owns, so the two cannot disagree. */
|
|
25
|
+
value?: string;
|
|
26
|
+
onChange?: (value: string) => void;
|
|
27
|
+
limit?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Default `true`: picking a suggestion fills the field with the
|
|
30
|
+
* canonical title, as every ordinary typeahead does.
|
|
31
|
+
*
|
|
32
|
+
* 🛑 Set `false` when the consumer's DOWNSTREAM LOGIC treats a mismatch
|
|
33
|
+
* between what was typed and the canonical form as a signal rather than
|
|
34
|
+
* noise — `/tools/authenticity` is the one caller that needs this. Its
|
|
35
|
+
* engine fires a trademark-evasion warning on a FUZZY match, and that
|
|
36
|
+
* warning is exactly what filling the field would silence: the engine
|
|
37
|
+
* would receive "Storm Pegasus" and match it exactly, never learning the
|
|
38
|
+
* user typed "Pegasis" at all. With this false, a pick still calls
|
|
39
|
+
* `onPick` (with `rawQuery` intact) but leaves the field exactly as
|
|
40
|
+
* typed — discovery without consequence.
|
|
41
|
+
*/
|
|
42
|
+
fillOnPick?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare function EntityTypeahead({ types, onPick, indexEnabled, label, placeholder, value, onChange, limit, fillOnPick, }: EntityTypeaheadProps): React.JSX.Element;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/*
|
|
4
|
+
* WHY: Three tools need "turn what the user typed into a canonical entity",
|
|
5
|
+
* and each wants a different slice. `resolveEntity`'s `types` parameter
|
|
6
|
+
* exists for exactly this; this is its UI.
|
|
7
|
+
* WHAT: A generic index-backed typeahead. Offers canonical names, reports
|
|
8
|
+
* BOTH the chosen entity and the raw string the user typed.
|
|
9
|
+
*
|
|
10
|
+
* ## 🛑 `onPick` hands back the RAW query as well as the entity
|
|
11
|
+
*
|
|
12
|
+
* This is the one thing about the component that is not a convenience.
|
|
13
|
+
*
|
|
14
|
+
* `/tools/authenticity` treats a fuzzy name match as EVIDENCE — a near-miss
|
|
15
|
+
* fires a trademark-evasion warning, and its own source records the case:
|
|
16
|
+
* "`Pegasis` … resolves as an `alias` hit, and the evasion-spelling warning
|
|
17
|
+
* (which only fires on a `fuzzy` hit) stays silent." Meanwhile this
|
|
18
|
+
* component's matcher derives its alias table from a crawl of a counterfeit
|
|
19
|
+
* seller's 1,277 titles, on the stated insight that "a counterfeiter's
|
|
20
|
+
* evasion spellings are the same strings a real buyer mistypes."
|
|
21
|
+
*
|
|
22
|
+
* Both systems consume the same corpus with OPPOSITE intent: search treats a
|
|
23
|
+
* counterfeiter's spelling as something to match through, authenticity
|
|
24
|
+
* treats it as the signal. So if someone types `Pegasis`, picks "Storm
|
|
25
|
+
* Pegasus" from this dropdown, and the consumer forwards only the resolved
|
|
26
|
+
* slug, the engine never sees `Pegasis` and the evasion warning is silenced
|
|
27
|
+
* BY CONSTRUCTION — the tool would confirm a fake using the exact spelling
|
|
28
|
+
* that indicates one.
|
|
29
|
+
*
|
|
30
|
+
* Hence: this component PROPOSES an entity; the consumer must keep passing
|
|
31
|
+
* what the user actually typed. Enforced from the other side by
|
|
32
|
+
* `audit-entity-resolver-consumers` R2, which forbids any module under
|
|
33
|
+
* `features/authenticity/` from importing the matcher at all — which is why
|
|
34
|
+
* this lives here and reaches that tool as a slot.
|
|
35
|
+
*
|
|
36
|
+
* @tag domain:search
|
|
37
|
+
* @tag layer:feature
|
|
38
|
+
* @tag pattern:none
|
|
39
|
+
* @tag access:client
|
|
40
|
+
* @tag consumers:/tools/authenticity,/tools/price-index
|
|
41
|
+
* @tag sideEffects:network
|
|
42
|
+
*/
|
|
43
|
+
import React from "react";
|
|
44
|
+
import { Button, Div, Input, Stack, Text } from "../../../ui/index.js";
|
|
45
|
+
import { useSiteSearch } from "../hooks/useSiteSearch.js";
|
|
46
|
+
/** Stable identity — a `useMemo` dependency inside the hook. */
|
|
47
|
+
const passthroughLabel = (typeKey) => typeKey;
|
|
48
|
+
export function EntityTypeahead({ types, onPick, indexEnabled, label, placeholder, value, onChange, limit = 6, fillOnPick = true, }) {
|
|
49
|
+
const controlled = value !== undefined;
|
|
50
|
+
const [internal, setInternal] = React.useState("");
|
|
51
|
+
const query = controlled ? value : internal;
|
|
52
|
+
const [active, setActive] = React.useState(false);
|
|
53
|
+
const [dismissed, setDismissed] = React.useState(false);
|
|
54
|
+
const { status, result } = useSiteSearch(query, {
|
|
55
|
+
active,
|
|
56
|
+
indexEnabled,
|
|
57
|
+
types,
|
|
58
|
+
label: passthroughLabel,
|
|
59
|
+
});
|
|
60
|
+
const hits = result?.groups.flatMap((g) => g.hits) ?? [];
|
|
61
|
+
const show = !dismissed && status === "ready" && hits.length > 0 && query.trim().length > 0;
|
|
62
|
+
const setQuery = (next) => {
|
|
63
|
+
setDismissed(false);
|
|
64
|
+
if (controlled)
|
|
65
|
+
onChange?.(next);
|
|
66
|
+
else
|
|
67
|
+
setInternal(next);
|
|
68
|
+
};
|
|
69
|
+
return (_jsxs(Stack, { gap: "xs", className: "min-w-0", children: [_jsx(Input, { type: "search", value: query, "aria-label": label, placeholder: placeholder, onFocus: () => setActive(true), onChange: (event) => {
|
|
70
|
+
setActive(true);
|
|
71
|
+
setQuery(event.target.value);
|
|
72
|
+
} }), show && (_jsxs(Stack, { gap: "xs", className: "min-w-0", children: [_jsx(Text, { size: "xs", color: "muted", children: "Did you mean one of these?" }), hits.slice(0, limit).map((hit) => (_jsxs(Div, { surface: "card", padding: "xs", rounded: "md", border: "subtle", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
73
|
+
/*
|
|
74
|
+
* 🛑 `rawQuery: query` — BEFORE the field is overwritten
|
|
75
|
+
* with the canonical title. Reading it after would hand the
|
|
76
|
+
* consumer the canonical name as "what the user typed",
|
|
77
|
+
* which is the silent version of the bug this whole
|
|
78
|
+
* component's header is about.
|
|
79
|
+
*/
|
|
80
|
+
onPick({
|
|
81
|
+
title: hit.title,
|
|
82
|
+
href: hit.href,
|
|
83
|
+
typeKey: result?.groups.find((g) => g.hits.includes(hit))?.key ?? "",
|
|
84
|
+
rawQuery: query,
|
|
85
|
+
});
|
|
86
|
+
// See `fillOnPick`'s 🛑 — skipping this is the whole
|
|
87
|
+
// reason the prop exists, not an inconsistency.
|
|
88
|
+
if (fillOnPick)
|
|
89
|
+
setQuery(hit.title);
|
|
90
|
+
setDismissed(true);
|
|
91
|
+
}, children: hit.title }), hit.detail && (_jsx(Text, { size: "xs", color: "muted", children: hit.detail }))] }, hit.id)))] }))] }));
|
|
92
|
+
}
|
|
@@ -47,5 +47,16 @@ export interface SearchProps {
|
|
|
47
47
|
resourceTypes?: SearchResourceTypeOption[];
|
|
48
48
|
defaultResourceType?: SearchResourceType;
|
|
49
49
|
storageKey?: string;
|
|
50
|
+
/**
|
|
51
|
+
* `siteSettings.search?.indexEnabled`. Read as `!== false`, so an absent
|
|
52
|
+
* settings object leaves the local index in play.
|
|
53
|
+
*
|
|
54
|
+
* When the index answers, the typeahead spends ZERO network requests per
|
|
55
|
+
* keystroke and spans all 21 indexed types instead of the four
|
|
56
|
+
* `/api/search/suggestions` can produce. When it is off or has failed, the
|
|
57
|
+
* component falls back to that endpoint unchanged — which is why
|
|
58
|
+
* `useNavSuggestions` is still called rather than deleted.
|
|
59
|
+
*/
|
|
60
|
+
searchIndexEnabled?: boolean;
|
|
50
61
|
}
|
|
51
|
-
export declare function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, placeholder, deferred, debounceMs, onClear, className, router, quickLinks, labels, suggestionTypeIcons, suggestionTypeBadges, resourceTypes, defaultResourceType, storageKey, }: SearchProps): React.JSX.Element | null;
|
|
62
|
+
export declare function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, placeholder, deferred, debounceMs, onClear, className, router, quickLinks, labels, suggestionTypeIcons, suggestionTypeBadges, resourceTypes, defaultResourceType, storageKey, searchIndexEnabled, }: SearchProps): React.JSX.Element | null;
|