@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,18 +1,18 @@
|
|
|
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
3
|
import { getClassifiedForDetail } from "../../../_internal/server/features/classified/data.js";
|
|
4
|
-
const CLS_BREADCRUMB_LINK = "hover:text-primary-600 transition-colors";
|
|
4
|
+
const CLS_BREADCRUMB_LINK = "hover:text-primary-600 dark:hover:text-primary-400 transition-colors";
|
|
5
5
|
const CLS_CLASSIFIED_BADGE = "inline-block rounded-full bg-cyan-100 dark:bg-cyan-900/30 px-[var(--appkit-space-2-5)] py-[var(--appkit-space-0-5)] text-cyan-700 dark:text-cyan-300";
|
|
6
6
|
import { ROUTES } from "../../../next/routing/route-map.js";
|
|
7
7
|
import { getDefaultCurrency } from "../../../core/baseline-resolver.js";
|
|
8
8
|
import { formatCurrency } from "../../../utils/number.formatter.js";
|
|
9
9
|
import { normalizeRichTextHtml } from "../../../utils/string.formatter.js";
|
|
10
10
|
import { safeDisplayName } from "../../../security/index.js";
|
|
11
|
-
import { Container, Div, GatedPrice, Heading, Main, Nav, RichText, Row, Section, Span, Stack, Text,
|
|
11
|
+
import { Container, Div, GatedPrice, Heading, Main, Nav, RichText, Row, Section, Span, Stack, Text, SpecTable, } from "../../../ui/index.js";
|
|
12
12
|
import { PreOrderDetailView } from "../../products/components/PreOrderDetailView.js";
|
|
13
13
|
import { ProductGalleryClient } from "../../products/components/ProductGalleryClient.js";
|
|
14
14
|
import { ProductTabsShell } from "../../products/components/ProductTabsShell.js";
|
|
15
|
-
import {
|
|
15
|
+
import { toCustomSectionTabs } from "../../products/components/CustomSectionTabContent.js";
|
|
16
16
|
import { ShareButton } from "../../products/components/ShareButton.js";
|
|
17
17
|
import { HistoryTracker } from "../../history/components/HistoryTracker.js";
|
|
18
18
|
import { RelatedItemsSection } from "../../products/components/RelatedItemsSection.js";
|
|
@@ -54,7 +54,6 @@ export async function ClassifiedDetailPageView({ slug, initialProduct, renderOff
|
|
|
54
54
|
const storeSlug = (typeof p.storeSlug === "string" ? p.storeSlug : null) || (typeof p.storeId === "string" ? p.storeId : null);
|
|
55
55
|
const storeHref = storeSlug ? String(ROUTES.PUBLIC.STORE_DETAIL(storeSlug)) : null;
|
|
56
56
|
const specs = Array.isArray(p.specifications) ? p.specifications : [];
|
|
57
|
-
const customSections = Array.isArray(p.customSections) ? p.customSections : [];
|
|
58
57
|
const descriptionHtml = toDescriptionHtml(p.description);
|
|
59
58
|
const [{ relatedItems, relatedByBrand, relatedByTags, relatedByStore }, groups, siteSettings] = await Promise.all([
|
|
60
59
|
computeRelatedItems(product),
|
|
@@ -94,11 +93,7 @@ export async function ClassifiedDetailPageView({ slug, initialProduct, renderOff
|
|
|
94
93
|
price: price ?? undefined,
|
|
95
94
|
storeId: typeof p.storeId === "string" ? p.storeId : undefined,
|
|
96
95
|
storeName: storeName ?? undefined,
|
|
97
|
-
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CLASSIFIED), className: CLS_BREADCRUMB_LINK, children: "Classifieds" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => _jsx(ProductGalleryClient, { images: images, productName: title }), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_CLASSIFIED_BADGE, children: "Classified" }), meta?.negotiable && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: "Negotiable" })), meta?.acceptsShipping && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: "Shipping available" }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), price !== null && (_jsx(Text, { size: "2xl", weight: "bold", color: "primary", children: _jsx(GatedPrice, { children: formatCurrency(price, currency) }) })), location && (_jsxs(Row, { color: "muted", textSize: "sm", align: "center", gap: "xs", children: [_jsx(Span, { children: "\uD83D\uDCCD" }), _jsx(Span, { children: location }), meta?.meetupArea?.pincode && _jsxs(Span, { children: ["\u00B7 ", meta.meetupArea.pincode] })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" })), safeSeller && (_jsx(Div, { border: "subtle", rounded: "xl", surface: "muted", padding: "md", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Sold by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined, specsContent: specs.length > 0 ? (_jsx(
|
|
98
|
-
id: s.id,
|
|
99
|
-
label: s.title,
|
|
100
|
-
content: _jsx(CustomSectionTabContent, { section: s }),
|
|
101
|
-
})) })), renderBuyBar: () => (_jsx(Stack, { id: "classified-contact-bar", className: "p-[var(--appkit-space-5)]", border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: offersAvailable && offerBounds ? (_jsxs(_Fragment, { children: [renderOfferAction({
|
|
96
|
+
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CLASSIFIED), className: CLS_BREADCRUMB_LINK, children: "Classifieds" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => _jsx(ProductGalleryClient, { images: images, productName: title }), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_CLASSIFIED_BADGE, children: "Classified" }), meta?.negotiable && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: "Negotiable" })), meta?.acceptsShipping && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: "Shipping available" }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), price !== null && (_jsx(Text, { size: "2xl", weight: "bold", color: "primary", children: _jsx(GatedPrice, { children: formatCurrency(price, currency) }) })), location && (_jsxs(Row, { color: "muted", textSize: "sm", align: "center", gap: "xs", children: [_jsx(Span, { children: "\uD83D\uDCCD" }), _jsx(Span, { children: location }), meta?.meetupArea?.pincode && _jsxs(Span, { children: ["\u00B7 ", meta.meetupArea.pincode] })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" })), safeSeller && (_jsx(Div, { border: "subtle", rounded: "xl", surface: "muted", padding: "md", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Sold by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined, specsContent: specs.length > 0 ? (_jsx(SpecTable, { rows: specs })) : undefined, customTabs: toCustomSectionTabs(p.customSections) })), renderBuyBar: () => (_jsx(Stack, { id: "classified-contact-bar", className: "p-[var(--appkit-space-5)]", border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: offersAvailable && offerBounds ? (_jsxs(_Fragment, { children: [renderOfferAction({
|
|
102
97
|
productId: String(product.id),
|
|
103
98
|
price: price,
|
|
104
99
|
currency,
|
|
@@ -1,2 +1,34 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* The legal entity and support channels, passed in by the consumer page.
|
|
4
|
+
*
|
|
5
|
+
* 🛑 PROPS, not an import. This is a `"use client"` component inside appkit,
|
|
6
|
+
* so it cannot reach the consumer's `appkit.config.js` — and it must not, or
|
|
7
|
+
* appkit would carry a second definition of facts whose entire point is to
|
|
8
|
+
* have one owner (Root Cause #81). Every field is a plain string or `null`,
|
|
9
|
+
* so the whole object crosses the server/client boundary as serialisable data.
|
|
10
|
+
*
|
|
11
|
+
* Everything optional is `null` when undecided and renders a visible
|
|
12
|
+
* `[to be confirmed]` marker — never an invented support number.
|
|
13
|
+
*/
|
|
14
|
+
export interface ContactBusinessInfo {
|
|
15
|
+
legalName: string;
|
|
16
|
+
proprietor?: string | null;
|
|
17
|
+
structure?: string | null;
|
|
18
|
+
gstin?: string | null;
|
|
19
|
+
/** Pre-formatted lines — the consumer owns the address formatting. */
|
|
20
|
+
addressLines?: readonly string[];
|
|
21
|
+
supportEmail?: string | null;
|
|
22
|
+
supportPhone?: string | null;
|
|
23
|
+
/** Ready-to-use wa.me URL, or null. Built by the consumer so the digit-stripping rule lives in one place. */
|
|
24
|
+
whatsAppLink?: string | null;
|
|
25
|
+
whatsAppDisplay?: string | null;
|
|
26
|
+
supportHours?: string | null;
|
|
27
|
+
/** Community group invite — marketing, not support. Optional. */
|
|
28
|
+
communityUrl?: string | null;
|
|
29
|
+
grievanceHref?: string | null;
|
|
30
|
+
}
|
|
31
|
+
export interface ContactPageViewProps {
|
|
32
|
+
business: ContactBusinessInfo;
|
|
33
|
+
}
|
|
34
|
+
export declare function ContactPageView({ business }: ContactPageViewProps): React.JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Container, Div, Heading, Main, Section, Stack, Text } from "../../../ui/index.js";
|
|
3
|
+
import { Anchor, Container, Div, Heading, Main, PendingValue, Section, Stack, Text } from "../../../ui/index.js";
|
|
4
4
|
import { AdSlot } from "../../homepage/components/AdSlot.js";
|
|
5
5
|
import { ContactForm } from "./ContactForm.js";
|
|
6
6
|
import { ContactInfoSidebar } from "./ContactInfoSidebar.js";
|
|
@@ -9,7 +9,53 @@ import { CONTACT_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
|
9
9
|
async function submitContact(data) {
|
|
10
10
|
await apiClient.post(CONTACT_ENDPOINTS.SUBMIT, data);
|
|
11
11
|
}
|
|
12
|
-
export function ContactPageView() {
|
|
12
|
+
export function ContactPageView({ business }) {
|
|
13
|
+
/*
|
|
14
|
+
* Built from `business` rather than hardcoded. Every one of these was a
|
|
15
|
+
* literal in this file until 2026-10-19 — `support@letitrip.in`,
|
|
16
|
+
* `+91 89196 65811`, `Mon-Sat · 10:00 AM - 7:00 PM IST` — which meant the
|
|
17
|
+
* contact page could disagree with `siteSettings.contact`, with the FAQ
|
|
18
|
+
* variables and with the footer, and did.
|
|
19
|
+
*
|
|
20
|
+
* A channel with no value is OMITTED rather than rendered pending: a contact
|
|
21
|
+
* sidebar listing "Email: [to be confirmed]" as a clickable row is worse
|
|
22
|
+
* than a sidebar that does not offer email. The pending markers belong on
|
|
23
|
+
* the legal-identity block below, where absence is the thing worth seeing.
|
|
24
|
+
*/
|
|
25
|
+
const infoItems = [
|
|
26
|
+
business.supportEmail
|
|
27
|
+
? {
|
|
28
|
+
icon: "📧",
|
|
29
|
+
label: "Email",
|
|
30
|
+
value: business.supportEmail,
|
|
31
|
+
href: `mailto:${business.supportEmail}`,
|
|
32
|
+
}
|
|
33
|
+
: null,
|
|
34
|
+
business.supportPhone
|
|
35
|
+
? {
|
|
36
|
+
icon: "📞",
|
|
37
|
+
label: "Phone",
|
|
38
|
+
value: business.supportPhone,
|
|
39
|
+
href: `tel:${business.supportPhone.replace(/[^\d+]/g, "")}`,
|
|
40
|
+
}
|
|
41
|
+
: null,
|
|
42
|
+
business.whatsAppLink
|
|
43
|
+
? {
|
|
44
|
+
icon: "💬",
|
|
45
|
+
label: "WhatsApp",
|
|
46
|
+
value: business.whatsAppDisplay ?? "Message us",
|
|
47
|
+
href: business.whatsAppLink,
|
|
48
|
+
}
|
|
49
|
+
: null,
|
|
50
|
+
business.communityUrl
|
|
51
|
+
? {
|
|
52
|
+
icon: "👥",
|
|
53
|
+
label: "WhatsApp Community",
|
|
54
|
+
value: "Join the group",
|
|
55
|
+
href: business.communityUrl,
|
|
56
|
+
}
|
|
57
|
+
: null,
|
|
58
|
+
].filter((i) => i !== null);
|
|
13
59
|
return (_jsx(Main, { children: _jsx(Section, { padding: "y-2xl", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "section", children: [_jsxs(Stack, { gap: "3", children: [_jsx(Heading, { level: 1, size: "3xl", weight: "semibold", color: "primary", children: "Contact Us" }), _jsx(Text, { color: "muted", children: "Questions, support requests, or partnership inquiries." })] }), _jsx(AdSlot, { id: "detail-below-gallery", className: "mb-2" }), _jsxs(Div, { layout: "grid", gap: "6", className: "grid-cols-12", children: [_jsx(Div, { className: "col-span-12 lg:col-span-8", children: _jsx(ContactForm, { onSubmit: submitContact, labels: {
|
|
14
60
|
title: "Send us a message",
|
|
15
61
|
nameLabel: "Your Name",
|
|
@@ -17,30 +63,14 @@ export function ContactPageView() {
|
|
|
17
63
|
subjectLabel: "Subject",
|
|
18
64
|
messageLabel: "Message",
|
|
19
65
|
submitButton: "Send Message",
|
|
20
|
-
} }) }), _jsx(Div, { className: "col-span-12 lg:col-span-4", children: _jsx(ContactInfoSidebar, { infoItems:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
label: "
|
|
30
|
-
value: "+91 89196 65811",
|
|
31
|
-
href: "https://wa.me/918919665811",
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
icon: "👥",
|
|
35
|
-
label: "WhatsApp Community",
|
|
36
|
-
value: "Join the group",
|
|
37
|
-
href: "https://chat.whatsapp.com/JOlbdSwhVWYDKujolcBZDr",
|
|
38
|
-
},
|
|
39
|
-
], labels: {
|
|
40
|
-
title: "Support",
|
|
41
|
-
businessHoursLabel: "Business Hours",
|
|
42
|
-
businessHoursValue: "Mon-Sat · 10:00 AM - 7:00 PM IST",
|
|
43
|
-
responseTimeLabel: "Response Time",
|
|
44
|
-
responseTimeValue: "Usually within 24 hours",
|
|
45
|
-
} }) })] }), _jsx(AdSlot, { id: "detail-below-price" })] }) }) }) }));
|
|
66
|
+
} }) }), _jsx(Div, { className: "col-span-12 lg:col-span-4", children: _jsxs(Stack, { gap: "lg", children: [_jsx(ContactInfoSidebar, { infoItems: infoItems, labels: {
|
|
67
|
+
title: "Support",
|
|
68
|
+
businessHoursLabel: "Business Hours",
|
|
69
|
+
// 🛑 No invented fallback. "Mon-Sat · 10:00 AM - 7:00 PM
|
|
70
|
+
// IST" was hardcoded here and matched nothing anyone had
|
|
71
|
+
// committed to.
|
|
72
|
+
businessHoursValue: business.supportHours ?? "[to be confirmed]",
|
|
73
|
+
responseTimeLabel: "Response Time",
|
|
74
|
+
responseTimeValue: "Usually within 24 hours",
|
|
75
|
+
} }), _jsxs(Div, { surface: "muted", padding: "card", rounded: "lg", border: "default", children: [_jsx(Heading, { level: 2, size: "sm", weight: "semibold", className: "mb-3 uppercase tracking-wide", color: "muted", children: "Business details" }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", children: business.legalName }), business.structure ? (_jsx(Text, { size: "sm", color: "muted", children: business.structure })) : null, business.proprietor ? (_jsxs(Text, { size: "sm", color: "muted", children: ["Proprietor: ", business.proprietor] })) : null, (business.addressLines ?? []).map((line) => (_jsx(Text, { size: "sm", color: "muted", children: line }, line))), _jsxs(Text, { size: "sm", color: "muted", className: "mt-2", children: ["GSTIN:", " ", _jsx(PendingValue, { value: business.gstin, label: "GSTIN" })] }), business.grievanceHref ? (_jsx(Text, { size: "sm", className: "mt-2", children: _jsx(Anchor, { href: business.grievanceHref, children: "Grievance redressal" }) })) : null] })] })] }) })] }), _jsx(AdSlot, { id: "detail-below-price" })] }) }) }) }));
|
|
46
76
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
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
3
|
import { getDigitalCodeForDetail } from "../../../_internal/server/features/digital-code/data.js";
|
|
4
|
-
const CLS_BREADCRUMB_LINK = "hover:text-primary-600 transition-colors";
|
|
4
|
+
const CLS_BREADCRUMB_LINK = "hover:text-primary-600 dark:hover:text-primary-400 transition-colors";
|
|
5
5
|
const CLS_DIGITALCODE_BADGE = "inline-block rounded-full bg-violet-100 dark:bg-violet-900/30 px-[var(--appkit-space-2-5)] py-[var(--appkit-space-0-5)] text-violet-700 dark:text-violet-300";
|
|
6
6
|
import { ROUTES } from "../../../next/routing/route-map.js";
|
|
7
7
|
import { getDefaultCurrency } from "../../../core/baseline-resolver.js";
|
|
8
8
|
import { formatCurrency } from "../../../utils/number.formatter.js";
|
|
9
9
|
import { normalizeRichTextHtml } from "../../../utils/string.formatter.js";
|
|
10
10
|
import { safeDisplayName } from "../../../security/index.js";
|
|
11
|
-
import { Div, Container, GatedPrice, Heading, Main, Nav, RichText, Row, Section, Span, Stack, Text,
|
|
11
|
+
import { Div, Container, GatedPrice, Heading, Main, Nav, RichText, Row, Section, Span, Stack, Text, SpecTable, } from "../../../ui/index.js";
|
|
12
12
|
import { PreOrderDetailView } from "../../products/components/PreOrderDetailView.js";
|
|
13
13
|
import { ProductGalleryClient } from "../../products/components/ProductGalleryClient.js";
|
|
14
14
|
import { ProductTabsShell } from "../../products/components/ProductTabsShell.js";
|
|
15
|
-
import {
|
|
15
|
+
import { toCustomSectionTabs } from "../../products/components/CustomSectionTabContent.js";
|
|
16
16
|
import { ShareButton } from "../../products/components/ShareButton.js";
|
|
17
17
|
import { HistoryTracker } from "../../history/components/HistoryTracker.js";
|
|
18
18
|
import { RelatedItemsSection } from "../../products/components/RelatedItemsSection.js";
|
|
@@ -52,7 +52,6 @@ export async function DigitalCodeDetailPageView({ slug, initialProduct, renderPr
|
|
|
52
52
|
const storeSlug = (typeof p.storeSlug === "string" ? p.storeSlug : null) || (typeof p.storeId === "string" ? p.storeId : null);
|
|
53
53
|
const storeHref = storeSlug ? String(ROUTES.PUBLIC.STORE_DETAIL(storeSlug)) : null;
|
|
54
54
|
const specs = Array.isArray(p.specifications) ? p.specifications : [];
|
|
55
|
-
const customSections = Array.isArray(p.customSections) ? p.customSections : [];
|
|
56
55
|
const descriptionHtml = toDescriptionHtml(p.description);
|
|
57
56
|
const [{ relatedItems, relatedByBrand, relatedByTags, relatedByStore }, groups, initialReviews] = await Promise.all([
|
|
58
57
|
computeRelatedItems(product),
|
|
@@ -65,11 +64,7 @@ export async function DigitalCodeDetailPageView({ slug, initialProduct, renderPr
|
|
|
65
64
|
price: price ?? undefined,
|
|
66
65
|
storeId: typeof p.storeId === "string" ? p.storeId : undefined,
|
|
67
66
|
storeName: storeName ?? undefined,
|
|
68
|
-
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.DIGITAL_CODES), className: CLS_BREADCRUMB_LINK, children: "Digital Codes" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => _jsx(ProductGalleryClient, { images: images, productName: title }), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_DIGITALCODE_BADGE, children: "Digital Code" }), codesLeft > 0 ? (_jsxs(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: [codesLeft, " available"] })) : (_jsx(Span, { size: "xs", weight: "semibold", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "danger-surface", color: "error", children: "Sold out" }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), price !== null && (_jsx(Text, { size: "2xl", weight: "bold", color: "primary", children: _jsx(GatedPrice, { children: formatCurrency(price, currency) }) })), meta?.redemptionInstructions && (_jsx(Text, { size: "xs", color: "muted", children: meta.redemptionInstructions })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" })), safeSeller && (_jsx(Div, { border: "subtle", rounded: "xl", surface: "muted", padding: "md", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Sold by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined, specsContent: specs.length > 0 ? (_jsx(
|
|
69
|
-
id: s.id,
|
|
70
|
-
label: s.title,
|
|
71
|
-
content: _jsx(CustomSectionTabContent, { section: s }),
|
|
72
|
-
})) })), renderBuyBar: () => (_jsx(Div, { id: "digital-code-buy-bar", children: renderPrimaryActions?.({
|
|
67
|
+
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.DIGITAL_CODES), className: CLS_BREADCRUMB_LINK, children: "Digital Codes" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => _jsx(ProductGalleryClient, { images: images, productName: title }), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_DIGITALCODE_BADGE, children: "Digital Code" }), codesLeft > 0 ? (_jsxs(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: [codesLeft, " available"] })) : (_jsx(Span, { size: "xs", weight: "semibold", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "danger-surface", color: "error", children: "Sold out" }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), price !== null && (_jsx(Text, { size: "2xl", weight: "bold", color: "primary", children: _jsx(GatedPrice, { children: formatCurrency(price, currency) }) })), meta?.redemptionInstructions && (_jsx(Text, { size: "xs", color: "muted", children: meta.redemptionInstructions })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" })), safeSeller && (_jsx(Div, { border: "subtle", rounded: "xl", surface: "muted", padding: "md", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Sold by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined, specsContent: specs.length > 0 ? (_jsx(SpecTable, { rows: specs })) : undefined, reviewsContent: _jsx(ReviewsListingPanel, { source: { kind: "product", productId: product.id }, stateMode: "local", context: "listing", initialData: initialReviews, emptyLabel: "No reviews yet \u2014 be the first to review this product." }), customTabs: toCustomSectionTabs(p.customSections) })), renderBuyBar: () => (_jsx(Div, { id: "digital-code-buy-bar", children: renderPrimaryActions?.({
|
|
73
68
|
productId: String(product.id),
|
|
74
69
|
productSlug: String(p.slug ?? product.id),
|
|
75
70
|
productTitle: title,
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Container, Div, Heading, Section, Span, Stack, Text, } from "../../../ui/index.js";
|
|
4
4
|
import { MediaImage } from "../../media/MediaImage.js";
|
|
5
5
|
import { GroupMemberPicker } from "../../products/components/GroupMemberPicker.js";
|
|
6
|
+
import { readGroupRules } from "../../../_internal/shared/features/products/group-rules.js";
|
|
6
7
|
const __O = { hidden: "overflow-hidden" };
|
|
7
8
|
/** Human label per theme — mirrors GROUP_THEME_TITLE on the carousel. */
|
|
8
9
|
const THEME_LABEL = {
|
|
@@ -13,5 +14,12 @@ const THEME_LABEL = {
|
|
|
13
14
|
generic: "Grouped listing",
|
|
14
15
|
};
|
|
15
16
|
export function GroupedListingDetailView({ group, members }) {
|
|
16
|
-
return (_jsx(Section, { padding: "section", children: _jsx(Container, { children: _jsxs(Stack, { gap: "comfortable", children: [group.coverImage && (_jsx(Div, { className: `relative h-48 w-full md:h-64 ${__O.hidden}`, rounded: "xl", surface: "muted", children: _jsx(MediaImage, { src: group.coverImage, alt: group.title, size: "banner" }) })), _jsxs(Stack, { gap: "xs", children: [_jsx(Span, { size: "xs", color: "muted", transform: "uppercase", weight: "semibold", children: THEME_LABEL[group.groupTheme] }), _jsx(Heading, { level: 1, children: group.title }), group.description && (_jsx(Text, { color: "muted", children: group.description })), _jsxs(Text, { size: "sm", color: "muted", children: [members.length, " item", members.length === 1 ? "" : "s", " in this group"] })] }), members.length > 0 ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "lg", children: "Pick what you want" }), _jsx(Text, { size: "sm", color: "muted", children: "Choose a quantity for each item \u2014 they'll go into your cart as a single line you can edit later." }), _jsx(GroupMemberPicker, { groupId: group.id, groupSource: "grouped-listing", members: members
|
|
17
|
+
return (_jsx(Section, { padding: "section", children: _jsx(Container, { children: _jsxs(Stack, { gap: "comfortable", children: [group.coverImage && (_jsx(Div, { className: `relative h-48 w-full md:h-64 ${__O.hidden}`, rounded: "xl", surface: "muted", children: _jsx(MediaImage, { src: group.coverImage, alt: group.title, size: "banner" }) })), _jsxs(Stack, { gap: "xs", children: [_jsx(Span, { size: "xs", color: "muted", transform: "uppercase", weight: "semibold", children: THEME_LABEL[group.groupTheme] }), _jsx(Heading, { level: 1, children: group.title }), group.description && (_jsx(Text, { color: "muted", children: group.description })), _jsxs(Text, { size: "sm", color: "muted", children: [members.length, " item", members.length === 1 ? "" : "s", " in this group"] })] }), members.length > 0 ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "lg", children: "Pick what you want" }), _jsx(Text, { size: "sm", color: "muted", children: "Choose a quantity for each item \u2014 they'll go into your cart as a single line you can edit later." }), _jsx(GroupMemberPicker, { groupId: group.id, groupSource: "grouped-listing", members: members,
|
|
18
|
+
/*
|
|
19
|
+
* Off the group document this view already loaded. The picker
|
|
20
|
+
* and `addGroupLineToCart` run the SAME `checkGroupRules`, so
|
|
21
|
+
* passing them here only changes WHEN the buyer learns a
|
|
22
|
+
* selection is invalid — not whether it is refused.
|
|
23
|
+
*/
|
|
24
|
+
rules: readGroupRules(group) })] })) : (_jsx(Text, { color: "muted", children: "Nothing in this group is available right now." }))] }) }) }));
|
|
17
25
|
}
|
|
@@ -39,6 +39,11 @@ export interface GroupedListingDocument extends BaseDocument {
|
|
|
39
39
|
/** Computed from activeMemberCount < minActiveMembers. */
|
|
40
40
|
visibilityStatus: GroupVisibility;
|
|
41
41
|
isActive: boolean;
|
|
42
|
+
groupMinMembers?: number;
|
|
43
|
+
groupMaxMembers?: number;
|
|
44
|
+
groupMaxUnitsPerMember?: number;
|
|
45
|
+
groupRequiredMemberIds?: string[];
|
|
46
|
+
groupDiscountPercent?: number;
|
|
42
47
|
isFeatured: boolean;
|
|
43
48
|
storeId?: string;
|
|
44
49
|
brandSlug?: string;
|
|
@@ -2,17 +2,17 @@ import { z } from "zod";
|
|
|
2
2
|
import type { HomepageSectionDocument } from "../schemas";
|
|
3
3
|
import type { FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
4
4
|
export declare const createSectionSchema: z.ZodObject<{
|
|
5
|
-
type: z.ZodEnum<["
|
|
5
|
+
type: z.ZodEnum<[import("../schemas").SectionType, ...import("../schemas").SectionType[]]>;
|
|
6
6
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
7
7
|
order: z.ZodDefault<z.ZodNumber>;
|
|
8
8
|
config: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
|
|
9
9
|
}, "strip", z.ZodTypeAny, {
|
|
10
|
-
type: "
|
|
10
|
+
type: import("../schemas").SectionType;
|
|
11
11
|
enabled: boolean;
|
|
12
12
|
order: number;
|
|
13
13
|
config?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
14
14
|
}, {
|
|
15
|
-
type: "
|
|
15
|
+
type: import("../schemas").SectionType;
|
|
16
16
|
enabled?: boolean | undefined;
|
|
17
17
|
order?: number | undefined;
|
|
18
18
|
config?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
import type { CollectionCardsSectionConfig } from "../schemas/firestore";
|
|
2
|
+
import type { CategoryItem } from "../../categories/types";
|
|
3
|
+
import type { StoreListItem } from "../../stores/types";
|
|
2
4
|
export interface CollectionCardsSectionProps {
|
|
3
5
|
config: CollectionCardsSectionConfig;
|
|
6
|
+
/**
|
|
7
|
+
* Pre-fetched items for the entry types that are actually wired — see the
|
|
8
|
+
* module docstring below for which ones and why those three specifically.
|
|
9
|
+
*/
|
|
10
|
+
sectionData?: {
|
|
11
|
+
categories?: CategoryItem[];
|
|
12
|
+
brands?: CategoryItem[];
|
|
13
|
+
stores?: StoreListItem[];
|
|
14
|
+
};
|
|
4
15
|
}
|
|
5
16
|
/**
|
|
6
|
-
* Renders a single homepage strip that mixes multiple resource types
|
|
7
|
-
* stores, blog, etc.)
|
|
17
|
+
* Renders a single homepage strip that mixes multiple resource types
|
|
18
|
+
* (products, stores, blog, etc.), driven entirely from config.
|
|
8
19
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
20
|
+
* 🛑 `categories` / `brands` / `stores` entries render REAL data as of
|
|
21
|
+
* 2026-10-19 — reusing `sectionData` that `MarketplaceHomepageView` already
|
|
22
|
+
* fetches for the dedicated `categories`/`brands`/`stores` sections, so this
|
|
23
|
+
* costs zero extra round-trips (Rule #6). Those three were chosen first
|
|
24
|
+
* because they are the PERMANENT axes: populated independently of the
|
|
25
|
+
* product catalogue's size, so a `collection-cards` section built from them
|
|
26
|
+
* can never go empty the way a `"products"`/`"auctions"` entry could.
|
|
27
|
+
*
|
|
28
|
+
* Every other entry type (`products`, `auctions`, `pre-orders`, `events`,
|
|
29
|
+
* `blog-posts`, `reviews`) still renders the honest placeholder — a real
|
|
30
|
+
* "not wired yet" state is correct here, not a bug to paper over with
|
|
31
|
+
* fabricated data, and the earlier revision's "data wiring lands with the
|
|
32
|
+
* bundle/raffle work" message survives per-entry rather than as a blanket
|
|
33
|
+
* claim about the whole section.
|
|
12
34
|
*/
|
|
13
|
-
export declare function CollectionCardsSection({ config, }: CollectionCardsSectionProps): Promise<import("react").JSX.Element | null>;
|
|
35
|
+
export declare function CollectionCardsSection({ config, sectionData, }: CollectionCardsSectionProps): Promise<import("react").JSX.Element | null>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import Link from "next/link.js";
|
|
3
|
-
import { Container, Heading, Row, Section, Span, Stack, Text, } from "../../../ui/index.js";
|
|
3
|
+
import { Container, Div, Grid, Heading, Row, Section, Span, Stack, Text, } from "../../../ui/index.js";
|
|
4
|
+
import { MediaImage } from "../../media/MediaImage.js";
|
|
5
|
+
import { ROUTES } from "../../../next/routing/route-map.js";
|
|
4
6
|
const DEFAULT_ITEMS_PER_ROW = 4;
|
|
5
7
|
const DEFAULT_MAX_ITEMS = 12;
|
|
6
8
|
function entryLabel(entry) {
|
|
@@ -19,14 +21,25 @@ function entryLabel(entry) {
|
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
|
-
* Renders a single homepage strip that mixes multiple resource types
|
|
23
|
-
* stores, blog, etc.)
|
|
24
|
+
* Renders a single homepage strip that mixes multiple resource types
|
|
25
|
+
* (products, stores, blog, etc.), driven entirely from config.
|
|
24
26
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
27
|
+
* 🛑 `categories` / `brands` / `stores` entries render REAL data as of
|
|
28
|
+
* 2026-10-19 — reusing `sectionData` that `MarketplaceHomepageView` already
|
|
29
|
+
* fetches for the dedicated `categories`/`brands`/`stores` sections, so this
|
|
30
|
+
* costs zero extra round-trips (Rule #6). Those three were chosen first
|
|
31
|
+
* because they are the PERMANENT axes: populated independently of the
|
|
32
|
+
* product catalogue's size, so a `collection-cards` section built from them
|
|
33
|
+
* can never go empty the way a `"products"`/`"auctions"` entry could.
|
|
34
|
+
*
|
|
35
|
+
* Every other entry type (`products`, `auctions`, `pre-orders`, `events`,
|
|
36
|
+
* `blog-posts`, `reviews`) still renders the honest placeholder — a real
|
|
37
|
+
* "not wired yet" state is correct here, not a bug to paper over with
|
|
38
|
+
* fabricated data, and the earlier revision's "data wiring lands with the
|
|
39
|
+
* bundle/raffle work" message survives per-entry rather than as a blanket
|
|
40
|
+
* claim about the whole section.
|
|
28
41
|
*/
|
|
29
|
-
export async function CollectionCardsSection({ config, }) {
|
|
42
|
+
export async function CollectionCardsSection({ config, sectionData, }) {
|
|
30
43
|
const title = config.title ?? "Featured Collections";
|
|
31
44
|
const subtitle = config.subtitle;
|
|
32
45
|
const collections = Array.isArray(config.collections) ? config.collections : [];
|
|
@@ -37,5 +50,27 @@ export async function CollectionCardsSection({ config, }) {
|
|
|
37
50
|
if (collections.length === 0) {
|
|
38
51
|
return null;
|
|
39
52
|
}
|
|
40
|
-
|
|
53
|
+
const wiredEntries = collections.filter((e) => e.type === "categories" || e.type === "brands" || e.type === "stores");
|
|
54
|
+
const unwiredEntries = collections.filter((e) => e.type !== "categories" && e.type !== "brands" && e.type !== "stores");
|
|
55
|
+
return (_jsx(Section, { padding: "y-2xl", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, size: "2xl", weight: "semibold", color: "primary", children: title }), subtitle ? (_jsx(Text, { size: "sm", color: "muted", children: subtitle })) : null] }), showTabs ? (_jsx(Row, { gap: "sm", wrap: true, children: collections.slice(0, 3).map((entry, idx) => (_jsx(Span, { border: "default", padding: "x-sm", className: "py-[0.25rem]", rounded: "full", color: "muted", size: "xs", weight: "medium", children: entryLabel(entry) }, `${entry.type}-${idx}`))) })) : null, wiredEntries.map((entry, idx) => {
|
|
56
|
+
const rows = entry.type === "categories"
|
|
57
|
+
? (sectionData?.categories ?? [])
|
|
58
|
+
.slice(0, entry.limit ?? maxItems)
|
|
59
|
+
.map((c) => ({ slug: c.slug, name: c.name, image: c.display?.coverImage }))
|
|
60
|
+
: entry.type === "brands"
|
|
61
|
+
? (sectionData?.brands ?? [])
|
|
62
|
+
.slice(0, entry.limit ?? maxItems)
|
|
63
|
+
.map((b) => ({ slug: b.slug, name: b.name, image: b.display?.coverImage }))
|
|
64
|
+
: (sectionData?.stores ?? [])
|
|
65
|
+
.slice(0, entry.limit ?? maxItems)
|
|
66
|
+
.map((s) => ({ slug: s.storeSlug, name: s.storeName, image: s.storeLogoURL }));
|
|
67
|
+
if (rows.length === 0)
|
|
68
|
+
return null;
|
|
69
|
+
const hrefFor = (slug) => entry.type === "categories"
|
|
70
|
+
? String(ROUTES.PUBLIC.CATEGORY_DETAIL(slug))
|
|
71
|
+
: entry.type === "brands"
|
|
72
|
+
? String(ROUTES.PUBLIC.BRAND_DETAIL(slug))
|
|
73
|
+
: String(ROUTES.PUBLIC.STORE_DETAIL(slug));
|
|
74
|
+
return (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: entryLabel(entry) }), _jsx(Grid, { cols: "categoryCards", gap: "md", children: rows.map((row) => (_jsx(Link, { href: hrefFor(row.slug), className: "group rounded-xl border border-[var(--appkit-color-border)] transition-all hover:border-primary-300 hover:shadow-sm dark:hover:border-primary-600", children: _jsxs(Stack, { align: "center", gap: "xs", padding: "sm", children: [_jsx(Div, { className: "h-16 w-16", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: row.image, alt: row.name, size: "thumbnail" }) }), _jsx(Text, { size: "xs", weight: "medium", className: "line-clamp-2", children: row.name })] }) }, row.slug))) })] }, `${entry.type}-${idx}`));
|
|
75
|
+
}), unwiredEntries.length > 0 && (_jsx(Stack, { align: "start", gap: "sm", className: "border-dashed text-left", border: "strong", paddingY: "y-3xl", paddingX: "x-lg", rounded: "2xl", children: _jsxs(Text, { size: "sm", color: "muted", children: [unwiredEntries.map(entryLabel).join(", "), " \u2014 up to ", maxItems, " ", "items, ", itemsPerRow, " per row when wired \u2014 data wiring lands with the bundle/raffle work."] }) })), cta ? (_jsxs(Link, { href: cta.href, className: "text-[length:var(--appkit-text-sm)] font-medium text-primary hover:underline", children: [cta.label, " \u2192"] })) : null] }) }) }));
|
|
41
76
|
}
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import type { IconKey } from "../../../ui";
|
|
3
|
+
/**
|
|
4
|
+
* Promoted to a registered `SectionType` ("how-it-works") 2026-10-19 — see
|
|
5
|
+
* `HowItWorksSectionConfig` in `schemas/firestore.ts`, which this prop shape
|
|
6
|
+
* mirrors exactly (Firestore config must be JSON-serialisable; a function
|
|
7
|
+
* value cannot round-trip through a document write).
|
|
8
|
+
*
|
|
9
|
+
* `renderIcon?: (props) => ReactNode` was the blocker: a function prop has no
|
|
10
|
+
* representation in a Firestore document. Replaced with `iconKey?: IconKey`,
|
|
11
|
+
* rendered through the shared `<Icon>` registry primitive (Root Cause #80) —
|
|
12
|
+
* the same indirection `TrustBadge.iconKey` already uses for this exact
|
|
13
|
+
* problem. `accentFrom` is dropped: declared on the old type, never read
|
|
14
|
+
* anywhere in this component.
|
|
15
|
+
*/
|
|
2
16
|
export interface HowItWorksStep {
|
|
3
17
|
number: number;
|
|
4
18
|
title: string;
|
|
5
19
|
desc: string;
|
|
6
|
-
|
|
7
|
-
className?: string;
|
|
8
|
-
}) => React.ReactNode;
|
|
9
|
-
accentFrom?: string;
|
|
20
|
+
iconKey?: IconKey;
|
|
10
21
|
iconColor?: string;
|
|
11
22
|
iconBg?: string;
|
|
12
23
|
badgeBg?: string;
|
|
@@ -16,8 +27,14 @@ export interface HowItWorksSectionProps {
|
|
|
16
27
|
subtitle?: string;
|
|
17
28
|
pillLabel?: string;
|
|
18
29
|
ctaLabel?: string;
|
|
19
|
-
|
|
30
|
+
/**
|
|
31
|
+
* A link, not a handler — `onCtaClick?: () => void` could not survive a
|
|
32
|
+
* Firestore round-trip either. Renders as an `<Anchor>`, matching how every
|
|
33
|
+
* other promoted section's CTA resolves (`EventRafflesSectionConfig`'s
|
|
34
|
+
* `cta`, `FeaturedBundlesSectionConfig`'s `viewMoreLabel` pattern, etc.).
|
|
35
|
+
*/
|
|
36
|
+
ctaLink?: string;
|
|
20
37
|
steps: HowItWorksStep[];
|
|
21
38
|
className?: string;
|
|
22
39
|
}
|
|
23
|
-
export declare function HowItWorksSection({ title, subtitle, pillLabel, ctaLabel,
|
|
40
|
+
export declare function HowItWorksSection({ title, subtitle, pillLabel, ctaLabel, ctaLink, steps, className, }: HowItWorksSectionProps): React.JSX.Element;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { THEMED_TEXT_PRIMARY, THEMED_TEXT_SECONDARY } from "../../../_internal/shared/styles/themed.js";
|
|
5
|
-
import { Button, Div, Grid, Heading, Row, Section, Span, Text } from "../../../ui/index.js";
|
|
5
|
+
import { Anchor, Button, Div, Grid, Heading, Icon, Row, Section, Span, Text } from "../../../ui/index.js";
|
|
6
6
|
// --- Constants ---------------------------------------------------------------
|
|
7
7
|
const CLS_VISIBLE = "opacity-100 translate-y-0";
|
|
8
8
|
const STEP_DELAYS = ["delay-0", "delay-150", "delay-300", "delay-[450ms]"];
|
|
@@ -16,10 +16,10 @@ function StepCard({ step, visible, delayClass, }) {
|
|
|
16
16
|
"transition-all duration-300",
|
|
17
17
|
delayClass,
|
|
18
18
|
visible ? CLS_VISIBLE : "opacity-0 translate-y-8",
|
|
19
|
-
].join(" "), "data-section": "howitworkssection-div-340", children: [_jsx(Span, { gradient: "brand", size: "7xl", family: "display", className: "absolute top-4 right-5 opacity-10 select-none pointer-events-none leading-none", "aria-hidden": "true", children: step.number }), _jsx(Row, { textWeight: "bold", textSize: "sm", className: `relative z-10 w-10 h-10 ${badgeBg} text-white mb-5`, align: "center", justify: "center", rounded: "full", shadow: "md", children: step.number }), step.
|
|
19
|
+
].join(" "), "data-section": "howitworkssection-div-340", children: [_jsx(Span, { gradient: "brand", size: "7xl", family: "display", className: "absolute top-4 right-5 opacity-10 select-none pointer-events-none leading-none", "aria-hidden": "true", children: step.number }), _jsx(Row, { textWeight: "bold", textSize: "sm", className: `relative z-10 w-10 h-10 ${badgeBg} text-white mb-5`, align: "center", justify: "center", rounded: "full", shadow: "md", children: step.number }), step.iconKey && (_jsx(Row, { className: `relative z-10 w-14 h-14 ${iconBg} mb-4 border border-white/80 dark:border-[var(--appkit-color-border)]`, align: "center", justify: "center", rounded: "2xl", children: _jsx(Span, { className: `${iconColor}`, "aria-hidden": "true", children: _jsx(Icon, { name: step.iconKey, size: "lg" }) }) })), _jsx(Heading, { level: 3, className: `relative z-10 ${THEMED_TEXT_PRIMARY} mb-2`, size: "base", weight: "semibold", children: step.title }), _jsx(Text, { className: `relative z-10 ${THEMED_TEXT_SECONDARY} leading-relaxed`, size: "sm", children: step.desc })] }));
|
|
20
20
|
}
|
|
21
21
|
// --- Section -----------------------------------------------------------------
|
|
22
|
-
export function HowItWorksSection({ title, subtitle, pillLabel, ctaLabel,
|
|
22
|
+
export function HowItWorksSection({ title, subtitle, pillLabel, ctaLabel, ctaLink, steps, className = "", }) {
|
|
23
23
|
const themed = { textPrimary: THEMED_TEXT_PRIMARY, textSecondary: THEMED_TEXT_SECONDARY };
|
|
24
24
|
const sectionRef = useRef(null);
|
|
25
25
|
const [visible, setVisible] = useState(false);
|
|
@@ -36,5 +36,5 @@ export function HowItWorksSection({ title, subtitle, pillLabel, ctaLabel, onCtaC
|
|
|
36
36
|
observer.observe(el);
|
|
37
37
|
return () => observer.disconnect();
|
|
38
38
|
}, []);
|
|
39
|
-
return (_jsx(Section, { ref: sectionRef, className: `${className}`, surface: "muted", padding: "xl", children: _jsxs(Div, { className: "max-w-6xl mx-auto", children: [_jsxs(Div, { className: `text-left mb-12 transition-all duration-700 ${visible ? CLS_VISIBLE : "opacity-0 translate-y-4"}`, children: [pillLabel && (_jsx(Div, { layout: "inline-flex", gap: "2", align: "center", textWeight: "medium", textSize: "xs", className: /* audit-variant-ok: colour+alpha border (primary-500/30); the `border` variant takes a token name, not an rgba step */ "mb-4 border border-primary-500/30 bg-primary-500/10 py-[0.375rem] tracking-[0.2em] uppercase text-primary-700 dark:text-primary-400 backdrop-blur-sm", padding: "x-md", rounded: "full", children: pillLabel })), _jsx(Heading, { level: 2, className: `${themed.textPrimary} mb-3`, size: "3xl", mdSize: "4xl", weight: "bold", children: title }), subtitle && (_jsx(Text, { className: `${themed.textSecondary} max-w-xl`, size: "base", children: subtitle }))] }), _jsx(Grid, { gap: "lg", className: "grid-cols-1 md:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-3 mb-10", children: steps.map((step, i) => (_jsx(StepCard, { step: step, visible: visible, delayClass: STEP_DELAYS[Math.min(i, STEP_DELAYS.length - 1)] }, step.number))) }), ctaLabel &&
|
|
39
|
+
return (_jsx(Section, { ref: sectionRef, className: `${className}`, surface: "muted", padding: "xl", children: _jsxs(Div, { className: "max-w-6xl mx-auto", children: [_jsxs(Div, { className: `text-left mb-12 transition-all duration-700 ${visible ? CLS_VISIBLE : "opacity-0 translate-y-4"}`, children: [pillLabel && (_jsx(Div, { layout: "inline-flex", gap: "2", align: "center", textWeight: "medium", textSize: "xs", className: /* audit-variant-ok: colour+alpha border (primary-500/30); the `border` variant takes a token name, not an rgba step */ "mb-4 border border-primary-500/30 bg-primary-500/10 py-[0.375rem] tracking-[0.2em] uppercase text-primary-700 dark:text-primary-400 backdrop-blur-sm", padding: "x-md", rounded: "full", children: pillLabel })), _jsx(Heading, { level: 2, className: `${themed.textPrimary} mb-3`, size: "3xl", mdSize: "4xl", weight: "bold", children: title }), subtitle && (_jsx(Text, { className: `${themed.textSecondary} max-w-xl`, size: "base", children: subtitle }))] }), _jsx(Grid, { gap: "lg", className: "grid-cols-1 md:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-3 mb-10", children: steps.map((step, i) => (_jsx(StepCard, { step: step, visible: visible, delayClass: STEP_DELAYS[Math.min(i, STEP_DELAYS.length - 1)] }, step.number))) }), ctaLabel && ctaLink && (_jsx(Div, { className: `text-left transition-all duration-700 delay-500 ${visible ? CLS_VISIBLE : "opacity-0 translate-y-4"}`, children: _jsx(Button, { variant: "primary", size: "lg", asChild: true, children: _jsx(Anchor, { href: ctaLink, tone: "none", children: ctaLabel }) }) }))] }) }));
|
|
40
40
|
}
|
|
@@ -1,6 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
/** Hard cap — this layout only has slots for 4 (one hero tile + three). */
|
|
2
|
+
export declare const PROMO_GRID_MAX_BANNERS: 4;
|
|
3
|
+
/**
|
|
4
|
+
* Promoted to a registered `SectionType` ("promo-grid") 2026-10-19 — see
|
|
5
|
+
* `PromoGridSectionConfig` in `schemas/firestore.ts`.
|
|
6
|
+
*
|
|
7
|
+
* Two fixes made in the same pass:
|
|
8
|
+
* 1. `resolveMediaUrl(banner.image)!` was a non-null assertion on a function
|
|
9
|
+
* that genuinely returns `undefined` for an empty slug — an admin saving a
|
|
10
|
+
* banner with no image picked would crash `next/image`. The image slot now
|
|
11
|
+
* simply renders nothing: the `<Link>` already carries its own dark
|
|
12
|
+
* background and the gradient + caption paint on top regardless, so a
|
|
13
|
+
* placeholder tile would have been a second element doing what the card
|
|
14
|
+
* behind it already does.
|
|
15
|
+
* 2. `--dark-section-alt` / `--dark-section-card` / `--color-red` /
|
|
16
|
+
* `--font-bangers` were undefined anywhere in the codebase — this
|
|
17
|
+
* component would have rendered on a transparent background with no
|
|
18
|
+
* borders. The section now uses the `media-dark` SURFACE TOKEN (the real
|
|
19
|
+
* theme-invariant dark preset), plus `text-error` / `family="display"`.
|
|
20
|
+
* It is dark in every theme by design — see `PromoGridSectionConfig`'s
|
|
21
|
+
* docstring for why no admin background field was added (Root Cause #67).
|
|
22
|
+
*/
|
|
23
|
+
export interface PromoBannerConfig {
|
|
24
|
+
id: string;
|
|
25
|
+
title: string;
|
|
26
|
+
ctaLabel: string;
|
|
27
|
+
ctaUrl: string;
|
|
28
|
+
/** A media slug (`/media/<id>`), not a raw URL — resolved via `resolveMediaUrl`. */
|
|
29
|
+
image: string;
|
|
30
|
+
}
|
|
2
31
|
export interface PromoGridProps {
|
|
3
|
-
banners:
|
|
32
|
+
banners: PromoBannerConfig[];
|
|
4
33
|
/** Eyebrow label above the section heading. Default: "LIMITED TIME". */
|
|
5
34
|
eyebrow?: string;
|
|
6
35
|
/** Section heading. Default: "HOT DEALS & PROMOS". */
|