@mohasinac/appkit 4.53.0 → 4.53.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/scaffolds/AppShell.js +1 -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/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/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/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 +25 -0
- package/dist/_internal/server/features/stores/tabs.js +122 -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 +2 -1
- 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 +39 -15
- package/dist/constants/api-endpoints.js +28 -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 +4 -11
- 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 +113 -0
- package/dist/features/categories/components/BrandDetailPageView.js +19 -3
- package/dist/features/categories/components/BrandDetailTabs.js +10 -0
- package/dist/features/categories/components/CategoriesIndexListing.js +23 -8
- package/dist/features/categories/components/CategoryDetailPageView.js +141 -14
- package/dist/features/categories/components/CategoryDetailTabs.js +12 -0
- 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 +4 -9
- 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 +4 -9
- 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 +4 -9
- 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 +4 -11
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +1 -1
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +3 -1
- package/dist/features/price-index/components/PriceIndexToolView.js +92 -13
- package/dist/features/products/components/CompareOverlay.d.ts +18 -1
- package/dist/features/products/components/CompareOverlay.js +21 -2
- 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 +91 -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 +74 -15
- 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/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 +98 -0
- package/dist/features/products/repository/products.repository.js +235 -17
- 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 +39 -0
- package/dist/features/products/utils/group-member-image.js +3 -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/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/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 +280 -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 +372 -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 +441 -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 +9 -9
- package/dist/server.js +27 -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 +135 -26
- 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
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* WHY: Authored six-part procedures for content-discovery/authenticity-checker.
|
|
3
|
-
* WHAT:
|
|
3
|
+
* WHAT: 16 cases, keyed by full checklist id.
|
|
4
4
|
*
|
|
5
5
|
* 🛑 EVERY CASE IS WRITTEN AS BEFORE → AFTER, because this feature's whole
|
|
6
6
|
* failure mode is a confident, plausible, wrong answer returned with HTTP 200. A
|
|
7
7
|
* case reading "check the authenticity tool works" passes against all of these:
|
|
8
8
|
*
|
|
9
|
-
* 1. A
|
|
10
|
-
*
|
|
11
|
-
* in to run a lookup" at
|
|
9
|
+
* 1. A sign-in wall on the one surface whose whole purpose is being consulted
|
|
10
|
+
* while standing in front of the item — and before that, a two-state gate
|
|
11
|
+
* that flashed "sign in to run a lookup" at collectors who WERE signed in,
|
|
12
|
+
* because `SessionProvider` mounts `initialUser={null}`.
|
|
12
13
|
* 2. A verdict with no hedge. The engine can match a midfake batch code; the
|
|
13
14
|
* wrong version of that screen reads as an accusation against the seller.
|
|
14
15
|
* 3. One date printed as fact. Two decodings of a Metal Fight box code
|
|
@@ -20,8 +21,12 @@
|
|
|
20
21
|
* line as "Pegasis" while Hasbro prints "Pegasus", and an earlier draft of
|
|
21
22
|
* the checker flagged the TT spelling as trademark evasion.
|
|
22
23
|
* 7. Calling Funskool a fake. It is a licensed Indian importer.
|
|
23
|
-
* 8.
|
|
24
|
-
*
|
|
24
|
+
* 8. Picking a side on a code two compilations flatly disagree about, where
|
|
25
|
+
* either answer costs a real person money.
|
|
26
|
+
* 9. Reporting a code its own source calls authentic as a reproduction,
|
|
27
|
+
* because it appears in a list whose subject is a TYPEFACE.
|
|
28
|
+
* 10. Grading a Burst bey against the Metal Fight checklist in silence,
|
|
29
|
+
* because the generation selector arrives pre-set and nobody touched it.
|
|
25
30
|
*
|
|
26
31
|
* So each case names what the wrong answer LOOKED like, and several assert the
|
|
27
32
|
* presence of a hedge rather than the presence of a result.
|
|
@@ -39,13 +44,16 @@ export const authored = {
|
|
|
39
44
|
startPage: "/tools/authenticity",
|
|
40
45
|
steps: [
|
|
41
46
|
"Open /tools/authenticity signed out.",
|
|
42
|
-
"Confirm
|
|
47
|
+
"Confirm the LOOKUP FORM itself is there and usable — Model name, Code on the box, Brand, Generation, How it was sold, and a 'Check it' button.",
|
|
48
|
+
"Confirm there is no sign-in card and no 'Sign in to run a lookup' text anywhere on the page.",
|
|
49
|
+
"Enter A0813 as the code and submit, still signed out.",
|
|
50
|
+
"Confirm a real assessment comes back rather than an error or a prompt to sign in.",
|
|
43
51
|
"Scroll past it and confirm the generation table renders — six rows, each naming years, code prefixes and licensed marques.",
|
|
44
|
-
"
|
|
45
|
-
"Answer no if the page is only a sign-in prompt, or if the reference content is missing.",
|
|
52
|
+
"Answer no if any part of the lookup asks for an account.",
|
|
46
53
|
],
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
inputs: { code: "A0813" },
|
|
55
|
+
expectedBehaviour: "BEFORE: the lookup required an account, so a buyer standing in a shop with the box in their hand was asked to register first — on the one surface whose whole purpose is being consulted at that moment, and the person most in need of it is the one least likely to have an account. AFTER: the entire page is public. What makes that safe is the shape of the output, not a gate: there is no 'counterfeit' verdict, the harshest outcome is 'likely reproduction', and every claim is about a production batch or a named brand rather than about a seller.",
|
|
56
|
+
expectedUiState: "The form, then (after submitting) a verdict block, then the heading 'Which generation is it, and what does the box code mean' with six generation rows, then the universal checks, then 'Who is actually licensed'. No sign-in card at any point.",
|
|
49
57
|
expectedData: { generationRows: 6 },
|
|
50
58
|
endResult: "Nothing is changed; this case only reads.",
|
|
51
59
|
},
|
|
@@ -56,18 +64,18 @@ export const authored = {
|
|
|
56
64
|
"Sign in as a buyer.",
|
|
57
65
|
"Hard-reload /tools/authenticity (Ctrl+F5, not a soft navigation).",
|
|
58
66
|
"Watch the lookup area through the first second of the load.",
|
|
59
|
-
"Answer no if the words 'Sign in to run a lookup' appear at any point.",
|
|
67
|
+
"Answer no if the words 'Sign in to run a lookup' or 'Checking your session…' appear at any point, or if the form is not usable immediately.",
|
|
60
68
|
],
|
|
61
|
-
expectedBehaviour: "BEFORE: a two-state gate (`!user` → prompt) showed the sign-in card to signed-in users on every hard load, because the session provider mounts with a null user and resolves asynchronously
|
|
62
|
-
expectedUiState: "
|
|
69
|
+
expectedBehaviour: "BEFORE: a two-state gate (`!user` → prompt) showed the sign-in card to signed-in users on every hard load, because the session provider mounts with a null user and resolves asynchronously; a three-state gate with a 'Checking your session…' placeholder fixed the flash. AFTER: the component reads no session at all, so neither string exists and there is nothing to resolve — the form is interactive on first paint.",
|
|
70
|
+
expectedUiState: "The form, immediately. No session placeholder, no sign-in card.",
|
|
63
71
|
endResult: "The form is present after reload. Nothing persists.",
|
|
64
72
|
},
|
|
65
73
|
"checklist-content-discovery-authenticity-checker-midfake-code-reads-as-batch-not-accusation": {
|
|
66
74
|
roles: ["buyer"],
|
|
67
75
|
startPage: "/tools/authenticity",
|
|
68
76
|
steps: [
|
|
69
|
-
"
|
|
70
|
-
"Enter A0813 in 'Code on the box' and leave
|
|
77
|
+
"Open /tools/authenticity (no account needed).",
|
|
78
|
+
"Enter A0813 in 'Code on the box' and leave the other fields as they are.",
|
|
71
79
|
"Submit.",
|
|
72
80
|
"Read the verdict block and the caveat directly beneath it.",
|
|
73
81
|
"Answer no if the screen anywhere calls the item counterfeit, or accuses the seller, or omits the caveat.",
|
|
@@ -115,12 +123,12 @@ export const authored = {
|
|
|
115
123
|
"Enter 'Storm Pegasus' as the model name and BB-105 as the code. Submit.",
|
|
116
124
|
"Confirm the verdict is 'Discrepancy found' and a signal explains that Storm Pegasus is BB-28 while BB-105 is a different release.",
|
|
117
125
|
"Confirm the signal tells you a Random Booster or multi-pack is an innocent explanation.",
|
|
118
|
-
"Now
|
|
126
|
+
"Now set 'How it was sold' to 'In a Random Booster or a multi-pack' and submit again.",
|
|
119
127
|
"Confirm the verdict is no longer a discrepancy and a signal says a mismatch is EXPECTED for a Random Booster.",
|
|
120
128
|
"Answer no if the second submission still reports a discrepancy.",
|
|
121
129
|
],
|
|
122
130
|
inputs: { name: "Storm Pegasus", code: "BB-105", randomBooster: true },
|
|
123
|
-
expectedBehaviour: "BEFORE: flagging every name/code mismatch would flag a large class of genuine product, because a Random Booster box carries the BOOSTER's code and not the code of the bey inside — the reference table records several, e.g. Pisces DF145BS under F2810 marked 'Random Booster TT'. AFTER: the mismatch is reported as a question with the Random Booster explanation offered, and
|
|
131
|
+
expectedBehaviour: "BEFORE: flagging every name/code mismatch would flag a large class of genuine product, because a Random Booster box carries the BOOSTER's code and not the code of the bey inside — the reference table records several, e.g. Pisces DF145BS under F2810 marked 'Random Booster TT'. AFTER: the mismatch is reported as a question with the Random Booster explanation offered, and answering 'How it was sold' re-reads it as expected rather than suspicious. 🛑 That control is a THREE-option dropdown, not the checkbox it used to be: a checkbox could only express 'yes' and silence, and silence was read as a firm 'no' from a buyer who may simply not have the box in front of them.",
|
|
124
132
|
expectedUiState: "First submission: 'Discrepancy found' plus a row naming both codes. Second: not a discrepancy, plus a row reading 'Code does not match the model — but you said this is a Random Booster'.",
|
|
125
133
|
expectedData: { firstVerdict: "discrepancy-found", secondVerdict: "inconclusive" },
|
|
126
134
|
endResult: "Nothing is written either time.",
|
|
@@ -177,15 +185,15 @@ export const authored = {
|
|
|
177
185
|
roles: ["buyer"],
|
|
178
186
|
startPage: "/tools/authenticity",
|
|
179
187
|
steps: [
|
|
180
|
-
"Enter L9999 as the code and submit.",
|
|
181
|
-
"If asked which generation, choose Metal Fight.",
|
|
188
|
+
"Enter L9999 as the code and submit (Generation is already on Metal Fight).",
|
|
182
189
|
"Read the signal about the code.",
|
|
183
190
|
"Confirm it says the code is NOT RECORDED and explicitly that this does not mean fake.",
|
|
191
|
+
"Confirm it also states the size of the tables it searched, so 'not recorded' is reported against a known coverage rather than as a bare miss.",
|
|
184
192
|
"Answer no if an unrecorded code is presented as evidence of a fake.",
|
|
185
193
|
],
|
|
186
194
|
inputs: { code: "L9999", generation: "mfb" },
|
|
187
|
-
expectedBehaviour: "BEFORE: an absent lookup result reads as a negative result. The
|
|
188
|
-
expectedUiState: "A neutral signal row saying L9999 is not in
|
|
195
|
+
expectedBehaviour: "BEFORE: an absent lookup result reads as a negative result. The tables are collectors' compilations from boxes they personally held — thorough at 495 original and 18 midfake codes across 45 models and 6 regions, and still not complete; new genuine codes surface regularly and an unphotographed regional batch is simply missing. AFTER: an unmatched code produces a neutral signal saying 'not recorded', with that reasoning and the coverage stated.",
|
|
196
|
+
expectedUiState: "A neutral signal row saying L9999 is not in any of the tables, naming how many codes were searched, and saying an absent code means 'not recorded', not 'fake'. The verdict is 'Inconclusive', never 'Likely reproduction'.",
|
|
189
197
|
expectedData: { verdict: "inconclusive" },
|
|
190
198
|
endResult: "Nothing is changed.",
|
|
191
199
|
},
|
|
@@ -193,15 +201,16 @@ export const authored = {
|
|
|
193
201
|
roles: ["buyer"],
|
|
194
202
|
startPage: "/tools/authenticity",
|
|
195
203
|
steps: [
|
|
196
|
-
"Submit the form with every field empty.",
|
|
197
|
-
"Confirm the tool asks
|
|
198
|
-
"
|
|
204
|
+
"Submit the form with every text field empty, leaving Generation on its Metal Fight default.",
|
|
205
|
+
"Confirm the tool asks you to say what you are looking at rather than returning a verdict about an item it knows nothing about.",
|
|
206
|
+
"Confirm it still shows the Metal Fight checklist, since that much IS known.",
|
|
207
|
+
"Now set Generation to 'Not sure / something else', enter a name that matches nothing — 'zzzznope' — and submit.",
|
|
199
208
|
"Confirm a question appears asking which generation it is, with six options each naming years and example code prefixes.",
|
|
200
|
-
"Answer no if
|
|
209
|
+
"Answer no if an empty submission returns a verdict such as 'Nothing contradicts it', or if the generation question never appears when no generation is set.",
|
|
201
210
|
],
|
|
202
211
|
inputs: { name: "zzzznope" },
|
|
203
|
-
expectedBehaviour: "BEFORE: guessing a generation hands the user a confidently wrong checklist — tri-wing screws mean something on Burst and nothing on a 2001 plastic bey, and the box date-code table only covers the Metal series.
|
|
204
|
-
expectedUiState: "
|
|
212
|
+
expectedBehaviour: "BEFORE: guessing a generation hands the user a confidently wrong checklist — tri-wing screws mean something on Burst and nothing on a 2001 plastic bey, and the box date-code table only covers the Metal series. 🛑 And once the selector gained a Metal Fight default, every empty submission carried a generation, which was briefly enough to escape the 'nothing to go on' branch — so pressing Check with nothing typed answered 'Nothing contradicts it, and nothing confirms it either': a verdict, about no item, phrased as reassurance. AFTER: a generation alone is still nothing to go on, and the branch uses it for the checklist instead of treating it as an answer.",
|
|
213
|
+
expectedUiState: "Empty submission → a 'Need a bit more' badge, 'Tell me what you are looking at.', and the Metal Fight checks. With Generation cleared and an unmatched name → a question headed 'Which generation is it?' with six buttons.",
|
|
205
214
|
expectedData: { verdict: "needs-input" },
|
|
206
215
|
endResult: "Nothing is changed.",
|
|
207
216
|
},
|
|
@@ -213,31 +222,257 @@ export const authored = {
|
|
|
213
222
|
// A tester step is prose a human types into a devtools console: it cannot
|
|
214
223
|
// import a constant, and calling the real URL signed out IS the case.
|
|
215
224
|
// audit-hardcoded-api-routes-ok: literal URL inside a manual test procedure
|
|
216
|
-
"Run: await fetch('/api/tools/authenticity', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: 'A0813' }) }).then(r => r.
|
|
217
|
-
"Confirm the
|
|
218
|
-
|
|
219
|
-
"
|
|
225
|
+
"Run: await fetch('/api/tools/authenticity', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: 'A0813' }) }).then(r => r.json())",
|
|
226
|
+
"Confirm the response is a real assessment — it has a verdict and signals — and not a 401.",
|
|
227
|
+
// audit-hardcoded-api-routes-ok: literal URL inside a manual test procedure
|
|
228
|
+
"Now run the same fetch with an extra key the schema does not declare, e.g. body: JSON.stringify({ code: 'A0813', listingId: 'x' }), and read r.status.",
|
|
229
|
+
"Confirm THAT one is refused with a 400 rather than quietly ignoring the unknown key.",
|
|
230
|
+
"Answer no if the plain call is refused, or if the unknown-key call succeeds.",
|
|
220
231
|
],
|
|
221
232
|
inputs: { code: "A0813" },
|
|
222
|
-
expectedBehaviour: "BEFORE:
|
|
223
|
-
expectedUiState: "The console shows
|
|
224
|
-
expectedData: { status:
|
|
225
|
-
endResult: "Nothing is written;
|
|
233
|
+
expectedBehaviour: "BEFORE: the route required `auth: true`, so the endpoint refused an anonymous caller. The reasoning was that 'this matches a batch recorded as a midfake' is a quotable claim that should have an account behind it; in practice it put a registration wall in front of someone standing in a shop. AFTER: the route is public and the protection is in the output's shape instead — no 'counterfeit' rung, nothing said about a seller. The `.strict()` schema is still enforced, so an undeclared key is a 400 and never a confident assessment of a different, emptier question.",
|
|
234
|
+
expectedUiState: "The console shows an object with a verdict and a signals array for the first call, and status 400 for the second.",
|
|
235
|
+
expectedData: { status: 200 },
|
|
236
|
+
endResult: "Nothing is written; the engine does zero I/O.",
|
|
226
237
|
},
|
|
227
|
-
"checklist-content-discovery-authenticity-checker-json-ld-declares-
|
|
238
|
+
"checklist-content-discovery-authenticity-checker-json-ld-declares-no-false-gate": {
|
|
228
239
|
roles: ["guest"],
|
|
229
240
|
startPage: "/tools/authenticity",
|
|
230
241
|
steps: [
|
|
231
242
|
"Signed out, view source on /tools/authenticity (Ctrl+U, not devtools' rendered DOM).",
|
|
232
243
|
"Find the application/ld+json block with \"@type\": \"WebApplication\".",
|
|
233
|
-
"Confirm it carries isAccessibleForFree true
|
|
234
|
-
"
|
|
235
|
-
"
|
|
236
|
-
"Answer no if the
|
|
237
|
-
],
|
|
238
|
-
expectedBehaviour: "BEFORE:
|
|
239
|
-
expectedUiState: "A WebApplication JSON-LD block naming the tool
|
|
240
|
-
expectedData: {
|
|
244
|
+
"Confirm it carries isAccessibleForFree true.",
|
|
245
|
+
"Confirm there is NO hasPart entry, and no isAccessibleForFree false anywhere in the block.",
|
|
246
|
+
"Search the page source for 'authenticity-gated-tool' and confirm it does not appear.",
|
|
247
|
+
"Answer no if the markup still declares a paywalled region.",
|
|
248
|
+
],
|
|
249
|
+
expectedBehaviour: "BEFORE: the lookup needed an account, and the page declared that honestly — `isAccessibleForFree: true` overall plus a `hasPart` node carving out the lookup by cssSelector as not free (the same pattern the guest-price gate uses). AFTER: the gate is gone, so the declaration had to go with it. A paywall node a crawler can see is false is worse than no node at all, and the class it named was deleted rather than left in the DOM as a selector nothing references — 🛑 Root Cause #93 is exactly that pair drifting apart.",
|
|
250
|
+
expectedUiState: "A WebApplication JSON-LD block naming the tool with isAccessibleForFree true and no hasPart, plus a breadcrumb block.",
|
|
251
|
+
expectedData: { hasPartNodes: 0 },
|
|
241
252
|
endResult: "Nothing is changed; this case only reads.",
|
|
242
253
|
},
|
|
254
|
+
"checklist-content-discovery-authenticity-checker-defaults-to-metal-fight": {
|
|
255
|
+
roles: ["guest"],
|
|
256
|
+
startPage: "/tools/authenticity",
|
|
257
|
+
steps: [
|
|
258
|
+
"Open /tools/authenticity.",
|
|
259
|
+
"Read the Generation control WITHOUT touching it — confirm it already shows Metal Fight.",
|
|
260
|
+
"Enter L9999 as the code and submit without changing anything.",
|
|
261
|
+
"Confirm the result is produced immediately and is NOT a 'Which generation is it?' question.",
|
|
262
|
+
"Now enter B-174 as the code, leave Generation on Metal Fight, and submit.",
|
|
263
|
+
"Confirm a signal appears saying Metal Fight is selected but this looks like Burst, and that it tells you to change the selector.",
|
|
264
|
+
"Answer no if the default is absent, or if a Burst code is silently graded against the Metal Fight checklist with no mention of the mismatch.",
|
|
265
|
+
],
|
|
266
|
+
inputs: { code: "B-174" },
|
|
267
|
+
expectedBehaviour: "BEFORE: the tool opened with no generation and asked for one before it would say anything, which made the commonest question — 'what does this code on my Metal Fight box mean' — cost a selection first. Everything the box-code tables know is Metal-series. AFTER: the selector is pre-set to Metal Fight. 🛑 The risk a pre-set default creates is handled rather than ignored: a supplied generation still wins, because the user can see the box and the engine cannot, but a code or name implying a different generation raises a `generation-mismatch` signal naming both. A default that cannot be questioned is the thing to avoid.",
|
|
268
|
+
expectedUiState: "The Generation control reads 'Metal Fight (Metal Fusion / Masters / Fury) — 2008–2012' on load. With B-174 submitted, a neutral signal headed 'You have Metal Fight … selected, but this looks like Burst'.",
|
|
269
|
+
expectedData: { defaultGeneration: "mfb" },
|
|
270
|
+
endResult: "Nothing is changed.",
|
|
271
|
+
},
|
|
272
|
+
"checklist-content-discovery-authenticity-checker-contested-code-reports-the-disagreement": {
|
|
273
|
+
roles: ["guest"],
|
|
274
|
+
startPage: "/tools/authenticity",
|
|
275
|
+
steps: [
|
|
276
|
+
"Enter I2211 as the code and submit.",
|
|
277
|
+
"Confirm the verdict badge reads 'Discrepancy found' and NOT 'Likely reproduction' and NOT 'Consistent with an original'.",
|
|
278
|
+
"Confirm the headline says the sources contradict each other about this code.",
|
|
279
|
+
"Confirm a signal names BOTH sides — which compilation records it on genuine boxes and which lists it as a midfake.",
|
|
280
|
+
"Answer no if the screen picks a side, or presents either reading as the answer.",
|
|
281
|
+
],
|
|
282
|
+
inputs: { code: "I2211" },
|
|
283
|
+
expectedBehaviour: "BEFORE: merging three code compilations put four codes (I0511, I2211, I2611, J2712) in two tables at once, and whichever table the lookup scanned first would have won. That is a coin flip with a person's money on it in both directions — the midfake reading accuses whoever is holding a genuine box, the original reading reassures whoever is holding a fake. AFTER: contested codes live in their own table, are checked before the precedence scan, and are reported as the disagreement they are.",
|
|
284
|
+
expectedUiState: "A 'Discrepancy found' badge, the headline 'My sources contradict each other about this exact code.', and a signal row naming the model-by-model list and the 2021 reference post on one side and the midfake list on the other.",
|
|
285
|
+
expectedData: { verdict: "discrepancy-found" },
|
|
286
|
+
endResult: "Nothing is changed.",
|
|
287
|
+
},
|
|
288
|
+
"checklist-content-discovery-authenticity-checker-font-midfake-does-not-accuse": {
|
|
289
|
+
roles: ["guest"],
|
|
290
|
+
startPage: "/tools/authenticity",
|
|
291
|
+
steps: [
|
|
292
|
+
"Enter D0511 as the code and submit.",
|
|
293
|
+
"Confirm the verdict is 'Discrepancy found' — NOT 'Likely reproduction'.",
|
|
294
|
+
"Read the midfake signal and confirm it says the point is the TYPEFACE and that the source considers the code itself authentic.",
|
|
295
|
+
"Now enter H3011 instead and submit.",
|
|
296
|
+
"Confirm THAT one does reach 'Likely reproduction' and names the high-quality Sonokong fakes.",
|
|
297
|
+
"Answer no if D0511 is reported as a reproduction, or if H3011 is not.",
|
|
298
|
+
],
|
|
299
|
+
inputs: { code: "D0511" },
|
|
300
|
+
expectedBehaviour: "BEFORE: every row in the midfake table drove the harshest verdict the engine has. Three of the Chinese rows carry their own author's words that *the code itself should be authentic* and only the printed font differs, so that verdict would have been the engine asserting something no source did, about someone's property, in the worst available direction. AFTER: a midfake row the source hedged on is still a concern — it lands on 'resolve this before you pay' with the font comparison spelled out — but only an unhedged row reaches 'likely reproduction'.",
|
|
301
|
+
expectedUiState: "D0511 → 'Discrepancy found', with a signal explaining the typeface comparison. H3011 → 'Likely reproduction', with a signal about Korean boxes reproducing the importer panel and a Safety block.",
|
|
302
|
+
expectedData: { d0511Verdict: "discrepancy-found", h3011Verdict: "likely-reproduction" },
|
|
303
|
+
endResult: "Nothing is written either time.",
|
|
304
|
+
},
|
|
305
|
+
"checklist-content-discovery-authenticity-checker-regional-code-reads-as-regional": {
|
|
306
|
+
roles: ["guest"],
|
|
307
|
+
startPage: "/tools/authenticity",
|
|
308
|
+
steps: [
|
|
309
|
+
"Enter C2811 as the code and submit.",
|
|
310
|
+
"Confirm the signal names the regions the code is recorded in, and that Korea (Sonokong) is among them.",
|
|
311
|
+
"Now enter K2510 and submit.",
|
|
312
|
+
"Confirm that one names Middle East (NewBoy), and that its source note says the 2021 post could only call it 'seems legit' while the later list records it as a genuine New Boy release.",
|
|
313
|
+
"Answer no if a regional code comes back with no region, or is presented as unrecorded.",
|
|
314
|
+
],
|
|
315
|
+
inputs: { code: "C2811" },
|
|
316
|
+
expectedBehaviour: "BEFORE: the table was 90 Japanese-centric codes with no region data, so a buyer holding a Korean Sonokong or Middle-East New Boy box got 'not recorded' — which reads as a warning about a perfectly genuine regional release. AFTER: 495 codes across 45 models and 6 regions, and the signal says which region the code is recorded in. The five New Boy codes the 2021 post could only hedge about are resolved by the later list, carrying that history in their notes rather than being quietly upgraded.",
|
|
317
|
+
expectedUiState: "C2811 → a supporting signal reading 'Recorded on … releases' naming Korea (Sonokong). K2510 → the same shape naming Middle East (NewBoy), plus the 'seems legit' history.",
|
|
318
|
+
endResult: "Nothing is changed.",
|
|
319
|
+
},
|
|
320
|
+
"checklist-content-discovery-authenticity-checker-series-window-names-the-production-run": {
|
|
321
|
+
roles: ["guest"],
|
|
322
|
+
startPage: "/tools/authenticity",
|
|
323
|
+
steps: [
|
|
324
|
+
"Enter C2312 as the code and submit (Generation stays on Metal Fight).",
|
|
325
|
+
"Confirm a signal says the date falls inside a NAMED production window, and names it 'Metal Fury' with a start and end date.",
|
|
326
|
+
"Confirm the signal says this is about the CALENDAR DATE, not about the code itself.",
|
|
327
|
+
"Now enter A0813 instead and submit.",
|
|
328
|
+
"Confirm there is NO series-window signal for A0813 — its derived date (8 January 2013) is after every window the source states ends (Shogun Steel, 23 December 2012).",
|
|
329
|
+
"Answer no if the window signal is missing for C2312, or present for A0813.",
|
|
330
|
+
],
|
|
331
|
+
inputs: { code: "C2312" },
|
|
332
|
+
expectedBehaviour: "BEFORE: the reference post's own 'Additional Information' section — the only place any source states a MANUFACTURING WINDOW rather than one code's date (Metal Fusion/Masters/Fury, Shogun Steel) — was parsed for nothing; the tool could only ever say what a specific code decoded to. AFTER: every derivable date is checked against those four windows and named when it falls inside one. This is a fact about the calendar, so it fires even for C2312, which matches no code in any table.",
|
|
333
|
+
expectedUiState: "C2312 → a signal reading 'Falls inside the Metal Fury production window', naming '3 April 2011 – 1 April 2012'. A0813 → no such signal at all.",
|
|
334
|
+
endResult: "Nothing is changed.",
|
|
335
|
+
},
|
|
336
|
+
"checklist-content-discovery-authenticity-checker-temporal-context-for-an-unmatched-code": {
|
|
337
|
+
roles: ["guest"],
|
|
338
|
+
startPage: "/tools/authenticity",
|
|
339
|
+
steps: [
|
|
340
|
+
"Enter C2312 as the code and submit.",
|
|
341
|
+
"Confirm the main 'not in any of the tables' signal is still present and still says not-recorded is not fake.",
|
|
342
|
+
"Find a SEPARATE signal headed 'What the tables record from March 2012'.",
|
|
343
|
+
"Confirm it lists several genuine releases from that month BY NAME, and explicitly says 'Midfake batches: none recorded' rather than omitting that half.",
|
|
344
|
+
"Confirm the detail text explicitly says this is NOT a match for the code and your item matching none of the names is NOT evidence of anything.",
|
|
345
|
+
"Answer no if the month-context signal is missing, or if it reads as though it identifies the item.",
|
|
346
|
+
],
|
|
347
|
+
inputs: { code: "C2312" },
|
|
348
|
+
expectedBehaviour: "BEFORE: a code that matched nothing got exactly one sentence — 'not recorded, not fake' — and nothing else, even though the tables know a great deal about what else was moving through production the same month. AFTER: for a well-formed code that matches no table row, the engine also reports every genuine and midfake product name recorded from the SAME CALENDAR MONTH, explicitly framed as context rather than a match. 🛑 This only fires for an UNMATCHED code — a code that DID match something already has its own, richer product list, and piling month-context on top of that would dilute the one thing that actually matters (the match itself, or for a contested code, the disagreement).",
|
|
349
|
+
expectedUiState: "A signal titled 'What the tables record from March 2012' listing names like Diablo Nemesis X:D, Fang Leone 130W²D, Kreis Cygnus, Phantom Orion and others, ending '… and N more', followed by 'Midfake batches: none recorded.' and the context/not-evidence sentence.",
|
|
350
|
+
endResult: "Nothing is changed.",
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
/*
|
|
354
|
+
* ── Guards for the near-miss work (2026-10-19) ──────────────────────────
|
|
355
|
+
*
|
|
356
|
+
* 🛑 All four protect behaviour that is ALREADY CORRECT, and that is the
|
|
357
|
+
* point of them. A "did you mean" affordance is precisely the change that
|
|
358
|
+
* could turn an absent code into an accusation, and this tool's third rule
|
|
359
|
+
* is that a false counterfeit accusation against a real seller is worse
|
|
360
|
+
* than a missed one.
|
|
361
|
+
*
|
|
362
|
+
* Appended as a second export rather than merged into the map above, so the
|
|
363
|
+
* diff shows what the near-miss change added and the original 21 cases stay
|
|
364
|
+
* byte-identical.
|
|
365
|
+
*/
|
|
366
|
+
export const authoredNearMiss = {
|
|
367
|
+
"checklist-content-discovery-authenticity-checker-authenticity-unknown-code-is-not-fake": {
|
|
368
|
+
roles: ["guest"],
|
|
369
|
+
startPage: "/tools/authenticity",
|
|
370
|
+
steps: [
|
|
371
|
+
"Open /tools/authenticity.",
|
|
372
|
+
"Leave the model name empty.",
|
|
373
|
+
"Enter Z9999 in the box-code field — well-formed (one letter, four digits) but in no table.",
|
|
374
|
+
"Pick any generation.",
|
|
375
|
+
"Run the check.",
|
|
376
|
+
"Read the verdict chip, the headline and the caveat word for word.",
|
|
377
|
+
"Read every row under 'What I checked' and note each row's direction.",
|
|
378
|
+
],
|
|
379
|
+
inputs: { code: "Z9999" },
|
|
380
|
+
expectedBehaviour: "An unmatched code is NOT evidence in either direction. New genuine codes appear regularly, and counterfeiters use real codes anyway — this project's own crawl found 516 real B-xx codes on one counterfeit catalogue.",
|
|
381
|
+
expectedUiState: "The verdict is 'Inconclusive'. The code signal reads NEUTRAL, and its text says the code is not recorded rather than that the item is fake. No wording anywhere implies counterfeit, and no verdict more severe than inconclusive appears. Either of those is the failure.",
|
|
382
|
+
endResult: "Read-only; nothing persists.",
|
|
383
|
+
},
|
|
384
|
+
"checklist-content-discovery-authenticity-checker-authenticity-near-miss-suggests": {
|
|
385
|
+
roles: ["guest"],
|
|
386
|
+
startPage: "/tools/authenticity",
|
|
387
|
+
steps: [
|
|
388
|
+
"Open /tools/authenticity.",
|
|
389
|
+
"Find a code the tool recognises — A0813 is a known one.",
|
|
390
|
+
"Enter A0831 instead: the same characters with one pair transposed.",
|
|
391
|
+
"Run the check.",
|
|
392
|
+
"Read the suggestion line and note where on the page it sits.",
|
|
393
|
+
"Read every row under 'What I checked'.",
|
|
394
|
+
"Write down the verdict.",
|
|
395
|
+
"Run the check again with the transposition removed and compare the verdict.",
|
|
396
|
+
],
|
|
397
|
+
inputs: { nearMiss: "A0831", realCode: "A0813" },
|
|
398
|
+
expectedBehaviour: "A near-miss is INPUT CORRECTION, never evidence. It is ambiguous between a typo and a batch deliberately mimicking a real code, and an ambiguous signal must not become an accusation.",
|
|
399
|
+
expectedUiState: "A neutral line offers 'did you mean A0813?'. It sits OUTSIDE the 'What I checked' list and does NOT appear as a signal row. No evasion or counterfeit warning fires. The verdict is identical to what the same input produced before the suggestion existed.",
|
|
400
|
+
endResult: "Read-only; nothing persists.",
|
|
401
|
+
},
|
|
402
|
+
"checklist-content-discovery-authenticity-checker-authenticity-inconclusive-asks-for-more": {
|
|
403
|
+
roles: ["guest"],
|
|
404
|
+
startPage: "/tools/authenticity",
|
|
405
|
+
steps: [
|
|
406
|
+
"Open /tools/authenticity.",
|
|
407
|
+
"Leave the model name EMPTY.",
|
|
408
|
+
"Enter a plausible box code and pick a generation.",
|
|
409
|
+
"Run the check.",
|
|
410
|
+
"Read the caveat under the verdict word for word.",
|
|
411
|
+
"Now fill in a model name as well and run it again.",
|
|
412
|
+
"Compare the two caveats.",
|
|
413
|
+
],
|
|
414
|
+
expectedBehaviour: "Name-against-code agreement is one of the engine's strongest signals and cannot be computed with only one of the two. The tool already asks which generation and already asks which model on an ambiguous name; asking for the missing half is the same mechanism one input over.",
|
|
415
|
+
expectedUiState: "With no name, the caveat says that supplying the model name lets the tool cross-check it against the code, and offers asking the seller for a photo of the box bottom and the screws. With the name supplied, that prompt is gone. The VERDICT is 'Inconclusive' in both cases — only the caveat differs.",
|
|
416
|
+
endResult: "Read-only; nothing persists.",
|
|
417
|
+
},
|
|
418
|
+
"checklist-content-discovery-authenticity-checker-authenticity-evasion-warning-survives": {
|
|
419
|
+
roles: ["guest"],
|
|
420
|
+
startPage: "/tools/authenticity",
|
|
421
|
+
steps: [
|
|
422
|
+
"Open /tools/authenticity.",
|
|
423
|
+
"Type Pegasis in the model name field — note the spelling, 'sis' not 'sus'.",
|
|
424
|
+
"If a typeahead offers canonical names, read them but do NOT pick one yet.",
|
|
425
|
+
"Run the check and read every row under 'What I checked'.",
|
|
426
|
+
"Note whether a trademark-evasion or fuzzy-spelling signal fired.",
|
|
427
|
+
"Now clear the field, type Pegasis again, and this time PICK 'Storm Pegasus' from the typeahead.",
|
|
428
|
+
"Run the check again and compare the signals.",
|
|
429
|
+
],
|
|
430
|
+
inputs: { evasionSpelling: "Pegasis", canonical: "Storm Pegasus" },
|
|
431
|
+
expectedBehaviour: "This is the hazard the whole separation exists for. The site search matcher deliberately treats 'Pegasis' as a match for 'Storm Pegasus' — its alias table is derived FROM a counterfeit seller's 1,277 titles, on the insight that an evasion spelling is the same string a buyer mistypes. This tool treats that same spelling as EVIDENCE. If picking a canonical name replaced what the user typed, the warning would be silenced by construction and the tool would confirm a fake using the exact spelling that indicates one.",
|
|
432
|
+
expectedUiState: "The evasion/fuzzy signal fires in BOTH runs. A case asserting only 'the typeahead found it' would pass against precisely the bug this guards, so the second run is the one that matters.",
|
|
433
|
+
endResult: "Read-only; nothing persists.",
|
|
434
|
+
},
|
|
435
|
+
};
|
|
436
|
+
/*
|
|
437
|
+
* ── The name typeahead (2026-10-19) ──────────────────────────────────────
|
|
438
|
+
*
|
|
439
|
+
* `EntityTypeahead` is wired into the model-name field with
|
|
440
|
+
* `fillOnPick={false}` for exactly the reason `authenticity-name-typeahead`
|
|
441
|
+
* below checks: picking a suggestion must never replace what the engine
|
|
442
|
+
* analyses, or the evasion signal `authenticity-evasion-warning-survives`
|
|
443
|
+
* guards would be silenced by the very feature meant to help discovery.
|
|
444
|
+
*/
|
|
445
|
+
export const authoredTypeahead = {
|
|
446
|
+
"checklist-content-discovery-authenticity-checker-authenticity-name-typeahead": {
|
|
447
|
+
roles: ["guest"],
|
|
448
|
+
startPage: "/tools/authenticity",
|
|
449
|
+
steps: [
|
|
450
|
+
"Open /tools/authenticity.",
|
|
451
|
+
"Type a partial model name into the model-name field, e.g. Drago.",
|
|
452
|
+
"Read the suggestions offered beneath the field.",
|
|
453
|
+
"Click one of the suggestions.",
|
|
454
|
+
"Read the model-name field's actual text after clicking.",
|
|
455
|
+
"Read any confirmation line that appeared.",
|
|
456
|
+
"Run the check and read the result.",
|
|
457
|
+
],
|
|
458
|
+
inputs: { partial: "Drago" },
|
|
459
|
+
expectedBehaviour: "The typeahead is discovery only. Clicking a suggestion must NOT overwrite the field — if it did, the engine would analyse the canonical name instead of what the user actually typed, which is the mechanism `authenticity-evasion-warning-survives` exists to catch happening the other way.",
|
|
460
|
+
expectedUiState: "Canonical releases appear as the user types. After clicking one, the field still shows exactly what was typed (e.g. 'Drago'), and a muted confirmation line names the matched release separately. The check result is consistent with the typed text, not the clicked suggestion.",
|
|
461
|
+
endResult: "Read-only; nothing persists.",
|
|
462
|
+
},
|
|
463
|
+
"checklist-content-discovery-authenticity-checker-authenticity-box-code-still-instant": {
|
|
464
|
+
roles: ["guest"],
|
|
465
|
+
startPage: "/tools/authenticity",
|
|
466
|
+
steps: [
|
|
467
|
+
"Open /tools/authenticity and open DevTools → Network.",
|
|
468
|
+
"Clear the network log.",
|
|
469
|
+
"Enter a known box date code (e.g. A0813) in the code field, leaving the name field empty.",
|
|
470
|
+
"Run the check.",
|
|
471
|
+
"Read the network log for any request to a search endpoint or the search artifact.",
|
|
472
|
+
],
|
|
473
|
+
inputs: { code: "A0813" },
|
|
474
|
+
expectedBehaviour: "GUARD CASE. The box date-code table is bundled locally and resolves with zero network requests — this was true before the typeahead work and must stay true, because the two code paths (date-code lookup vs. name typeahead) are independent.",
|
|
475
|
+
expectedUiState: "The result renders immediately. The network log shows no fetch to the search artifact or any /api/search* endpoint.",
|
|
476
|
+
endResult: "Read-only; nothing persists.",
|
|
477
|
+
},
|
|
243
478
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* WHY: Authored six-part procedures for the categories-index tabs, the
|
|
3
3
|
* franchise unit and the bundle collage (2026-10-14).
|
|
4
|
-
* WHAT:
|
|
4
|
+
* WHAT: 9 cases, keyed by full checklist id.
|
|
5
5
|
*
|
|
6
6
|
* 🛑 EVERY CASE IS BEFORE → AFTER, because every defect this work produced
|
|
7
7
|
* rendered a page that looked fine:
|
|
@@ -9,17 +9,19 @@
|
|
|
9
9
|
* 1. The index rendered all 1,185 rows flat — six roots buried under 752
|
|
10
10
|
* part leaves. A case reading "the categories page loads" passes against
|
|
11
11
|
* that.
|
|
12
|
-
* 2.
|
|
13
|
-
* as well as the leaves, because their own `parentIds` also pass through
|
|
14
|
-
* a `*-parts` node. A list mixing "Attack Rings" with attack rings is not
|
|
15
|
-
* visibly wrong.
|
|
16
|
-
* 3. Seven franchises have no stock. Active, each is a card opening an empty
|
|
12
|
+
* 2. Seven franchises have no stock. Active, each is a card opening an empty
|
|
17
13
|
* shop; absent, seventeen `CharacterHotspot` links stay 404. Both look
|
|
18
14
|
* correct from any single page — which is why two cases here check the
|
|
19
15
|
* index and the detail page SEPARATELY.
|
|
20
|
-
*
|
|
16
|
+
* 3. Every bundle also carries a placeholder cover, so a collage tested
|
|
21
17
|
* after the cover never renders — and one grey tile is not "broken".
|
|
22
18
|
*
|
|
19
|
+
* 🛑 A fifth PARTS tab was removed 2026-10-20 and its case deleted with it.
|
|
20
|
+
* Parts live at `/parts/[classification]/[code]` and in the spares listing
|
|
21
|
+
* type; a flat tab over 752 leaves was the worse half of both. The
|
|
22
|
+
* product-detail Parts COMPOSITION tab is unrelated and still covered, on the
|
|
23
|
+
* `parts-database` page.
|
|
24
|
+
*
|
|
23
25
|
* So each case counts an exact number or compares two surfaces. Several carry
|
|
24
26
|
* a nonsense control, because a filter that returns everything looks identical
|
|
25
27
|
* to a filter that works.
|
|
@@ -47,49 +49,36 @@ export const authored = {
|
|
|
47
49
|
expectedData: { rootCards: 6 },
|
|
48
50
|
endResult: "Nothing is changed; this case only reads.",
|
|
49
51
|
},
|
|
50
|
-
"checklist-content-discovery-category-tabs-franchise-bundles-
|
|
52
|
+
"checklist-content-discovery-category-tabs-franchise-bundles-four-tabs-no-all-no-parts": {
|
|
51
53
|
roles: ["guest"],
|
|
52
54
|
startPage: "/categories",
|
|
53
55
|
steps: [
|
|
54
56
|
"Open /categories.",
|
|
55
57
|
"Read the tab bar.",
|
|
56
|
-
"Confirm there are exactly
|
|
58
|
+
"Confirm there are exactly four tabs: Categories, Brands, Franchises, Bundles.",
|
|
57
59
|
"Answer no if a tab labelled 'All' is present.",
|
|
60
|
+
"Answer no if a tab labelled 'Parts' is present.",
|
|
61
|
+
"Open a product detail page for a named-model Beyblade — for example /categories/category-ace-dragon-sting-charge-zan reached through Spinning Tops, or any complete-top listing — and confirm its own 'Parts' / 'What it is made of' composition section is STILL there. That is a different tab on a different surface and must not have been removed.",
|
|
58
62
|
],
|
|
59
|
-
expectedBehaviour: "BEFORE: the tabs were All / Categories / Brands, and 'All' was the DEFAULT — it is what rendered the 1,185 rows. AFTER:
|
|
60
|
-
expectedUiState: "
|
|
61
|
-
expectedData: { tabCount:
|
|
62
|
-
endResult: "Nothing is changed; this case only reads.",
|
|
63
|
-
},
|
|
64
|
-
"checklist-content-discovery-category-tabs-franchise-bundles-parts-tab-is-leaves-only": {
|
|
65
|
-
roles: ["guest"],
|
|
66
|
-
startPage: "/categories",
|
|
67
|
-
steps: [
|
|
68
|
-
"Open /categories and click the Parts tab.",
|
|
69
|
-
"Read the total the pager reports.",
|
|
70
|
-
"Confirm it reads 752 and not 772.",
|
|
71
|
-
"Scan the first page and confirm every card is an individual PART — '1B', 'Ten Heavy', 'Neo Right SG'.",
|
|
72
|
-
"Answer no if a classification GROUP appears as a card: 'Attack Rings', 'Weight Disks', 'Performance Tips', 'Bit Chips'.",
|
|
73
|
-
],
|
|
74
|
-
expectedBehaviour: "BEFORE: the tab returned 772, because the predicate matched any row whose ancestry passes a `*-parts` node — which is true of the 20 classification NODES as well as the leaves beneath them. The list mixed groups with their own contents and was not visibly wrong. AFTER: leaves only, 752. A part is a leaf by itself; a classification is a branch you reach by drilling down.",
|
|
75
|
-
expectedUiState: "752 parts reported. Every card is a single part; no classification group appears.",
|
|
76
|
-
expectedData: { partsTotal: 752, classificationNodesShown: 0 },
|
|
63
|
+
expectedBehaviour: "BEFORE the first fix: the tabs were All / Categories / Brands, and 'All' was the DEFAULT — it is what rendered the 1,185 rows. BEFORE this one: a fifth 'Parts' tab listed all 752 part leaves flat, duplicating /parts and the spares listing type with a worse version of both. AFTER: four tabs, no 'All' and no 'Parts'. The part leaves still exist and are still reached by drilling into a `*-parts` node; only the flat shortcut tab is gone. The product-level composition tab is untouched — removing that would have deleted the part database's own reason to exist.",
|
|
64
|
+
expectedUiState: "Four tabs, named exactly as above. No 'All', no 'Parts'. A model listing still shows its composition section.",
|
|
65
|
+
expectedData: { tabCount: 4, hasAllTab: false, hasPartsTab: false },
|
|
77
66
|
endResult: "Nothing is changed; this case only reads.",
|
|
78
67
|
},
|
|
79
68
|
"checklist-content-discovery-category-tabs-franchise-bundles-tabs-do-not-overlap": {
|
|
80
69
|
roles: ["guest"],
|
|
81
70
|
startPage: "/categories",
|
|
82
71
|
steps: [
|
|
83
|
-
"Open /categories and note the total each of the
|
|
84
|
-
"Expect 6, 28, 8,
|
|
72
|
+
"Open /categories and note the total each of the four tabs reports: Categories, Brands, Franchises, Bundles.",
|
|
73
|
+
"Expect 6, 28, 8, 5.",
|
|
85
74
|
"Pick one brand by name on the Brands tab — for example Takara-Tomy.",
|
|
86
|
-
"Open each of the other
|
|
75
|
+
"Open each of the other three tabs and search for that same name.",
|
|
87
76
|
"Answer no if it appears on any tab other than Brands.",
|
|
88
77
|
"Repeat once with a bundle name taken from the Bundles tab.",
|
|
89
78
|
],
|
|
90
79
|
expectedBehaviour: "Brands, franchises and bundles are all tier-0 rows, so a predicate that tested the tier before the discriminator would list every one of them under Categories as well. The type check has to come first. A row on two tabs double-counts the catalogue and makes the totals unreadable.",
|
|
91
|
-
expectedUiState: "Tab totals read 6 / 28 / 8 /
|
|
92
|
-
expectedData: { categories: 6, brands: 28, franchises: 8,
|
|
80
|
+
expectedUiState: "Tab totals read 6 / 28 / 8 / 5. The sampled brand and bundle each appear on exactly one tab.",
|
|
81
|
+
expectedData: { categories: 6, brands: 28, franchises: 8, bundles: 5 },
|
|
93
82
|
endResult: "Nothing is changed; this case only reads.",
|
|
94
83
|
},
|
|
95
84
|
"checklist-content-discovery-category-tabs-franchise-bundles-franchise-index-hides-unstocked": {
|
|
@@ -149,4 +138,37 @@ export const authored = {
|
|
|
149
138
|
expectedData: { bundleCards: 5, cardsWithCollage: 5 },
|
|
150
139
|
endResult: "Nothing is changed; this case only reads.",
|
|
151
140
|
},
|
|
141
|
+
"checklist-content-discovery-category-tabs-franchise-bundles-tabs-collapse-on-narrow-viewport": {
|
|
142
|
+
roles: ["guest"],
|
|
143
|
+
startPage: "/categories",
|
|
144
|
+
steps: [
|
|
145
|
+
"Resize the viewport to a phone width, around 390px.",
|
|
146
|
+
"Open /categories.",
|
|
147
|
+
"Look at the tab row reading Categories / Brands / Franchises / Bundles.",
|
|
148
|
+
"BEFORE a fix landed 2026-10-19: this was a plain row of `<Button variant=\"ghost\">`s with a hand-written underline — no `role=\"tablist\"`, no overflow handling, and on a narrow viewport the later tabs ran past the right edge with no scrollbar and no visible way to reach them.",
|
|
149
|
+
"AFTER: the row is `<OverflowRow>`. Confirm it has collapsed into a single trigger button showing the active tab's name plus a chevron.",
|
|
150
|
+
"Click the trigger. Confirm a searchable dropdown opens listing all four tabs.",
|
|
151
|
+
"Pick a different tab from the dropdown and confirm the page re-filters to it.",
|
|
152
|
+
],
|
|
153
|
+
expectedBehaviour: "A row of controls that does not fit its container must become a trigger that opens a searchable list, not a row the visitor has to discover can be scrolled. `<OverflowRow>` is the one primitive this project built for exactly this shape (see CLAUDE.md § Overflowing Pill & Tab Rows) — the old row predates it and was never migrated. 🛑 Four tabs can still overflow 390px: the threshold is MEASURED width, never a count (that is why `TabsList`'s old `items.length > 5` rule was deleted), so dropping the fifth tab does not retire this case.",
|
|
154
|
+
expectedUiState: "At 390px: one trigger button, not four separate tab buttons. Opening it shows all four tabs in a dropdown, each clickable, with the active one marked.",
|
|
155
|
+
expectedData: { tabsVisibleAsRowAt390px: false, collapsedTriggerPresent: true },
|
|
156
|
+
endResult: "Nothing is changed; this case only reads.",
|
|
157
|
+
},
|
|
158
|
+
"checklist-content-discovery-category-tabs-franchise-bundles-tabs-hover-changes-ink": {
|
|
159
|
+
roles: ["guest"],
|
|
160
|
+
startPage: "/categories",
|
|
161
|
+
steps: [
|
|
162
|
+
"Open /categories on a desktop-width viewport (at least 1024px, so the tabs render as a row rather than a collapsed trigger).",
|
|
163
|
+
"Pick a tab that is NOT currently active.",
|
|
164
|
+
"BEFORE a fix landed 2026-10-19: this row (and several others — the notification bell, the admin sidebar/nav/header, the mobile bottom-nav item) carried two competing `hover:text-*` classes on one element, one of which resolved to the element's own RESTING colour. Hovering therefore changed nothing, which reads as 'hover is broken' rather than as a styling bug.",
|
|
165
|
+
"Hover the mouse pointer over the inactive tab's label without clicking.",
|
|
166
|
+
"Compare its text colour before and during the hover.",
|
|
167
|
+
"Move the pointer away and confirm the colour returns to the resting shade.",
|
|
168
|
+
],
|
|
169
|
+
expectedBehaviour: "Every hover state must visibly differ from the resting state. A hover ink that equals the resting ink is indistinguishable from no hover handler at all.",
|
|
170
|
+
expectedUiState: "The inactive tab's text colour visibly changes while hovered, and reverts when the pointer leaves.",
|
|
171
|
+
expectedData: { hoverChangesTextColor: true },
|
|
172
|
+
endResult: "Nothing is changed; this case only reads.",
|
|
173
|
+
},
|
|
152
174
|
};
|
|
@@ -36,12 +36,13 @@ export const authored = {
|
|
|
36
36
|
startPage: "/parts",
|
|
37
37
|
steps: [
|
|
38
38
|
"Open /parts.",
|
|
39
|
+
"Confirm the page heading reads 'Beyblade parts gallery'.",
|
|
39
40
|
"Count the classification tiles.",
|
|
40
41
|
"Confirm every tile reads as a part NAME in sentence case — 'Performance Tip', 'Fusion Wheel', 'Bit Chip' — and not as a URL slug like 'performance-tip' or 'bit-chip'.",
|
|
41
42
|
"Open any one tile and confirm it loads a page rather than 404ing.",
|
|
42
43
|
],
|
|
43
|
-
expectedBehaviour: "BEFORE: there was no parts
|
|
44
|
-
expectedUiState: "Exactly 20 tiles, each labelled in sentence case, each a working link.",
|
|
44
|
+
expectedBehaviour: "BEFORE: there was no parts reference at all, so a buyer reading '105RF' on a listing had nowhere to learn that 105 is a spin-track height and RF is a rubber flat tip. AFTER: 20 classifications, each a real page, under the heading 'Beyblade parts gallery'. The labels come from a compile-checked map rather than being title-cased from the slug, because 'Bit Chip' is not 'bit-chip' title-cased and 'Armour' is not 'armour'.",
|
|
45
|
+
expectedUiState: "The heading reads 'Beyblade parts gallery'. Exactly 20 tiles, each labelled in sentence case, each a working link.",
|
|
45
46
|
expectedData: { classificationTiles: 20 },
|
|
46
47
|
endResult: "Nothing is changed; this case only reads.",
|
|
47
48
|
},
|