@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
|
@@ -273,6 +273,15 @@ export declare const CATEGORY_ENDPOINTS: {
|
|
|
273
273
|
* category change in the form costs zero function invocations on a hit.
|
|
274
274
|
*/
|
|
275
275
|
readonly DESCRIPTION_TEMPLATE: (id: string, variant?: string) => string;
|
|
276
|
+
/**
|
|
277
|
+
* The inherited `priceGuidance` band, restated on the calling seller's own
|
|
278
|
+
* tax basis — seller/admin/moderator only. Deliberately a SEPARATE
|
|
279
|
+
* endpoint from `DESCRIPTION_TEMPLATE`: that one is public and edge-cached
|
|
280
|
+
* so anyone can fetch it, and `priceGuidance` is withheld from that
|
|
281
|
+
* response for the same reason a listing's own price is hidden from guests
|
|
282
|
+
* (CLAUDE.md § "Guest price gating").
|
|
283
|
+
*/
|
|
284
|
+
readonly PRICE_GUIDANCE: (id: string) => string;
|
|
276
285
|
};
|
|
277
286
|
export declare const TOOL_ENDPOINTS: {
|
|
278
287
|
/**
|
|
@@ -286,6 +295,19 @@ export declare const TOOL_ENDPOINTS: {
|
|
|
286
295
|
* its invocation allowance.
|
|
287
296
|
*/
|
|
288
297
|
readonly PRICE_INDEX_SEARCH: (q: string) => string;
|
|
298
|
+
/**
|
|
299
|
+
* Tier 1 adoption — the client resolved these ids LOCALLY, from the
|
|
300
|
+
* public search artifact, at zero Firestore reads. This route's only job
|
|
301
|
+
* is the multi-get `searchPriceIndex` otherwise pays for every lookup.
|
|
302
|
+
*/
|
|
303
|
+
readonly PRICE_INDEX_BANDS: (ids: readonly string[]) => string;
|
|
304
|
+
/**
|
|
305
|
+
* The competitor-crawl market band for the same leaves — SIGNED-IN ONLY, and
|
|
306
|
+
* therefore a separate request from `PRICE_INDEX_BANDS` rather than more
|
|
307
|
+
* fields on it. That route is public and edge-cached; this one must not be,
|
|
308
|
+
* or one signed-in lookup populates an entry every guest reads.
|
|
309
|
+
*/
|
|
310
|
+
readonly PRICE_INDEX_MARKET: (ids: readonly string[]) => string;
|
|
289
311
|
};
|
|
290
312
|
/**
|
|
291
313
|
* The sticker-sheet kiss-cut tracer. Admin-only, every route behind
|
|
@@ -377,8 +399,6 @@ export declare const FAQ_ENDPOINTS: {
|
|
|
377
399
|
readonly VOTE: "/api/faqs/vote";
|
|
378
400
|
};
|
|
379
401
|
export declare const HOMEPAGE_ENDPOINTS: {
|
|
380
|
-
readonly GET: "/api/homepage";
|
|
381
|
-
readonly SECTIONS: "/api/homepage-sections";
|
|
382
402
|
readonly CAROUSEL: "/api/carousel";
|
|
383
403
|
readonly NEWSLETTER_SUBSCRIBE: "/api/newsletter/subscribe";
|
|
384
404
|
};
|
|
@@ -434,10 +454,9 @@ export declare const REVIEW_ENDPOINTS: {
|
|
|
434
454
|
readonly FEATURED: "/api/reviews?featured=true";
|
|
435
455
|
};
|
|
436
456
|
export declare const SEARCH_ENDPOINTS: {
|
|
437
|
-
readonly QUERY: (q: string, extra?: string) => string;
|
|
438
457
|
readonly SUGGESTIONS: "/api/search/suggestions";
|
|
439
|
-
/**
|
|
440
|
-
readonly
|
|
458
|
+
/** Tier 2 — the viewer's own orders/offers/drafts/addresses. `private, no-store`. */
|
|
459
|
+
readonly SCOPED: (q: string) => string;
|
|
441
460
|
};
|
|
442
461
|
export declare const SELLER_ENDPOINTS: {
|
|
443
462
|
readonly BECOME: "/api/seller/become";
|
|
@@ -816,6 +835,15 @@ export declare const API_ENDPOINTS: {
|
|
|
816
835
|
* category change in the form costs zero function invocations on a hit.
|
|
817
836
|
*/
|
|
818
837
|
readonly DESCRIPTION_TEMPLATE: (id: string, variant?: string) => string;
|
|
838
|
+
/**
|
|
839
|
+
* The inherited `priceGuidance` band, restated on the calling seller's own
|
|
840
|
+
* tax basis — seller/admin/moderator only. Deliberately a SEPARATE
|
|
841
|
+
* endpoint from `DESCRIPTION_TEMPLATE`: that one is public and edge-cached
|
|
842
|
+
* so anyone can fetch it, and `priceGuidance` is withheld from that
|
|
843
|
+
* response for the same reason a listing's own price is hidden from guests
|
|
844
|
+
* (CLAUDE.md § "Guest price gating").
|
|
845
|
+
*/
|
|
846
|
+
readonly PRICE_GUIDANCE: (id: string) => string;
|
|
819
847
|
};
|
|
820
848
|
readonly BEFORE_AFTER: {
|
|
821
849
|
readonly LIST: "/api/before-after";
|
|
@@ -880,8 +908,6 @@ export declare const API_ENDPOINTS: {
|
|
|
880
908
|
readonly VOTE: "/api/faqs/vote";
|
|
881
909
|
};
|
|
882
910
|
readonly HOMEPAGE: {
|
|
883
|
-
readonly GET: "/api/homepage";
|
|
884
|
-
readonly SECTIONS: "/api/homepage-sections";
|
|
885
911
|
readonly CAROUSEL: "/api/carousel";
|
|
886
912
|
readonly NEWSLETTER_SUBSCRIBE: "/api/newsletter/subscribe";
|
|
887
913
|
};
|
|
@@ -934,10 +960,9 @@ export declare const API_ENDPOINTS: {
|
|
|
934
960
|
readonly FEATURED: "/api/reviews?featured=true";
|
|
935
961
|
};
|
|
936
962
|
readonly SEARCH: {
|
|
937
|
-
readonly QUERY: (q: string, extra?: string) => string;
|
|
938
963
|
readonly SUGGESTIONS: "/api/search/suggestions";
|
|
939
|
-
/**
|
|
940
|
-
readonly
|
|
964
|
+
/** Tier 2 — the viewer's own orders/offers/drafts/addresses. `private, no-store`. */
|
|
965
|
+
readonly SCOPED: (q: string) => string;
|
|
941
966
|
};
|
|
942
967
|
readonly SELLER: {
|
|
943
968
|
readonly BECOME: "/api/seller/become";
|
|
@@ -1337,6 +1362,15 @@ export declare const API_ROUTES: {
|
|
|
1337
1362
|
* category change in the form costs zero function invocations on a hit.
|
|
1338
1363
|
*/
|
|
1339
1364
|
readonly DESCRIPTION_TEMPLATE: (id: string, variant?: string) => string;
|
|
1365
|
+
/**
|
|
1366
|
+
* The inherited `priceGuidance` band, restated on the calling seller's own
|
|
1367
|
+
* tax basis — seller/admin/moderator only. Deliberately a SEPARATE
|
|
1368
|
+
* endpoint from `DESCRIPTION_TEMPLATE`: that one is public and edge-cached
|
|
1369
|
+
* so anyone can fetch it, and `priceGuidance` is withheld from that
|
|
1370
|
+
* response for the same reason a listing's own price is hidden from guests
|
|
1371
|
+
* (CLAUDE.md § "Guest price gating").
|
|
1372
|
+
*/
|
|
1373
|
+
readonly PRICE_GUIDANCE: (id: string) => string;
|
|
1340
1374
|
};
|
|
1341
1375
|
readonly BEFORE_AFTER: {
|
|
1342
1376
|
readonly LIST: "/api/before-after";
|
|
@@ -1401,8 +1435,6 @@ export declare const API_ROUTES: {
|
|
|
1401
1435
|
readonly VOTE: "/api/faqs/vote";
|
|
1402
1436
|
};
|
|
1403
1437
|
readonly HOMEPAGE: {
|
|
1404
|
-
readonly GET: "/api/homepage";
|
|
1405
|
-
readonly SECTIONS: "/api/homepage-sections";
|
|
1406
1438
|
readonly CAROUSEL: "/api/carousel";
|
|
1407
1439
|
readonly NEWSLETTER_SUBSCRIBE: "/api/newsletter/subscribe";
|
|
1408
1440
|
};
|
|
@@ -1455,10 +1487,9 @@ export declare const API_ROUTES: {
|
|
|
1455
1487
|
readonly FEATURED: "/api/reviews?featured=true";
|
|
1456
1488
|
};
|
|
1457
1489
|
readonly SEARCH: {
|
|
1458
|
-
readonly QUERY: (q: string, extra?: string) => string;
|
|
1459
1490
|
readonly SUGGESTIONS: "/api/search/suggestions";
|
|
1460
|
-
/**
|
|
1461
|
-
readonly
|
|
1491
|
+
/** Tier 2 — the viewer's own orders/offers/drafts/addresses. `private, no-store`. */
|
|
1492
|
+
readonly SCOPED: (q: string) => string;
|
|
1462
1493
|
};
|
|
1463
1494
|
readonly SELLER: {
|
|
1464
1495
|
readonly BECOME: "/api/seller/become";
|
|
@@ -317,6 +317,15 @@ export const CATEGORY_ENDPOINTS = {
|
|
|
317
317
|
* category change in the form costs zero function invocations on a hit.
|
|
318
318
|
*/
|
|
319
319
|
DESCRIPTION_TEMPLATE: (id, variant) => `/api/categories/${id}/description-template${variant ? `?variant=${variant}` : ""}`,
|
|
320
|
+
/**
|
|
321
|
+
* The inherited `priceGuidance` band, restated on the calling seller's own
|
|
322
|
+
* tax basis — seller/admin/moderator only. Deliberately a SEPARATE
|
|
323
|
+
* endpoint from `DESCRIPTION_TEMPLATE`: that one is public and edge-cached
|
|
324
|
+
* so anyone can fetch it, and `priceGuidance` is withheld from that
|
|
325
|
+
* response for the same reason a listing's own price is hidden from guests
|
|
326
|
+
* (CLAUDE.md § "Guest price gating").
|
|
327
|
+
*/
|
|
328
|
+
PRICE_GUIDANCE: (id) => `/api/categories/${id}/price-guidance`,
|
|
320
329
|
};
|
|
321
330
|
// ---------------------------------------------------------------------------
|
|
322
331
|
// Tools — public surfaces that answer a question rather than sell something
|
|
@@ -333,6 +342,19 @@ export const TOOL_ENDPOINTS = {
|
|
|
333
342
|
* its invocation allowance.
|
|
334
343
|
*/
|
|
335
344
|
PRICE_INDEX_SEARCH: (q) => `/api/tools/price-index?q=${encodeURIComponent(q)}`,
|
|
345
|
+
/**
|
|
346
|
+
* Tier 1 adoption — the client resolved these ids LOCALLY, from the
|
|
347
|
+
* public search artifact, at zero Firestore reads. This route's only job
|
|
348
|
+
* is the multi-get `searchPriceIndex` otherwise pays for every lookup.
|
|
349
|
+
*/
|
|
350
|
+
PRICE_INDEX_BANDS: (ids) => `/api/tools/price-index/bands?ids=${ids.map(encodeURIComponent).join(",")}`,
|
|
351
|
+
/**
|
|
352
|
+
* The competitor-crawl market band for the same leaves — SIGNED-IN ONLY, and
|
|
353
|
+
* therefore a separate request from `PRICE_INDEX_BANDS` rather than more
|
|
354
|
+
* fields on it. That route is public and edge-cached; this one must not be,
|
|
355
|
+
* or one signed-in lookup populates an entry every guest reads.
|
|
356
|
+
*/
|
|
357
|
+
PRICE_INDEX_MARKET: (ids) => `/api/tools/price-index/market?ids=${ids.map(encodeURIComponent).join(",")}`,
|
|
336
358
|
};
|
|
337
359
|
/**
|
|
338
360
|
* The sticker-sheet kiss-cut tracer. Admin-only, every route behind
|
|
@@ -473,8 +495,17 @@ export const FAQ_ENDPOINTS = {
|
|
|
473
495
|
// Homepage
|
|
474
496
|
// ---------------------------------------------------------------------------
|
|
475
497
|
export const HOMEPAGE_ENDPOINTS = {
|
|
476
|
-
|
|
477
|
-
|
|
498
|
+
/*
|
|
499
|
+
* 🛑 `GET` and `SECTIONS` were both removed 2026-10-19 — `/api/homepage` was
|
|
500
|
+
* NEVER mounted (only a dead hook referenced it), and `/api/homepage-sections`
|
|
501
|
+
* was a mounted generic-starter-kit stub writing/reading the real
|
|
502
|
+
* `homepageSections` collection through an incompatible schema. See the
|
|
503
|
+
* deletion note in `features/homepage/types/index.ts` for the full writeup.
|
|
504
|
+
* The real, single section API is `/api/admin/sections` (admin CRUD);
|
|
505
|
+
* public rendering reads `homepageSectionsRepository` directly, server-side,
|
|
506
|
+
* in `MarketplaceHomepageView` — no public GET endpoint for sections exists
|
|
507
|
+
* or is needed.
|
|
508
|
+
*/
|
|
478
509
|
CAROUSEL: "/api/carousel",
|
|
479
510
|
NEWSLETTER_SUBSCRIBE: "/api/newsletter/subscribe",
|
|
480
511
|
};
|
|
@@ -566,10 +597,9 @@ export const REVIEW_ENDPOINTS = {
|
|
|
566
597
|
// Search
|
|
567
598
|
// ---------------------------------------------------------------------------
|
|
568
599
|
export const SEARCH_ENDPOINTS = {
|
|
569
|
-
QUERY: (q, extra) => `/api/search?q=${encodeURIComponent(q)}${extra ? `&${extra}` : ""}`,
|
|
570
600
|
SUGGESTIONS: "/api/search/suggestions",
|
|
571
|
-
/**
|
|
572
|
-
|
|
601
|
+
/** Tier 2 — the viewer's own orders/offers/drafts/addresses. `private, no-store`. */
|
|
602
|
+
SCOPED: (q) => `/api/search/scoped?q=${encodeURIComponent(q)}`,
|
|
573
603
|
};
|
|
574
604
|
// ---------------------------------------------------------------------------
|
|
575
605
|
// Seller
|
|
@@ -20,7 +20,9 @@ export const COLLECTION_CACHE_PATHS = {
|
|
|
20
20
|
categories: [CATEGORY_ENDPOINTS.LIST],
|
|
21
21
|
products: [PRODUCT_ENDPOINTS.LIST],
|
|
22
22
|
carouselSlides: [HOMEPAGE_ENDPOINTS.CAROUSEL],
|
|
23
|
-
homepageSections
|
|
23
|
+
// No public GET endpoint exists for homepageSections (removed 2026-10-19 —
|
|
24
|
+
// see HOMEPAGE_ENDPOINTS' own comment). Real-time freshness comes from
|
|
25
|
+
// `revalidatePath("/")`, which /api/admin/sections already calls on write.
|
|
24
26
|
siteSettings: [SITE_SETTINGS_ENDPOINTS.GET],
|
|
25
27
|
faqs: [FAQ_ENDPOINTS.LIST],
|
|
26
28
|
reviews: [REVIEW_ENDPOINTS.LIST],
|
|
@@ -49,6 +49,9 @@ export declare const PRODUCT_FIELDS: {
|
|
|
49
49
|
readonly STOCK_QUANTITY: "stockQuantity";
|
|
50
50
|
readonly AVAILABLE_QUANTITY: "availableQuantity";
|
|
51
51
|
readonly IS_SOLD: "isSold";
|
|
52
|
+
readonly MADE_TO_ORDER: "madeToOrder";
|
|
53
|
+
readonly PART_GLOSSARY_ID: "partGlossaryId";
|
|
54
|
+
readonly COLOURWAY: "colourway";
|
|
52
55
|
readonly MAIN_IMAGE: "mainImage";
|
|
53
56
|
readonly IMAGES: "images";
|
|
54
57
|
readonly VIDEO: "video";
|
|
@@ -181,6 +184,7 @@ export declare const PRODUCT_FIELDS: {
|
|
|
181
184
|
readonly LIVE: "live";
|
|
182
185
|
readonly ART: "art";
|
|
183
186
|
readonly STICKERS: "stickers";
|
|
187
|
+
readonly SPARES: "spares";
|
|
184
188
|
};
|
|
185
189
|
};
|
|
186
190
|
export declare const PRODUCT_STATUS_TRANSITIONS: Record<string, readonly string[]>;
|
|
@@ -853,6 +857,8 @@ export declare const HOMEPAGE_SECTION_FIELDS: {
|
|
|
853
857
|
readonly ORDER: "order";
|
|
854
858
|
readonly ENABLED: "enabled";
|
|
855
859
|
readonly CONFIG: "config";
|
|
860
|
+
/** Which page the row belongs to. Absent on a row means "homepage". */
|
|
861
|
+
readonly SURFACE: "surface";
|
|
856
862
|
readonly CREATED_AT: "createdAt";
|
|
857
863
|
readonly UPDATED_AT: "updatedAt";
|
|
858
864
|
readonly TYPE_VALUES: {
|
|
@@ -58,6 +58,9 @@ export const PRODUCT_FIELDS = {
|
|
|
58
58
|
STOCK_QUANTITY: "stockQuantity",
|
|
59
59
|
AVAILABLE_QUANTITY: "availableQuantity",
|
|
60
60
|
IS_SOLD: "isSold",
|
|
61
|
+
MADE_TO_ORDER: "madeToOrder",
|
|
62
|
+
PART_GLOSSARY_ID: "partGlossaryId",
|
|
63
|
+
COLOURWAY: "colourway",
|
|
61
64
|
MAIN_IMAGE: "mainImage",
|
|
62
65
|
IMAGES: "images",
|
|
63
66
|
VIDEO: "video",
|
|
@@ -201,6 +204,7 @@ export const PRODUCT_FIELDS = {
|
|
|
201
204
|
LIVE: "live",
|
|
202
205
|
ART: "art",
|
|
203
206
|
STICKERS: "stickers",
|
|
207
|
+
SPARES: "spares",
|
|
204
208
|
},
|
|
205
209
|
};
|
|
206
210
|
export const PRODUCT_STATUS_TRANSITIONS = {
|
|
@@ -1035,6 +1039,8 @@ export const HOMEPAGE_SECTION_FIELDS = {
|
|
|
1035
1039
|
ORDER: "order",
|
|
1036
1040
|
ENABLED: "enabled",
|
|
1037
1041
|
CONFIG: "config",
|
|
1042
|
+
/** Which page the row belongs to. Absent on a row means "homepage". */
|
|
1043
|
+
SURFACE: "surface",
|
|
1038
1044
|
CREATED_AT: "createdAt",
|
|
1039
1045
|
UPDATED_AT: "updatedAt",
|
|
1040
1046
|
TYPE_VALUES: {
|
|
@@ -1,6 +1,26 @@
|
|
|
1
|
-
export type PolicyKey = "privacy" | "terms" | "cookies" | "refund" | "ethics" | "conduct";
|
|
1
|
+
export type PolicyKey = "privacy" | "terms" | "cookies" | "refund" | "ethics" | "conduct" | "cancellation" | "grievance";
|
|
2
2
|
export interface PolicyPageViewProps {
|
|
3
3
|
/** Which policy to render */
|
|
4
4
|
policy: PolicyKey;
|
|
5
|
+
/**
|
|
6
|
+
* Rendered after the policy sections and before the related links.
|
|
7
|
+
*
|
|
8
|
+
* The slot exists for /grievance, whose officer block (name, designation,
|
|
9
|
+
* email, phone) comes from `appkit.config.js` → `business.grievance` rather
|
|
10
|
+
* than from i18n — those values are deployment-specific and some are still
|
|
11
|
+
* undecided, so they render through `<PendingValue>` as a visible
|
|
12
|
+
* `[to be confirmed]` marker.
|
|
13
|
+
*
|
|
14
|
+
* 🛑 Deliberately a `children` ReactNode and not a render prop: this is an
|
|
15
|
+
* async Server Component, and a function prop would have to cross the
|
|
16
|
+
* server/client boundary at the call site to be useful. The caller builds
|
|
17
|
+
* the node and passes it down, which needs nothing serialisable.
|
|
18
|
+
*
|
|
19
|
+
* Note it renders under the i18n sections but is SKIPPED when an admin HTML
|
|
20
|
+
* override is present — an override replaces the page's body, and silently
|
|
21
|
+
* appending a block the admin cannot see or edit would make the override a
|
|
22
|
+
* half-truth.
|
|
23
|
+
*/
|
|
24
|
+
children?: React.ReactNode;
|
|
5
25
|
}
|
|
6
|
-
export declare function PolicyPageView({ policy, }: PolicyPageViewProps): Promise<import("react").JSX.Element>;
|
|
26
|
+
export declare function PolicyPageView({ policy, children, }: PolicyPageViewProps): Promise<import("react").JSX.Element>;
|
|
@@ -58,9 +58,21 @@ const POLICY_META = {
|
|
|
58
58
|
firestoreField: "codeOfConduct",
|
|
59
59
|
relatedLabelKey: "relatedConduct",
|
|
60
60
|
},
|
|
61
|
+
cancellation: {
|
|
62
|
+
path: String(ROUTES.PUBLIC.CANCELLATION_POLICY),
|
|
63
|
+
namespace: "cancellationPolicy",
|
|
64
|
+
firestoreField: "cancellation",
|
|
65
|
+
relatedLabelKey: "relatedCancellation",
|
|
66
|
+
},
|
|
67
|
+
grievance: {
|
|
68
|
+
path: String(ROUTES.PUBLIC.GRIEVANCE),
|
|
69
|
+
namespace: "grievance",
|
|
70
|
+
firestoreField: "grievance",
|
|
71
|
+
relatedLabelKey: "relatedGrievance",
|
|
72
|
+
},
|
|
61
73
|
};
|
|
62
74
|
const POLICY_KEYS = Object.keys(POLICY_META);
|
|
63
|
-
export async function PolicyPageView({ policy, }) {
|
|
75
|
+
export async function PolicyPageView({ policy, children, }) {
|
|
64
76
|
const page = { container: PAGE_CONTAINER };
|
|
65
77
|
const meta = POLICY_META[policy];
|
|
66
78
|
const { getTranslations } = await import("next-intl/server");
|
|
@@ -93,5 +105,5 @@ export async function PolicyPageView({ policy, }) {
|
|
|
93
105
|
}));
|
|
94
106
|
return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "banner", children: _jsxs(Div, { className: `${page.container.sm}`, children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-3", children: t("title") }), _jsx(Text, { color: "inverse", variant: "none", className: "/80", size: "sm", children: t("lastUpdated") })] }) }), _jsxs(Div, { className: `${page.container.sm}`, padding: "content-banner", children: [adminHtml ? (
|
|
95
107
|
/* Admin-set HTML takes priority */
|
|
96
|
-
_jsx(Div, { className: "prose max-w-none", dangerouslySetInnerHTML: { __html: adminHtml } })) : (_jsxs(_Fragment, { children: [t.has("intro") && (_jsx(Text, { variant: "secondary", className: "mb-10 leading-relaxed", size: "base", children: t("intro") })), _jsx(Stack, { gap: "xl", children: sections.map((section, i) => (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-3", children: section.heading }), _jsx(Text, { variant: "secondary", className: "leading-relaxed whitespace-pre-line", children: section.body })] }, i))) })] })), relatedLinks.length > 0 && (_jsxs(Div, { className: `mt-14 border-t`, border: "default", padding: "t-xl", children: [_jsx(Heading, { level: 3, className: "mb-3 uppercase tracking-wide", color: "muted", size: "sm", weight: "semibold", children: t("relatedTitle") }), _jsx(Row, { wrap: true, gap: "md", children: relatedLinks.map((l) => (_jsx(TextLink, { href: l.href, variant: "muted", size: "sm", children: l.label }, l.href))) })] }))] })] }));
|
|
108
|
+
_jsx(Div, { className: "prose max-w-none", dangerouslySetInnerHTML: { __html: adminHtml } })) : (_jsxs(_Fragment, { children: [t.has("intro") && (_jsx(Text, { variant: "secondary", className: "mb-10 leading-relaxed", size: "base", children: t("intro") })), _jsx(Stack, { gap: "xl", children: sections.map((section, i) => (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-3", children: section.heading }), _jsx(Text, { variant: "secondary", className: "leading-relaxed whitespace-pre-line", children: section.body })] }, i))) }), children] })), relatedLinks.length > 0 && (_jsxs(Div, { className: `mt-14 border-t`, border: "default", padding: "t-xl", children: [_jsx(Heading, { level: 3, className: "mb-3 uppercase tracking-wide", color: "muted", size: "sm", weight: "semibold", children: t("relatedTitle") }), _jsx(Row, { wrap: true, gap: "md", children: relatedLinks.map((l) => (_jsx(TextLink, { href: l.href, variant: "muted", size: "sm", children: l.label }, l.href))) })] }))] })] }));
|
|
97
109
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import Link from "next/link.js";
|
|
3
|
+
import { productImageUrls } from "../../products/schemas/firestore.js";
|
|
3
4
|
import { getPublicUserProfile, getProfileStoreProducts, getReviewsAuthoredBy, getReviewsReceivedBy } from "../../auth/actions/profile-actions.js";
|
|
4
5
|
import { storeRepository } from "../../stores/repository/store.repository.js";
|
|
5
6
|
import { safeRead } from "../../../errors/safe-read.js";
|
|
@@ -37,7 +38,7 @@ function toProductItem(p) {
|
|
|
37
38
|
originalPrice: p.originalPrice,
|
|
38
39
|
currency: p.currency,
|
|
39
40
|
mainImage: p.mainImage,
|
|
40
|
-
images: p.images,
|
|
41
|
+
images: productImageUrls(p.images),
|
|
41
42
|
status: p.status,
|
|
42
43
|
condition: p.condition,
|
|
43
44
|
listingType: p.listingType,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { normalizeError } from "../../../errors/normalize.js";
|
|
3
|
+
import { serverLogger } from "../../../monitoring/server-logger.js";
|
|
4
|
+
import { siteSettingsRepository } from "../../../repositories/index.js";
|
|
2
5
|
import { ROUTES } from "../../../constants/index.js";
|
|
3
6
|
import { PAGE_CONTAINER } from "../../../_internal/shared/styles/page.js";
|
|
4
7
|
import { Div, Heading, Text, Section, Stack, FlowDiagram } from "../../../ui/index.js";
|
|
@@ -8,6 +11,35 @@ export async function ShippingPolicyView({} = {}) {
|
|
|
8
11
|
const page = { container: PAGE_CONTAINER };
|
|
9
12
|
const { getTranslations } = await import("next-intl/server");
|
|
10
13
|
const t = await getTranslations("shippingPolicy");
|
|
14
|
+
/*
|
|
15
|
+
* 🛑 `siteSettings.legalPages.shipping` was WRITTEN by the admin form and
|
|
16
|
+
* read by nothing.
|
|
17
|
+
*
|
|
18
|
+
* Every other legal page renders through `PolicyPageView`, which checks that
|
|
19
|
+
* group for an admin HTML override. This page does not — it has a bespoke
|
|
20
|
+
* layout with a five-step delivery diagram that a flat `sections[]` cannot
|
|
21
|
+
* express — so the override had no reader and an admin who rewrote the
|
|
22
|
+
* shipping policy saw their text saved successfully and never appear.
|
|
23
|
+
*
|
|
24
|
+
* Reading it here rather than converting the page is the deliberate choice:
|
|
25
|
+
* converting would mean flattening 38 i18n keys into `sections[]` and losing
|
|
26
|
+
* the diagram, which is a regression dressed up as consolidation.
|
|
27
|
+
*/
|
|
28
|
+
let adminHtml = "";
|
|
29
|
+
try {
|
|
30
|
+
const settings = await siteSettingsRepository.getSingleton();
|
|
31
|
+
adminHtml = settings.legalPages?.shipping ?? "";
|
|
32
|
+
}
|
|
33
|
+
catch (_err) {
|
|
34
|
+
void normalizeError(_err);
|
|
35
|
+
// The i18n copy below is a complete policy in its own right, so a failed
|
|
36
|
+
// read degrades to it — but an admin who HAS overridden this page would
|
|
37
|
+
// silently see the old text, so the operator needs to know.
|
|
38
|
+
serverLogger.warn("about.shippingPolicyView: siteSettings.legalPages read failed", {
|
|
39
|
+
field: "shipping",
|
|
40
|
+
error: normalizeError(_err).message,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
11
43
|
const SECTIONS = [
|
|
12
44
|
{ title: t("orderProcessTitle"), text: t("orderProcessText") },
|
|
13
45
|
{ title: t("standardTitle"), text: t("standardText") },
|
|
@@ -55,5 +87,13 @@ export async function ShippingPolicyView({} = {}) {
|
|
|
55
87
|
desc: t("diagramStatus5Desc"),
|
|
56
88
|
},
|
|
57
89
|
];
|
|
58
|
-
return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "banner", children: _jsxs(Div, { className: `${page.container.sm}`, children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-3", children: t("title") }), _jsx(Text, { color: "inverse", variant: "none", className: "/80", children: t("lastUpdated") })] }) }), _jsxs(Div, { className: `${page.container.sm}`, padding: "content-banner", children: [
|
|
90
|
+
return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "banner", children: _jsxs(Div, { className: `${page.container.sm}`, children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-3", children: t("title") }), _jsx(Text, { color: "inverse", variant: "none", className: "/80", children: t("lastUpdated") })] }) }), _jsxs(Div, { className: `${page.container.sm}`, padding: "content-banner", children: [adminHtml ? (
|
|
91
|
+
/*
|
|
92
|
+
Admin-set HTML replaces the WHOLE body — subtitle, sections,
|
|
93
|
+
diagram and contact block — matching PolicyPageView's behaviour
|
|
94
|
+
exactly. Rendering the override above the bundled copy would show
|
|
95
|
+
two shipping policies on one page, and the reader has no way to
|
|
96
|
+
know which one binds.
|
|
97
|
+
*/
|
|
98
|
+
_jsx(Div, { className: "prose max-w-none", dangerouslySetInnerHTML: { __html: adminHtml } })) : (_jsxs(_Fragment, { children: [_jsx(Text, { size: "lg", variant: "secondary", className: "mb-8", children: t("subtitle") }), _jsx(FlowDiagram, { title: `🚚 ${t("diagramTitle")}`, titleClass: "text-sky-700 dark:text-sky-300", connectorClass: "bg-sky-200 dark:bg-sky-800", steps: DIAGRAM_STEPS, className: "mb-10", note: `📌 ${t("diagramNote")}` }), _jsxs(Stack, { gap: "xl", children: [SECTIONS.map(({ title, text }) => (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-3", children: title }), _jsx(Text, { variant: "secondary", className: "leading-relaxed", children: text })] }, title))), _jsxs(Section, { rounded: "xl", padding: "lg", border: "default", surface: "subtle", children: [_jsx(Heading, { level: 2, className: "mb-2", children: t("contactTitle") }), _jsx(Text, { variant: "secondary", children: t("contactText") })] })] })] })), _jsxs(Div, { layout: "flex", gap: "6", textSize: "sm", className: `mt-12 border-t`, border: "default", padding: "t-xl", children: [_jsx(TextLink, { href: String(ROUTES.PUBLIC.TRACK_ORDER), className: CLS_FOOTER_LINK, children: t("trackOrder") }), _jsx(TextLink, { href: String(ROUTES.PUBLIC.HELP), className: CLS_FOOTER_LINK, children: t("helpCenter") }), _jsx(TextLink, { href: String(ROUTES.PUBLIC.CONTACT), className: CLS_FOOTER_LINK, children: t("contactUs") }), _jsx(TextLink, { href: String(ROUTES.PUBLIC.REFUND_POLICY), className: CLS_FOOTER_LINK, children: "Refund Policy" })] })] })] }));
|
|
59
99
|
}
|
|
@@ -109,5 +109,5 @@ function renderNotificationListContent(props) {
|
|
|
109
109
|
return _jsx(Row, { align: "center", justify: "center", padding: "y-2xl", children: _jsx(Spinner, { size: "md" }) });
|
|
110
110
|
if (notifications.length === 0)
|
|
111
111
|
return (_jsxs(Stack, { justify: "center", className: "text-left", paddingY: "y-2xl", paddingX: "x-md", align: "start", children: [_jsx("svg", { className: "w-12 h-12 text-[var(--appkit-color-text-faint)] mb-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" }) }), _jsx(Text, { weight: "medium", color: "primary", children: labels.empty }), _jsx(Text, { size: "sm", className: "mt-1", color: "muted", children: labels.emptyDesc })] }));
|
|
112
|
-
return (_jsx(Ul, { children: notifications.map((notification) => (_jsxs(Li, { layout: "flex-start", gap: "3", padding: "inline", className: `group border-b border-[var(--appkit-color-border)] last:border-0 transition-colors hover:bg-[var(--appkit-color-bg)] dark:hover:bg-[var(--appkit-color-surface-elevated)] ${!notification.isRead ? "bg-primary/5 dark:bg-primary/10" : ""}`, children: [_jsx(Span, { size: "xl", className: "flex-shrink-0 mt-0.5", children: notificationIcons[notification.type] ?? "🔔" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { align: "start", justify: "between", gap: "xs", children: [_jsxs(Text, { size: "sm", className: "leading-tight", color: "primary", weight: "medium", children: [notification.title, !notification.isRead && _jsx(Span, { className: "ml-1.5 inline-block w-2 h-2 bg-primary flex-shrink-0 align-middle", rounded: "full" })] }), _jsx(Span, { size: "xs", className: "flex-shrink-0", color: "muted", children: formatRelativeTime(notification.createdAt) })] }), _jsx(Text, { size: "sm", className: `mt-0.5`, truncate: 2, color: "muted", children: notification.message }), _jsxs(Row, { gap: "3", className: "mt-1.5", children: [notification.actionUrl && renderActionLink({ href: notification.actionUrl, onClick: () => { void handleMarkReadAndClose(notification); }, className: "text-[length:var(--appkit-text-xs)] text-primary hover:underline font-medium", children: notification.actionLabel ?? labels.viewAction }), !notification.isRead && _jsx(Button, { variant: "ghost", onClick: () => void handleMarkRead(notification.id), className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)] hover:text-
|
|
112
|
+
return (_jsx(Ul, { children: notifications.map((notification) => (_jsxs(Li, { layout: "flex-start", gap: "3", padding: "inline", className: `group border-b border-[var(--appkit-color-border)] last:border-0 transition-colors hover:bg-[var(--appkit-color-bg)] dark:hover:bg-[var(--appkit-color-surface-elevated)] ${!notification.isRead ? "bg-primary/5 dark:bg-primary/10" : ""}`, children: [_jsx(Span, { size: "xl", className: "flex-shrink-0 mt-0.5", children: notificationIcons[notification.type] ?? "🔔" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { align: "start", justify: "between", gap: "xs", children: [_jsxs(Text, { size: "sm", className: "leading-tight", color: "primary", weight: "medium", children: [notification.title, !notification.isRead && _jsx(Span, { className: "ml-1.5 inline-block w-2 h-2 bg-primary flex-shrink-0 align-middle", rounded: "full" })] }), _jsx(Span, { size: "xs", className: "flex-shrink-0", color: "muted", children: formatRelativeTime(notification.createdAt) })] }), _jsx(Text, { size: "sm", className: `mt-0.5`, truncate: 2, color: "muted", children: notification.message }), _jsxs(Row, { gap: "3", className: "mt-1.5", children: [notification.actionUrl && renderActionLink({ href: notification.actionUrl, onClick: () => { void handleMarkReadAndClose(notification); }, className: "text-[length:var(--appkit-text-xs)] text-primary hover:underline font-medium", children: notification.actionLabel ?? labels.viewAction }), !notification.isRead && _jsx(Button, { variant: "ghost", onClick: () => void handleMarkRead(notification.id), className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)] hover:underline p-[var(--appkit-space-0)] h-auto", children: labels.markRead })] })] })] }, notification.id))) }));
|
|
113
113
|
}
|
|
@@ -4,7 +4,11 @@ import { ACCOUNT_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
|
4
4
|
export function useProfileStats(enabled) {
|
|
5
5
|
const { data: ordersData, isLoading: ordersLoading } = useQuery({
|
|
6
6
|
queryKey: ["user-orders-count"],
|
|
7
|
-
queryFn: () =>
|
|
7
|
+
queryFn: () =>
|
|
8
|
+
// `items`, not `orders` — GET /api/user/orders emits the shared list
|
|
9
|
+
// envelope. Only `total` is read below, so the wrong key never threw
|
|
10
|
+
// here; it was a false declaration waiting for someone to read it.
|
|
11
|
+
apiClient.get(ACCOUNT_ENDPOINTS.ORDERS),
|
|
8
12
|
enabled,
|
|
9
13
|
});
|
|
10
14
|
const { data: addressesData, isLoading: addressesLoading } = useQuery({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { JsonValue } from "../../../schemas/types";
|
|
1
2
|
import { BaseRepository } from "../../../providers/db-firebase";
|
|
2
3
|
import { type AddressBanStatus, type AddressCreateInput, type AddressDocument, type AddressPlain, type AddressOwnerType, type AddressUpdateInput } from "../schemas";
|
|
3
4
|
export declare class AddressesRepository extends BaseRepository<AddressDocument> {
|
|
@@ -14,6 +15,21 @@ export declare class AddressesRepository extends BaseRepository<AddressDocument>
|
|
|
14
15
|
* call.
|
|
15
16
|
*/
|
|
16
17
|
protected piiFields: readonly ["fullName", "phone", "addressLine1", "addressLine2"];
|
|
18
|
+
/**
|
|
19
|
+
* Admin search by name, without decryption. ONLY `fullName` — never
|
|
20
|
+
* `addressLine1`: "find everyone on Brigade Road" is not a capability
|
|
21
|
+
* admin needs and is the single most re-identifying field on the
|
|
22
|
+
* document. See `security/blind-index-tokens.ts` for why this is
|
|
23
|
+
* whole-word rather than a prefix-expanded index.
|
|
24
|
+
*
|
|
25
|
+
* Runs through `applyWriteHooks`, so it reaches every write path
|
|
26
|
+
* including a seed load — which is what makes it survive the tester
|
|
27
|
+
* harness's cascade on `addresses` (it references `ownerId`, a
|
|
28
|
+
* PRESERVE-tier user, so the row itself is never actually wiped — but
|
|
29
|
+
* the field regenerating on every write is what makes that true by
|
|
30
|
+
* construction rather than by luck).
|
|
31
|
+
*/
|
|
32
|
+
protected buildTokensIndexFor(data: Record<string, JsonValue>): Record<string, string[]> | null;
|
|
17
33
|
/** Plaintext PII. **Opt-in and named** — shows up in review and in a grep. */
|
|
18
34
|
revealAddress(doc: AddressDocument): AddressPlain;
|
|
19
35
|
/** As `revealAddress`, for a list read. */
|
|
@@ -14,6 +14,7 @@ import { DatabaseError } from "../../../errors/index.js";
|
|
|
14
14
|
import { serverLogger } from "../../../monitoring/index.js";
|
|
15
15
|
import { BaseRepository, getFirestoreCount, prepareForFirestore, } from "../../../providers/db-firebase/index.js";
|
|
16
16
|
import { maskPii, revealPii, ADDRESS_PII_FIELDS, } from "../../../security/index.js";
|
|
17
|
+
import { buildTokensIndex } from "../../../security/blind-index-tokens.js";
|
|
17
18
|
import { ADDRESS_FIELDS, ADDRESSES_COLLECTION, } from "../schemas/index.js";
|
|
18
19
|
export class AddressesRepository extends BaseRepository {
|
|
19
20
|
constructor() {
|
|
@@ -31,6 +32,26 @@ export class AddressesRepository extends BaseRepository {
|
|
|
31
32
|
*/
|
|
32
33
|
this.piiFields = ADDRESS_PII_FIELDS;
|
|
33
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Admin search by name, without decryption. ONLY `fullName` — never
|
|
37
|
+
* `addressLine1`: "find everyone on Brigade Road" is not a capability
|
|
38
|
+
* admin needs and is the single most re-identifying field on the
|
|
39
|
+
* document. See `security/blind-index-tokens.ts` for why this is
|
|
40
|
+
* whole-word rather than a prefix-expanded index.
|
|
41
|
+
*
|
|
42
|
+
* Runs through `applyWriteHooks`, so it reaches every write path
|
|
43
|
+
* including a seed load — which is what makes it survive the tester
|
|
44
|
+
* harness's cascade on `addresses` (it references `ownerId`, a
|
|
45
|
+
* PRESERVE-tier user, so the row itself is never actually wiped — but
|
|
46
|
+
* the field regenerating on every write is what makes that true by
|
|
47
|
+
* construction rather than by luck).
|
|
48
|
+
*/
|
|
49
|
+
buildTokensIndexFor(data) {
|
|
50
|
+
const fullName = typeof data.fullName === "string" ? data.fullName : undefined;
|
|
51
|
+
if (!fullName)
|
|
52
|
+
return null;
|
|
53
|
+
return { nameTokensIndex: buildTokensIndex(fullName) };
|
|
54
|
+
}
|
|
34
55
|
/*
|
|
35
56
|
* 🛑 `mapDoc` is NOT overridden, and that absence IS the change.
|
|
36
57
|
*
|
|
@@ -238,7 +238,22 @@ export async function adminUpdateProduct(adminId, id, input) {
|
|
|
238
238
|
finalized.mainImage = await finalizeStagedMediaUrl(finalized.mainImage);
|
|
239
239
|
}
|
|
240
240
|
if (Array.isArray(finalized.images) && finalized.images.length > 0) {
|
|
241
|
-
|
|
241
|
+
/*
|
|
242
|
+
* 🛑 Finalise the URL and KEEP THE REST OF THE OBJECT (A6).
|
|
243
|
+
*
|
|
244
|
+
* `finalizeStagedMediaArray` takes and returns `string[]` — it moves
|
|
245
|
+
* staged uploads to their permanent path. Handing it
|
|
246
|
+
* `images.map(i => i.url)` and assigning the result back would have
|
|
247
|
+
* silently dropped `alt`, `width`, `height` and `thumbnailUrl` on every
|
|
248
|
+
* admin save: the exact lossy-write failure `toProductImages` carries its
|
|
249
|
+
* own warning about, on the one code path where a human has just typed
|
|
250
|
+
* the alt text.
|
|
251
|
+
*/
|
|
252
|
+
const finalisedUrls = await finalizeStagedMediaArray(finalized.images.map((img) => img.url));
|
|
253
|
+
finalized.images = finalized.images.map((img, i) => ({
|
|
254
|
+
...img,
|
|
255
|
+
url: finalisedUrls[i] ?? img.url,
|
|
256
|
+
}));
|
|
242
257
|
}
|
|
243
258
|
if (finalized.video?.url) {
|
|
244
259
|
finalized.video = {
|
|
@@ -115,6 +115,14 @@ const categoryFormSchema = z.object({
|
|
|
115
115
|
.optional()
|
|
116
116
|
.or(z.literal("")),
|
|
117
117
|
highlightsText: z.string().optional().or(z.literal("")),
|
|
118
|
+
/*
|
|
119
|
+
* The Hasbro / international-market name for a generation wave, rendered on
|
|
120
|
+
* the category page as "Also known as". Editable here because wave naming is
|
|
121
|
+
* editorial and gets corrected — the seed deliberately leaves it unset
|
|
122
|
+
* wherever a divergence is not evidenced, and this is how one gets filled in
|
|
123
|
+
* later without a reseed.
|
|
124
|
+
*/
|
|
125
|
+
hasbroName: z.string().max(120, "Keep the name under 120 characters").optional().or(z.literal("")),
|
|
118
126
|
taxCodeId: z.string().optional().or(z.literal("")),
|
|
119
127
|
defaultCondition: z.string().optional().or(z.literal("")),
|
|
120
128
|
defaultAuthenticity: z.string().optional().or(z.literal("")),
|
|
@@ -145,6 +153,7 @@ const EMPTY_VALUES = {
|
|
|
145
153
|
showInMenu: true,
|
|
146
154
|
contentBody: "",
|
|
147
155
|
highlightsText: "",
|
|
156
|
+
hasbroName: "",
|
|
148
157
|
taxCodeId: "",
|
|
149
158
|
defaultCondition: "",
|
|
150
159
|
defaultAuthenticity: "",
|
|
@@ -245,6 +254,13 @@ function buildContentPayload(v, faqs, specs, templates, carriedPriceGuidance) {
|
|
|
245
254
|
contentBody: String(v.contentBody ?? ""),
|
|
246
255
|
highlights: linesToList(String(v.highlightsText ?? "")),
|
|
247
256
|
faqs: faqs.filter((f) => f.question.trim() !== "" && f.answer.trim() !== ""),
|
|
257
|
+
/*
|
|
258
|
+
* Omitted when blank rather than sent as `""`. The route schema is
|
|
259
|
+
* `.min(1)`, so an empty string is a 400 — and "I cleared the field" has
|
|
260
|
+
* to mean "do not write it", not "write an empty string every reader then
|
|
261
|
+
* has to treat as absent".
|
|
262
|
+
*/
|
|
263
|
+
...(String(v.hasbroName ?? "").trim() ? { hasbroName: String(v.hasbroName).trim() } : {}),
|
|
248
264
|
descriptionTemplates: templates
|
|
249
265
|
.filter((t) => t.label.trim() !== "" && t.body.trim() !== "")
|
|
250
266
|
.map((t) => ({ ...t, id: t.id || toCategorySlug(t.label) || "template" })),
|
|
@@ -316,6 +332,7 @@ export function AdminCategoryEditorView({ categoryId, onSaved, onDeleted, embedd
|
|
|
316
332
|
showInMenu: cat.display?.showInMenu ?? true,
|
|
317
333
|
contentBody: cat.contentBody ?? "",
|
|
318
334
|
highlightsText: listToLines(cat.highlights),
|
|
335
|
+
hasbroName: cat.hasbroName ?? "",
|
|
319
336
|
taxCodeId: cat.productDefaults?.taxCodeId ?? "",
|
|
320
337
|
defaultCondition: cat.productDefaults?.defaultCondition ?? "",
|
|
321
338
|
defaultAuthenticity: cat.productDefaults?.defaultAuthenticity ?? "",
|
|
@@ -430,8 +447,8 @@ export function AdminCategoryEditorView({ categoryId, onSaved, onDeleted, embedd
|
|
|
430
447
|
{
|
|
431
448
|
id: "content",
|
|
432
449
|
label: "Content & Templates",
|
|
433
|
-
fields: ["contentBody", "highlightsText"],
|
|
434
|
-
render: ({ values: v, onChange, errors }) => (_jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Category page body" }), _jsx(Textarea, { value: String(v.contentBody ?? ""), onChange: (e) => onChange({ contentBody: e.target.value }), rows: 8, placeholder: "400\u2013900 characters of keyword-rich HTML with links to sibling and parent categories\u2026" }), errors.contentBody ? (_jsx(Text, { size: "sm", variant: "error", role: "alert", children: errors.contentBody })) : (_jsx(Text, { size: "xs", color: "muted", children: "Rendered on the category page. Distinct from the short Description above, which also feeds the SEO meta and every card. 36 of worldhobbyshop's 46 categories carry one of these and it is their actual ranking mechanism; no Shopify competitor in the crawl has any." }))] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Highlights \u2014 one per line" }), _jsx(Textarea, { value: String(v.highlightsText ?? ""), onChange: (e) => onChange({ highlightsText: e.target.value }), rows: 4, placeholder: "Genuine Takara Tomy stock\nChecked and photographed before listing" }), _jsx(Text, { size: "xs", color: "muted", children: "Short \"why shop this\" bullets, shown above the FAQs. Blank lines are dropped. Up to 12." })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Category FAQs" }), _jsx(Text, { size: "xs", color: "muted", children: "Rendered as an accordion on the category page and emitted as FAQPage structured data. Keep platform-wide answers in the global FAQ collection instead \u2014 two copies of one answer means the one that gets fixed is never the one being read." }), _jsx(Repeater, { rows: faqs, onChange: setFaqs, blank: () => ({ question: "", answer: "" }), addLabel: "Add an FAQ", max: 20, renderRow: (row, set) => (_jsxs(_Fragment, { children: [_jsx(Input, { label: "Question", value: row.question, onChange: (e) => set({ question: e.target.value }), placeholder: "Are these compatible with Burst launchers?" }), _jsx(Textarea, { value: row.answer, onChange: (e) => set({ answer: e.target.value }), rows: 3, placeholder: "Answer\u2026" })] })) })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Description templates" }), _jsxs(Text, { size: "xs", color: "muted", children: ["What the seller form offers when a listing is filed here. One per condition band \u2014 worldhobbyshop reuses exactly two across 1,195 listings. Slots like", " ", _jsx(Span, { family: "mono", children: "{{title}}" }), ",", " ", _jsx(Span, { family: "mono", children: "{{brand}}" }), ",", " ", _jsx(Span, { family: "mono", children: "{{condition}}" }), " and", " ", _jsx(Span, { family: "mono", children: "{{series}}" }), " are filled from the listing; an unresolved one becomes empty and is reported to the seller, never left as a literal brace pair."] }), _jsx(Repeater, { rows: templates, onChange: setTemplates, blank: () => ({ id: "", label: "", variant: "default", body: "" }), addLabel: "Add a template", max: 8, renderRow: (row, set) => (_jsxs(_Fragment, { children: [_jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: [_jsx(Input, { label: "Label", value: row.label, onChange: (e) => set({ label: e.target.value }), placeholder: "Pre-owned \u2014 condition as pictured" }), _jsx(Select, { label: "Condition band", value: row.variant, onChange: (e) => set({ variant: e.target.value }), options: TEMPLATE_VARIANT_OPTIONS })] }), _jsx(Textarea, { value: row.body, onChange: (e) => set({ body: e.target.value }), rows: 6, placeholder: "Important things to know about {{title}} \u2014 HTML permitted\u2026" }), _jsx(Text, { size: "xs", color: "muted", children: usedPlaceholders(row.body).length > 0
|
|
450
|
+
fields: ["contentBody", "highlightsText", "hasbroName"],
|
|
451
|
+
render: ({ values: v, onChange, errors }) => (_jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Category page body" }), _jsx(Textarea, { value: String(v.contentBody ?? ""), onChange: (e) => onChange({ contentBody: e.target.value }), rows: 8, placeholder: "400\u2013900 characters of keyword-rich HTML with links to sibling and parent categories\u2026" }), errors.contentBody ? (_jsx(Text, { size: "sm", variant: "error", role: "alert", children: errors.contentBody })) : (_jsx(Text, { size: "xs", color: "muted", children: "Rendered on the category page. Distinct from the short Description above, which also feeds the SEO meta and every card. 36 of worldhobbyshop's 46 categories carry one of these and it is their actual ranking mechanism; no Shopify competitor in the crawl has any." }))] }), _jsx(FieldInput, { name: "hasbroName", label: "Hasbro / international name", value: String(v.hasbroName ?? ""), onChange: (val) => onChange({ hasbroName: val }), placeholder: "e.g. Beyblade Burst Turbo", error: errors.hasbroName, hint: "Only when the Hasbro season name genuinely differs from the Takara-Tomy one \u2014 Cho-Z ships as Turbo, God as Evolution. Rendered on the category page as 'Also known as'. Leave blank if there is no divergence, or if you cannot confirm one: a guessed pairing reads as fact." }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Highlights \u2014 one per line" }), _jsx(Textarea, { value: String(v.highlightsText ?? ""), onChange: (e) => onChange({ highlightsText: e.target.value }), rows: 4, placeholder: "Genuine Takara Tomy stock\nChecked and photographed before listing" }), _jsx(Text, { size: "xs", color: "muted", children: "Short \"why shop this\" bullets, shown above the FAQs. Blank lines are dropped. Up to 12." })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Category FAQs" }), _jsx(Text, { size: "xs", color: "muted", children: "Rendered as an accordion on the category page and emitted as FAQPage structured data. Keep platform-wide answers in the global FAQ collection instead \u2014 two copies of one answer means the one that gets fixed is never the one being read." }), _jsx(Repeater, { rows: faqs, onChange: setFaqs, blank: () => ({ question: "", answer: "" }), addLabel: "Add an FAQ", max: 20, renderRow: (row, set) => (_jsxs(_Fragment, { children: [_jsx(Input, { label: "Question", value: row.question, onChange: (e) => set({ question: e.target.value }), placeholder: "Are these compatible with Burst launchers?" }), _jsx(Textarea, { value: row.answer, onChange: (e) => set({ answer: e.target.value }), rows: 3, placeholder: "Answer\u2026" })] })) })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Description templates" }), _jsxs(Text, { size: "xs", color: "muted", children: ["What the seller form offers when a listing is filed here. One per condition band \u2014 worldhobbyshop reuses exactly two across 1,195 listings. Slots like", " ", _jsx(Span, { family: "mono", children: "{{title}}" }), ",", " ", _jsx(Span, { family: "mono", children: "{{brand}}" }), ",", " ", _jsx(Span, { family: "mono", children: "{{condition}}" }), " and", " ", _jsx(Span, { family: "mono", children: "{{series}}" }), " are filled from the listing; an unresolved one becomes empty and is reported to the seller, never left as a literal brace pair."] }), _jsx(Repeater, { rows: templates, onChange: setTemplates, blank: () => ({ id: "", label: "", variant: "default", body: "" }), addLabel: "Add a template", max: 8, renderRow: (row, set) => (_jsxs(_Fragment, { children: [_jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: [_jsx(Input, { label: "Label", value: row.label, onChange: (e) => set({ label: e.target.value }), placeholder: "Pre-owned \u2014 condition as pictured" }), _jsx(Select, { label: "Condition band", value: row.variant, onChange: (e) => set({ variant: e.target.value }), options: TEMPLATE_VARIANT_OPTIONS })] }), _jsx(Textarea, { value: row.body, onChange: (e) => set({ body: e.target.value }), rows: 6, placeholder: "Important things to know about {{title}} \u2014 HTML permitted\u2026" }), _jsx(Text, { size: "xs", color: "muted", children: usedPlaceholders(row.body).length > 0
|
|
435
452
|
? `Slots used: ${usedPlaceholders(row.body).map((p) => `{{${p}}}`).join(", ")}`
|
|
436
453
|
: "No slots used — this body will read identically on every listing." })] })) })] })] })),
|
|
437
454
|
},
|
|
@@ -12,7 +12,7 @@ const __O = {
|
|
|
12
12
|
hidden: "overflow-hidden",
|
|
13
13
|
};
|
|
14
14
|
function NavItemRow({ item, idx, total, reorderPending, onMoveUp, onMoveDown, onVisibilityChange, onEdit, onDelete, }) {
|
|
15
|
-
return (_jsxs(Row, { surface: "default", padding: "inline", align: "center", gap: "3", children: [_jsxs(Stack, { className: "shrink-0 gap-[0.125rem]", children: [_jsx(Button, { variant: "ghost", type: "button", onClick: () => onMoveUp(idx), disabled: idx === 0 || reorderPending, className: "text-zinc-400 hover:text-
|
|
15
|
+
return (_jsxs(Row, { surface: "default", padding: "inline", align: "center", gap: "3", children: [_jsxs(Stack, { className: "shrink-0 gap-[0.125rem]", children: [_jsx(Button, { variant: "ghost", type: "button", onClick: () => onMoveUp(idx), disabled: idx === 0 || reorderPending, className: "text-zinc-400 hover:text-[var(--appkit-color-text)] disabled:opacity-30 leading-none text-[length:var(--appkit-text-xs)] p-[var(--appkit-space-0)] min-h-0 h-auto rounded-none", "aria-label": "Move up", children: "\u25B2" }), _jsx(Button, { variant: "ghost", type: "button", onClick: () => onMoveDown(idx), disabled: idx >= total - 1 || reorderPending, className: "text-zinc-400 hover:text-[var(--appkit-color-text)] disabled:opacity-30 leading-none text-[length:var(--appkit-text-xs)] p-[var(--appkit-space-0)] min-h-0 h-auto rounded-none", "aria-label": "Move down", children: "\u25BC" })] }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Text, { className: "truncate", color: "primary", size: "sm", weight: "medium", children: [item.parentId ? _jsx(Span, { className: "mr-1", color: "faint", children: "\u21B3" }) : null, item.label] }), _jsx(Text, { className: "truncate", color: "muted", size: "xs", children: item.href })] }), _jsx(Toggle, { checked: item.isVisible ?? true, onChange: (val) => onVisibilityChange(item.id, val), label: "" }), _jsx(RowActionMenu, { actions: [
|
|
16
16
|
{ label: ROW_ACTION_META[ROW_ACTION_ID.EDIT].label, onClick: () => onEdit(item) },
|
|
17
17
|
{ label: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label, destructive: true, onClick: () => onDelete(item) },
|
|
18
18
|
] })] }));
|