@mohasinac/appkit 4.13.1 → 4.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +14 -5
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +3 -0
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +6 -0
- package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +8 -0
- package/dist/_internal/client/features/lottery/LotteryDetailView.js +4 -1
- package/dist/_internal/client/features/lottery/LotteryListView.d.ts +7 -0
- package/dist/_internal/client/features/lottery/LotteryListView.js +1 -1
- package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +4 -1
- package/dist/_internal/client/features/lottery/slot-collage-items.d.ts +15 -0
- package/dist/_internal/client/features/lottery/slot-collage-items.js +13 -0
- package/dist/_internal/server/features/auctions/actions.js +8 -2
- package/dist/_internal/server/features/bundles/data.d.ts +16 -0
- package/dist/_internal/server/features/bundles/data.js +26 -0
- package/dist/_internal/server/features/bundles/index.d.ts +1 -1
- package/dist/_internal/server/features/bundles/index.js +1 -1
- package/dist/_internal/server/features/cart/service.d.ts +3 -3
- package/dist/_internal/server/features/checkout/actions.js +25 -0
- package/dist/_internal/server/features/checkout/locked-lines.d.ts +38 -0
- package/dist/_internal/server/features/checkout/locked-lines.js +171 -0
- package/dist/_internal/server/features/checkout/source-context.d.ts +5 -0
- package/dist/_internal/server/features/checkout/source-context.js +157 -0
- package/dist/_internal/server/features/homepage/index.d.ts +0 -1
- package/dist/_internal/server/features/homepage/index.js +7 -1
- package/dist/_internal/server/features/lottery/adapters.js +4 -0
- package/dist/_internal/server/features/offers/adapters.d.ts +103 -0
- package/dist/_internal/server/features/offers/adapters.js +88 -0
- package/dist/_internal/server/features/offers/index.d.ts +2 -0
- package/dist/_internal/server/features/offers/index.js +1 -0
- package/dist/_internal/server/features/orders/adapters.js +24 -0
- package/dist/_internal/server/features/products/data.js +15 -37
- package/dist/_internal/server/features/products/index.d.ts +1 -1
- package/dist/_internal/server/features/products/index.js +1 -1
- package/dist/_internal/server/features/products/list-public.d.ts +46 -20
- package/dist/_internal/server/features/products/list-public.js +266 -90
- package/dist/_internal/server/features/products/listing-tab-counts.d.ts +44 -0
- package/dist/_internal/server/features/products/listing-tab-counts.js +97 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +51 -0
- package/dist/_internal/server/features/site-settings/adapters.js +75 -0
- package/dist/_internal/server/features/site-settings/index.d.ts +1 -0
- package/dist/_internal/server/features/site-settings/index.js +1 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +56 -0
- package/dist/_internal/server/features/stores/adapters.js +91 -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/jobs/core/auctionSettlement.js +56 -2
- package/dist/_internal/server/jobs/core/offerExpiry.js +55 -19
- package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.d.ts +5 -0
- package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.js +33 -1
- package/dist/_internal/server/jobs/handlers/messages.d.ts +7 -0
- package/dist/_internal/server/jobs/handlers/messages.js +10 -0
- package/dist/_internal/shared/actions/action-registry.js +17 -1
- package/dist/_internal/shared/checkout/lanes.d.ts +25 -0
- package/dist/_internal/shared/checkout/lanes.js +26 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +27 -0
- package/dist/_internal/shared/features/auctions/config.js +27 -0
- package/dist/_internal/shared/features/auctions/schema.d.ts +14 -2
- package/dist/_internal/shared/features/auctions/schema.js +13 -1
- package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
- package/dist/_internal/shared/features/cart/schema.d.ts +20 -15
- package/dist/_internal/shared/features/cart/schema.js +15 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
- package/dist/_internal/shared/features/emi/schedule.d.ts +24 -3
- package/dist/_internal/shared/features/emi/schedule.js +20 -5
- package/dist/_internal/shared/features/events/schema.d.ts +2 -2
- package/dist/_internal/shared/features/homepage/section-gate.d.ts +14 -0
- package/dist/_internal/shared/features/homepage/section-gate.js +51 -0
- package/dist/_internal/shared/features/offers/config.d.ts +30 -0
- package/dist/_internal/shared/features/offers/config.js +59 -0
- package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
- package/dist/_internal/shared/features/products/schema.d.ts +30 -30
- package/dist/_internal/shared/features/products/to-product-item.js +6 -0
- package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
- package/dist/_internal/shared/features/site-settings/fees.d.ts +60 -0
- package/dist/_internal/shared/features/site-settings/fees.js +39 -0
- package/dist/_internal/shared/features/site-settings/types.d.ts +40 -0
- package/dist/_internal/shared/features/site-settings/types.js +1 -0
- package/dist/_internal/shared/history/history.d.ts +88 -0
- package/dist/_internal/shared/history/history.js +198 -0
- package/dist/_internal/shared/history/index.d.ts +4 -0
- package/dist/_internal/shared/history/index.js +2 -0
- package/dist/_internal/shared/history/types.d.ts +61 -0
- package/dist/_internal/shared/history/types.js +6 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +54 -1
- package/dist/_internal/shared/listing-types/_registry.js +67 -0
- package/dist/_internal/shared/listing-types/art/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/art/config.js +5 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +13 -0
- package/dist/_internal/shared/listing-types/auction/config.js +21 -0
- package/dist/_internal/shared/listing-types/availability.d.ts +79 -0
- package/dist/_internal/shared/listing-types/availability.js +104 -0
- package/dist/_internal/shared/listing-types/capabilities.d.ts +15 -0
- package/dist/_internal/shared/listing-types/capabilities.js +12 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/classified/config.js +5 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +11 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +22 -2
- package/dist/_internal/shared/listing-types/live/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/live/config.js +5 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +19 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +11 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +15 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/standard/config.js +5 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +5 -0
- package/dist/client.d.ts +20 -7
- package/dist/client.js +22 -4
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/constants/field-names.d.ts +57 -8
- package/dist/constants/field-names.js +101 -8
- package/dist/constants/table-keys.d.ts +17 -0
- package/dist/constants/table-keys.js +17 -0
- package/dist/errors/error-codes.d.ts +13 -1
- package/dist/errors/error-codes.js +13 -1
- package/dist/errors/error-display-map.js +12 -3
- package/dist/errors/messages.d.ts +10 -2
- package/dist/errors/messages.js +10 -2
- package/dist/features/about/components/AboutView.d.ts +3 -0
- package/dist/features/about/components/AboutView.js +3 -2
- package/dist/features/about/components/PolicyPageView.d.ts +2 -1
- package/dist/features/about/components/PolicyPageView.js +61 -23
- package/dist/features/about/components/PublicProfileView.js +5 -0
- package/dist/features/about/schemas/firestore.d.ts +3 -0
- package/dist/features/account/components/UserBidsView.js +12 -4
- package/dist/features/account/components/UserOrdersView.js +7 -0
- package/dist/features/account/components/UserReturnsView.d.ts +3 -0
- package/dist/features/account/components/UserReturnsView.js +17 -1
- package/dist/features/account/components/index.d.ts +0 -2
- package/dist/features/account/components/index.js +0 -1
- package/dist/features/account/schemas/index.d.ts +2 -2
- package/dist/features/account/types/index.d.ts +21 -2
- package/dist/features/addresses/schemas/index.d.ts +4 -4
- package/dist/features/admin/components/AdminArtView.js +8 -3
- package/dist/features/admin/components/AdminAuditLogView.js +1 -0
- package/dist/features/admin/components/AdminBidsView.js +15 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +65 -33
- package/dist/features/admin/components/AdminClassifiedView.js +8 -3
- package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
- package/dist/features/admin/components/AdminFeatureEditorView.js +35 -6
- package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
- package/dist/features/admin/components/AdminLiveView.js +8 -3
- package/dist/features/admin/components/AdminNavEditorView.js +15 -5
- package/dist/features/admin/components/AdminNewsletterView.js +18 -2
- package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
- package/dist/features/admin/components/AdminOffersView.js +230 -0
- package/dist/features/admin/components/AdminOrdersView.js +10 -1
- package/dist/features/admin/components/AdminPaymentMethodsView.js +24 -4
- package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
- package/dist/features/admin/components/AdminProductsView.js +16 -29
- package/dist/features/admin/components/AdminScammerEditorView.d.ts +2 -1
- package/dist/features/admin/components/AdminScammerEditorView.js +3 -13
- package/dist/features/admin/components/AdminScammersView.js +3 -8
- package/dist/features/admin/components/AdminSectionsView.js +32 -23
- package/dist/features/admin/components/AdminSessionsView.js +17 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +31 -13
- package/dist/features/admin/components/AdminSiteView.js +6 -2
- package/dist/features/admin/components/AdminStickersView.js +8 -3
- package/dist/features/admin/components/ViewAuditLogEntryModal.js +1 -0
- 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.d.ts +4 -2
- package/dist/features/admin/components/sections/adminSectionsTypes.js +45 -27
- package/dist/features/admin/constants/filter-tabs.d.ts +89 -0
- package/dist/features/admin/constants/filter-tabs.js +56 -0
- package/dist/features/admin/schemas/firestore.d.ts +122 -3
- package/dist/features/admin/schemas/firestore.js +1 -0
- package/dist/features/admin/schemas/homepage-section-form.d.ts +22 -0
- package/dist/features/admin/schemas/homepage-section-form.js +68 -0
- package/dist/features/admin/schemas/nav-item-form.d.ts +74 -0
- package/dist/features/admin/schemas/nav-item-form.js +72 -0
- package/dist/features/auctions/actions/bid-actions.d.ts +20 -5
- package/dist/features/auctions/actions/bid-actions.js +93 -20
- package/dist/features/auctions/components/AuctionBidsTable.d.ts +10 -1
- package/dist/features/auctions/components/AuctionBidsTable.js +22 -4
- package/dist/features/auctions/components/AuctionDetailPageView.js +11 -6
- package/dist/features/auctions/components/AuctionsListView.js +5 -6
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +7 -0
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +19 -2
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +8 -4
- package/dist/features/auctions/components/bid-detail-fields.d.ts +29 -0
- package/dist/features/auctions/components/bid-detail-fields.js +103 -0
- package/dist/features/auctions/repository/bid.repository.d.ts +13 -0
- package/dist/features/auctions/repository/bid.repository.js +23 -0
- package/dist/features/auctions/schemas/firestore.d.ts +17 -0
- package/dist/features/auctions/schemas/index.d.ts +12 -12
- package/dist/features/audit-log/schemas/firestore.d.ts +14 -0
- package/dist/features/audit-log/schemas/firestore.js +7 -0
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +10 -1
- package/dist/features/blog/actions/blog-actions.d.ts +7 -7
- package/dist/features/blog/schemas/blog-form.d.ts +80 -0
- package/dist/features/blog/schemas/blog-form.js +100 -0
- package/dist/features/blog/schemas/index.d.ts +6 -6
- package/dist/features/cart/repository/cart.repository.js +1 -0
- package/dist/features/cart/schemas/firestore.d.ts +15 -0
- package/dist/features/catalogue/schemas/validation.d.ts +4 -4
- package/dist/features/categories/components/BrandDetailPageView.js +23 -40
- package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -7
- package/dist/features/categories/components/BrandDetailTabs.js +8 -12
- package/dist/features/categories/components/BundleCollage.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +53 -57
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +10 -8
- package/dist/features/categories/components/CategoryDetailTabs.js +11 -16
- package/dist/features/categories/components/CategoryProductsListing.js +11 -1
- package/dist/features/categories/components/ConcernCard.js +7 -1
- package/dist/features/categories/schemas/firestore.d.ts +13 -0
- package/dist/features/categories/schemas/index.d.ts +26 -26
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +16 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.js +23 -3
- package/dist/features/events/components/AdminEventEditorView.js +32 -54
- package/dist/features/events/components/AdminEventEntriesView.js +22 -3
- package/dist/features/events/components/AdminEventsView.js +15 -11
- package/dist/features/events/components/EventFilters.d.ts +9 -1
- package/dist/features/events/components/EventFilters.js +45 -21
- package/dist/features/events/components/EventsIndexListing.d.ts +6 -1
- package/dist/features/events/components/EventsIndexListing.js +2 -2
- package/dist/features/events/components/EventsListPageView.js +13 -4
- package/dist/features/events/repository/events.repository.d.ts +17 -0
- package/dist/features/events/repository/events.repository.js +27 -0
- package/dist/features/events/schemas/event-form.d.ts +113 -0
- package/dist/features/events/schemas/event-form.js +82 -0
- package/dist/features/events/schemas/firestore.d.ts +12 -0
- package/dist/features/events/schemas/firestore.js +12 -0
- package/dist/features/events/schemas/index.d.ts +50 -50
- package/dist/features/events/types/index.d.ts +11 -0
- package/dist/features/events/types/index.js +57 -1
- package/dist/features/faq/actions/faq-actions.d.ts +12 -12
- package/dist/features/faq/schemas/index.d.ts +8 -8
- package/dist/features/filters/FilterFacetSection.d.ts +7 -1
- package/dist/features/filters/FilterFacetSection.js +11 -3
- package/dist/features/grouped/schemas/grouped-listing-form.d.ts +101 -0
- package/dist/features/grouped/schemas/grouped-listing-form.js +89 -0
- package/dist/features/grouped/schemas/index.d.ts +4 -4
- package/dist/features/homepage/actions/homepage-section-actions.d.ts +11 -3
- package/dist/features/homepage/actions/homepage-section-actions.js +33 -1
- package/dist/features/homepage/components/AnnouncementBar.d.ts +7 -1
- package/dist/features/homepage/components/AnnouncementBar.js +3 -3
- package/dist/features/homepage/components/MarketplaceHomepageView.js +20 -6
- package/dist/features/homepage/hooks/homepage-query.d.ts +16 -0
- package/dist/features/homepage/hooks/homepage-query.js +45 -0
- package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +10 -0
- package/dist/features/homepage/hooks/useFeaturedAuctions.js +28 -8
- package/dist/features/homepage/hooks/useFeaturedPreOrders.js +17 -8
- package/dist/features/homepage/hooks/useFeaturedProducts.js +15 -8
- package/dist/features/homepage/schemas/index.d.ts +33 -2
- package/dist/features/homepage/schemas/index.js +24 -0
- package/dist/features/layout/AppLayoutShell.d.ts +11 -1
- package/dist/features/layout/AppLayoutShell.js +15 -14
- package/dist/features/layout/BackToTop.d.ts +9 -0
- package/dist/features/layout/BackToTop.js +10 -1
- package/dist/features/layout/BottomActions.js +28 -25
- package/dist/features/layout/BottomChrome.d.ts +34 -0
- package/dist/features/layout/BottomChrome.js +55 -0
- package/dist/features/layout/BottomNavLayout.d.ts +6 -0
- package/dist/features/layout/BottomNavLayout.js +7 -1
- package/dist/features/layout/BottomNavbar.js +15 -2
- package/dist/features/layout/FooterLayout.js +11 -1
- package/dist/features/layout/NavbarLayout.js +1 -1
- package/dist/features/layout/TitleBarLayout.d.ts +7 -0
- package/dist/features/layout/TitleBarLayout.js +28 -5
- package/dist/features/layout/index.d.ts +2 -0
- package/dist/features/layout/index.js +1 -0
- package/dist/features/lottery/types/index.d.ts +3 -0
- package/dist/features/media/MediaPickerModal.js +5 -1
- package/dist/features/messages/schemas/index.d.ts +2 -2
- package/dist/features/orders/components/OrderScopeTabs.d.ts +20 -0
- package/dist/features/orders/components/OrderScopeTabs.js +31 -0
- package/dist/features/orders/components/OrderStatusTimeline.d.ts +5 -1
- package/dist/features/orders/components/OrderStatusTimeline.js +39 -20
- package/dist/features/orders/constants/order-scope.d.ts +48 -0
- package/dist/features/orders/constants/order-scope.js +111 -0
- package/dist/features/orders/repository/orders.repository.d.ts +54 -7
- package/dist/features/orders/repository/orders.repository.js +121 -22
- package/dist/features/orders/schemas/firestore.d.ts +152 -0
- package/dist/features/orders/schemas/firestore.js +30 -0
- package/dist/features/orders/schemas/index.d.ts +18 -18
- package/dist/features/orders/types/index.d.ts +35 -7
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +14 -9
- package/dist/features/pre-orders/components/PreOrdersListView.js +6 -5
- package/dist/features/pre-orders/schemas/index.d.ts +1 -1
- package/dist/features/products/actions/product-actions.d.ts +14 -0
- package/dist/features/products/actions/product-actions.js +49 -11
- package/dist/features/products/api/route.d.ts +12 -0
- package/dist/features/products/api/route.js +42 -100
- package/dist/features/products/components/ArtStickersListView.js +2 -2
- package/dist/features/products/components/AuctionsIndexListing.js +13 -11
- package/dist/features/products/components/AvailabilityTabs.d.ts +11 -0
- package/dist/features/products/components/AvailabilityTabs.js +47 -0
- package/dist/features/products/components/GroupSettingsPanel.js +5 -1
- package/dist/features/products/components/MakeOfferButton.js +48 -49
- package/dist/features/products/components/PrizeDrawCollage.d.ts +20 -4
- package/dist/features/products/components/PrizeDrawCollage.js +9 -1
- package/dist/features/products/components/PrizeDrawsIndexListing.js +14 -12
- package/dist/features/products/components/PrizeDrawsListingView.js +2 -2
- package/dist/features/products/components/PrizeDrawsSection.d.ts +7 -1
- package/dist/features/products/components/PrizeDrawsSection.js +23 -18
- package/dist/features/products/components/ProductDetailPageView.d.ts +4 -1
- package/dist/features/products/components/ProductDetailPageView.js +18 -4
- package/dist/features/products/components/ProductForm.js +5 -2
- package/dist/features/products/components/ProductGrid.js +6 -1
- package/dist/features/products/components/ProductsIndexListing.js +22 -32
- package/dist/features/products/components/ProductsIndexPageView.js +6 -9
- package/dist/features/products/components/index.d.ts +2 -2
- package/dist/features/products/components/index.js +1 -1
- package/dist/features/products/config/listing-type-listing-config.d.ts +20 -0
- package/dist/features/products/config/listing-type-listing-config.js +22 -10
- package/dist/features/products/constants/action-defs.d.ts +2 -0
- package/dist/features/products/constants/action-defs.js +6 -0
- package/dist/features/products/hooks/useAvailabilityScope.d.ts +13 -0
- package/dist/features/products/hooks/useAvailabilityScope.js +19 -0
- package/dist/features/products/hooks/useProducts.d.ts +5 -0
- package/dist/features/products/hooks/useProducts.js +13 -2
- package/dist/features/products/repository/products.repository.d.ts +31 -2
- package/dist/features/products/repository/products.repository.js +53 -21
- package/dist/features/products/schemas/firestore.d.ts +17 -3
- package/dist/features/products/schemas/firestore.js +7 -2
- package/dist/features/products/schemas/index.d.ts +64 -64
- package/dist/features/products/schemas/product-features.validators.d.ts +79 -6
- package/dist/features/products/schemas/product-features.validators.js +63 -0
- package/dist/features/products/types/index.d.ts +16 -1
- package/dist/features/promotions/schemas/index.d.ts +2 -2
- package/dist/features/reviews/schemas/index.d.ts +16 -16
- package/dist/features/scams/constants/scammer-status-display.d.ts +28 -0
- package/dist/features/scams/constants/scammer-status-display.js +79 -0
- package/dist/features/search/schemas/index.d.ts +4 -4
- package/dist/features/seller/actions/offer-actions.js +57 -4
- package/dist/features/seller/components/OfferPhaseTimeline.d.ts +59 -0
- package/dist/features/seller/components/OfferPhaseTimeline.js +107 -0
- package/dist/features/seller/components/SellerArtView.js +5 -0
- package/dist/features/seller/components/SellerAuctionsView.js +13 -15
- package/dist/features/seller/components/SellerBidsView.js +11 -4
- package/dist/features/seller/components/SellerClassifiedView.js +5 -0
- package/dist/features/seller/components/SellerCouponEditorView.js +17 -17
- package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
- package/dist/features/seller/components/SellerLiveView.js +5 -0
- package/dist/features/seller/components/SellerOffersView.js +2 -1
- package/dist/features/seller/components/SellerOrdersView.js +31 -9
- package/dist/features/seller/components/SellerPayoutSettingsView.js +56 -31
- package/dist/features/seller/components/SellerPreOrdersView.js +5 -0
- package/dist/features/seller/components/SellerPrizeDrawsView.js +14 -16
- package/dist/features/seller/components/SellerProductShell.js +7 -1
- package/dist/features/seller/components/SellerProductsView.js +11 -8
- package/dist/features/seller/components/SellerStickersView.js +5 -0
- package/dist/features/seller/components/index.d.ts +0 -2
- package/dist/features/seller/components/index.js +0 -1
- package/dist/features/seller/repository/offer.repository.d.ts +142 -24
- package/dist/features/seller/repository/offer.repository.js +165 -26
- package/dist/features/seller/schemas/coupon-form.d.ts +72 -0
- package/dist/features/seller/schemas/coupon-form.js +116 -0
- package/dist/features/seller/schemas/firestore.d.ts +56 -2
- package/dist/features/seller/schemas/firestore.js +27 -0
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/seller/schemas/offer-forms.d.ts +46 -0
- package/dist/features/seller/schemas/offer-forms.js +42 -0
- package/dist/features/seller/server.d.ts +0 -1
- package/dist/features/seller/server.js +0 -1
- package/dist/features/shell/SectionForm.d.ts +99 -0
- package/dist/features/shell/SectionForm.js +128 -0
- package/dist/features/shell/build-sections.d.ts +63 -0
- package/dist/features/shell/build-sections.js +207 -0
- package/dist/features/shell/entity-form.d.ts +109 -0
- package/dist/features/shell/entity-form.js +96 -0
- package/dist/features/shell/field-ui-meta.d.ts +115 -0
- package/dist/features/shell/field-ui-meta.js +324 -0
- package/dist/features/shell/index.d.ts +8 -0
- package/dist/features/shell/index.js +4 -0
- package/dist/features/status-history/components/StatusTimeline.d.ts +74 -0
- package/dist/features/status-history/components/StatusTimeline.js +46 -0
- package/dist/features/status-history/index.d.ts +11 -0
- package/dist/features/status-history/index.js +10 -0
- package/dist/features/store-extensions/components/ReviewDecisionModal.d.ts +27 -0
- package/dist/features/store-extensions/components/ReviewDecisionModal.js +82 -0
- package/dist/features/store-extensions/index.d.ts +12 -0
- package/dist/features/store-extensions/index.js +12 -0
- package/dist/features/store-extensions/schemas/admin-notification-form.d.ts +36 -0
- package/dist/features/store-extensions/schemas/admin-notification-form.js +114 -0
- package/dist/features/store-extensions/schemas/analytics-forms.d.ts +57 -0
- package/dist/features/store-extensions/schemas/analytics-forms.js +158 -0
- package/dist/features/store-extensions/schemas/custom-role-form.d.ts +85 -0
- package/dist/features/store-extensions/schemas/custom-role-form.js +118 -0
- package/dist/features/store-extensions/schemas/firestore.d.ts +21 -1
- package/dist/features/store-extensions/schemas/firestore.js +60 -0
- package/dist/features/store-extensions/schemas/google-config-form.d.ts +53 -0
- package/dist/features/store-extensions/schemas/google-config-form.js +105 -0
- package/dist/features/store-extensions/schemas/item-request-create-form.d.ts +28 -0
- package/dist/features/store-extensions/schemas/item-request-create-form.js +80 -0
- package/dist/features/store-extensions/schemas/listing-template-form.d.ts +78 -0
- package/dist/features/store-extensions/schemas/listing-template-form.js +87 -0
- package/dist/features/store-extensions/schemas/moderation-review-form.d.ts +49 -0
- package/dist/features/store-extensions/schemas/moderation-review-form.js +101 -0
- package/dist/features/store-extensions/schemas/payout-method-form.d.ts +153 -0
- package/dist/features/store-extensions/schemas/payout-method-form.js +155 -0
- package/dist/features/store-extensions/schemas/report-create-form.d.ts +25 -0
- package/dist/features/store-extensions/schemas/report-create-form.js +79 -0
- package/dist/features/store-extensions/schemas/report-review-form.d.ts +47 -0
- package/dist/features/store-extensions/schemas/report-review-form.js +111 -0
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +148 -0
- package/dist/features/store-extensions/schemas/shipping-config-form.js +104 -0
- package/dist/features/store-extensions/schemas/store-category-form.d.ts +54 -0
- package/dist/features/store-extensions/schemas/store-category-form.js +73 -0
- package/dist/features/stores/api/[storeSlug]/route.js +2 -25
- package/dist/features/stores/components/StoreAuctionsListing.js +15 -1
- package/dist/features/stores/components/StoreClassifiedsListing.js +12 -2
- package/dist/features/stores/components/StoreDetailLayoutView.js +24 -71
- package/dist/features/stores/components/StoreDigitalCodesListing.js +12 -2
- package/dist/features/stores/components/StoreLiveItemsListing.js +12 -2
- package/dist/features/stores/components/StorePreOrdersListing.js +12 -1
- package/dist/features/stores/components/StoreProductsListing.js +11 -1
- package/dist/features/stores/repository/store.repository.js +6 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- package/dist/features/support/schemas/index.d.ts +4 -4
- package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
- package/dist/features/tester/seed-data/offers-tester-seed-data.js +119 -3
- package/dist/features/tester/seed-data/products-tester-seed-data.js +131 -1
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +431 -1
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +25 -12
- package/dist/index.js +26 -11
- package/dist/next/routing/route-map.d.ts +23 -3
- package/dist/next/routing/route-map.js +14 -2
- package/dist/providers/db-firebase/base.repository.d.ts +30 -0
- package/dist/providers/db-firebase/base.repository.js +50 -1
- package/dist/providers/db-firebase/index.d.ts +1 -1
- package/dist/providers/db-firebase/index.js +1 -1
- package/dist/providers/db-firebase/sieve.d.ts +25 -0
- package/dist/providers/db-firebase/sieve.js +52 -18
- package/dist/react/hooks/useUrlTable.js +10 -1
- package/dist/schemas/firestore-value.d.ts +8 -0
- package/dist/schemas/firestore-value.js +44 -0
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/index.js +1 -1
- package/dist/schemas/registry.d.ts +702 -69
- package/dist/schemas/registry.js +64 -1
- package/dist/seed/_helpers/category-forest.d.ts +2 -0
- package/dist/seed/_helpers/category-forest.js +309 -0
- package/dist/seed/_helpers/category-tree.d.ts +27 -0
- package/dist/seed/_helpers/category-tree.js +103 -0
- package/dist/seed/bids-seed-data.js +30 -0
- package/dist/seed/catalogue-seed-data.js +12 -12
- package/dist/seed/categories-seed-data.js +177 -162
- package/dist/seed/events-seed-data.js +61 -10
- package/dist/seed/homepage-sections-seed-data.js +5 -1
- package/dist/seed/offers-seed-data.js +244 -34
- package/dist/seed/orders-seed-data.js +336 -1
- package/dist/seed/products-art-seed-data.js +50 -12
- package/dist/seed/products-auctions-seed-data.js +138 -18
- package/dist/seed/products-classifieds-seed-data.js +80 -16
- package/dist/seed/products-digital-codes-seed-data.js +66 -18
- package/dist/seed/products-live-items-seed-data.js +64 -13
- package/dist/seed/products-preorders-seed-data.d.ts +0 -1
- package/dist/seed/products-preorders-seed-data.js +52 -13
- package/dist/seed/products-prize-draws-seed-data.js +63 -14
- package/dist/seed/products-standard-seed-data.js +99 -26
- package/dist/seed/products-stickers-seed-data.js +47 -12
- package/dist/seed/reviews-seed-data.js +6 -1
- package/dist/seed/site-settings-seed-data.d.ts +1 -38
- package/dist/seed/site-settings-seed-data.js +38 -76
- package/dist/seed/stores-seed-data.js +71 -1
- package/dist/seed/users-seed-data.js +15 -5
- package/dist/server-entry.d.ts +1 -2
- package/dist/server-entry.js +4 -3
- package/dist/server.d.ts +6 -3
- package/dist/server.js +13 -4
- package/dist/styles.css +78 -12
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Button.style.css +19 -4
- package/dist/ui/components/CollapsibleSection.d.ts +17 -1
- package/dist/ui/components/CollapsibleSection.js +6 -2
- package/dist/ui/components/FilterChipGroup.d.ts +25 -1
- package/dist/ui/components/FilterChipGroup.js +21 -4
- package/dist/ui/components/HandMode.style.css +47 -0
- package/dist/ui/components/Layout.d.ts +9 -2
- package/dist/ui/components/Layout.js +1 -1
- package/dist/ui/components/ListingLayout.js +8 -8
- package/dist/ui/components/ListingLayout.style.css +8 -7
- package/dist/ui/components/RecordDetailModal.d.ts +10 -1
- package/dist/ui/components/RecordDetailModal.js +2 -2
- package/dist/ui/components/StickyToolbar.d.ts +3 -1
- package/dist/ui/components/StickyToolbar.js +62 -20
- package/dist/ui/components/bottom-chrome-slot.d.ts +21 -0
- package/dist/ui/components/bottom-chrome-slot.js +29 -0
- package/dist/ui/components/index.style.css +3 -0
- package/dist/ui/forms/FormShell.d.ts +43 -13
- package/dist/ui/forms/FormShell.js +22 -11
- package/dist/ui/forms/index.d.ts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +2 -1
- package/scripts/node-esm-loader.mjs +65 -0
- package/scripts/seed-cli-loader.mjs +6 -49
- package/scripts/seed-cli.mjs +1 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: The blog form's schema used to live inside `AdminBlogEditorView.tsx`.
|
|
3
|
+
* Registering it in `SCHEMAS.forms` from there would have made
|
|
4
|
+
* `schemas/registry.ts` import a `.tsx` component — and `routeHandler.ts`
|
|
5
|
+
* imports SCHEMAS, so every API route would have pulled the admin blog
|
|
6
|
+
* editor's whole React tree into the server bundle. Schemas belong in
|
|
7
|
+
* `schemas/`; that is the layering this restores.
|
|
8
|
+
* WHAT: The blog editor's form schema, with its UI metadata.
|
|
9
|
+
*
|
|
10
|
+
* EXPORTS:
|
|
11
|
+
* blogDraftSchema
|
|
12
|
+
*
|
|
13
|
+
* @tag domain:blog
|
|
14
|
+
* @tag layer:schema
|
|
15
|
+
* @tag pattern:none
|
|
16
|
+
* @tag access:isomorphic
|
|
17
|
+
* @tag consumers:AdminBlogEditorView,SCHEMAS.forms
|
|
18
|
+
* @tag sideEffects:none
|
|
19
|
+
*/
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import { annotate } from "../../shell/field-ui-meta";
|
|
22
|
+
import { blogPostCategorySchema } from "./index";
|
|
23
|
+
/**
|
|
24
|
+
* The blog form's own schema. Distinct from `blogPostSchema` (blog/schemas),
|
|
25
|
+
* which requires an `id` this draft cannot have before its first save and
|
|
26
|
+
* models `coverImage` as a `MediaField` where the form holds a plain URL.
|
|
27
|
+
*
|
|
28
|
+
* ## Why `.passthrough()` is gone
|
|
29
|
+
*
|
|
30
|
+
* It used to name FOUR of the draft's fourteen fields and rely on passthrough
|
|
31
|
+
* to keep the other ten alive. That is the single most dangerous shape in this
|
|
32
|
+
* codebase — `z.object()` strips unknown keys, so the moment anyone tightened
|
|
33
|
+
* it, ten fields would vanish on the next save with no error. Verified safe to
|
|
34
|
+
* remove here: every one of the fourteen is now named, and the save payload
|
|
35
|
+
* (below) maps each explicitly rather than spreading the draft, so there is no
|
|
36
|
+
* extra key for passthrough to have been protecting.
|
|
37
|
+
*
|
|
38
|
+
* ## Section, row and control come from the field itself
|
|
39
|
+
*
|
|
40
|
+
* 🛑 `annotate()` must be the OUTERMOST call — it keys a WeakMap by schema
|
|
41
|
+
* instance and every zod wrapper returns a new one, so
|
|
42
|
+
* `annotate(z.string(), …).optional()` silently loses the metadata and drops
|
|
43
|
+
* the field into the "advanced" fallback. `audit-field-ui-meta` blocks it.
|
|
44
|
+
*/
|
|
45
|
+
export const blogDraftSchema = z.object({
|
|
46
|
+
title: annotate(z.string().min(1, "Title is required"), {
|
|
47
|
+
section: "content", sectionLabel: "Content", sectionRequired: true,
|
|
48
|
+
quick: true, order: 1, row: "full",
|
|
49
|
+
}),
|
|
50
|
+
/**
|
|
51
|
+
* NO `blog-` prefix. Surfaced by `roundtrip-diff` in W4: the schema demanded
|
|
52
|
+
* `/^blog-/`, and **all 20 stored posts fail it** — every seeded post holds
|
|
53
|
+
* the bare slug (`spot-genuine-takara-tomy-beyblade`) while only the
|
|
54
|
+
* DOCUMENT ID carries the prefix (`generateBlogPostId` adds it).
|
|
55
|
+
*
|
|
56
|
+
* `BlogRepository.findBySlug` queries this field directly, so the public URL
|
|
57
|
+
* is the bare slug. The regex therefore broke the editor from both ends:
|
|
58
|
+
* opening any existing post flagged its slug invalid, and "fixing" it to
|
|
59
|
+
* satisfy the rule would rewrite the stored slug and 404 the live URL.
|
|
60
|
+
*/
|
|
61
|
+
slug: annotate(z.string().min(1, "Slug is required"), {
|
|
62
|
+
section: "content", quick: true, order: 2, row: "full",
|
|
63
|
+
// T1 calculated: derived from the title on CREATE only, then frozen.
|
|
64
|
+
// Recomputing on rename would break every inbound link and bookmark —
|
|
65
|
+
// categories, brands and bundles all establish that convention.
|
|
66
|
+
tier: "t1-derive",
|
|
67
|
+
help: "Auto-filled from the title. Changing it after publishing breaks existing links.",
|
|
68
|
+
}),
|
|
69
|
+
excerpt: annotate(z.string().max(300, "Keep the excerpt under 300 characters."), {
|
|
70
|
+
section: "content", order: 3, row: "full",
|
|
71
|
+
}),
|
|
72
|
+
content: annotate(z.string().min(1, "Content is required"), {
|
|
73
|
+
section: "content", quick: true, order: 4, row: "full",
|
|
74
|
+
}),
|
|
75
|
+
coverImage: annotate(z.string(), {
|
|
76
|
+
section: "media", sectionLabel: "Media", order: 1, row: "full", kind: "media",
|
|
77
|
+
}),
|
|
78
|
+
youtubeId: annotate(z.string(), {
|
|
79
|
+
section: "media", order: 2, row: "full",
|
|
80
|
+
help: "A YouTube video id or watch URL — rendered as a privacy-mode embed.",
|
|
81
|
+
}),
|
|
82
|
+
category: annotate(blogPostCategorySchema, {
|
|
83
|
+
section: "seo", sectionLabel: "SEO & Tags", quick: true, order: 1, row: "pair",
|
|
84
|
+
}),
|
|
85
|
+
tags: annotate(z.array(z.string()), {
|
|
86
|
+
section: "seo", order: 2, row: "full", kind: "list",
|
|
87
|
+
}),
|
|
88
|
+
metaTitle: annotate(z.string().max(60, "Keep the meta title under 60 characters."), {
|
|
89
|
+
section: "seo", order: 3, row: "full", tier: "t1-derive",
|
|
90
|
+
}),
|
|
91
|
+
metaDescription: annotate(z.string().max(160, "Keep the meta description under 160 characters.").or(z.literal("")), { section: "seo", order: 4, row: "full", tier: "t1-derive" }),
|
|
92
|
+
status: annotate(z.enum(["draft", "published", "archived"]), {
|
|
93
|
+
section: "publish", sectionLabel: "Publish", quick: true, order: 1, row: "pair",
|
|
94
|
+
}),
|
|
95
|
+
publishedAt: annotate(z.string(), {
|
|
96
|
+
section: "publish", order: 2, row: "pair", kind: "date",
|
|
97
|
+
}),
|
|
98
|
+
authorName: annotate(z.string(), { section: "publish", order: 3, row: "pair" }),
|
|
99
|
+
isFeatured: annotate(z.boolean(), { section: "publish", order: 4, row: "quarter" }),
|
|
100
|
+
});
|
|
@@ -125,8 +125,8 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
|
|
|
125
125
|
updatedAt: string | Date | z.objectOutputType<{
|
|
126
126
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
127
127
|
}, z.ZodTypeAny, "passthrough">;
|
|
128
|
-
category: string;
|
|
129
128
|
slug: string;
|
|
129
|
+
category: string;
|
|
130
130
|
tags: string[];
|
|
131
131
|
isFeatured: boolean;
|
|
132
132
|
excerpt: string;
|
|
@@ -178,8 +178,8 @@ export declare const blogPostFirestoreSchema: z.ZodObject<{
|
|
|
178
178
|
updatedAt: string | Date | z.objectInputType<{
|
|
179
179
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
180
180
|
}, z.ZodTypeAny, "passthrough">;
|
|
181
|
-
category: string;
|
|
182
181
|
slug: string;
|
|
182
|
+
category: string;
|
|
183
183
|
tags: string[];
|
|
184
184
|
isFeatured: boolean;
|
|
185
185
|
excerpt: string;
|
|
@@ -358,8 +358,8 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
358
358
|
status: "draft" | "published" | "archived";
|
|
359
359
|
id: string;
|
|
360
360
|
title: string;
|
|
361
|
-
category: "news" | "tips" | "guides" | "updates" | "community";
|
|
362
361
|
slug: string;
|
|
362
|
+
category: "news" | "tips" | "guides" | "updates" | "community";
|
|
363
363
|
coverImage: {
|
|
364
364
|
type: "video" | "image" | "file";
|
|
365
365
|
url: string;
|
|
@@ -394,8 +394,8 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
394
394
|
createdAt?: string | undefined;
|
|
395
395
|
updatedAt?: string | undefined;
|
|
396
396
|
tags?: string[] | undefined;
|
|
397
|
-
isFeatured?: boolean | undefined;
|
|
398
397
|
publishedAt?: string | undefined;
|
|
398
|
+
isFeatured?: boolean | undefined;
|
|
399
399
|
excerpt?: string | undefined;
|
|
400
400
|
authorId?: string | undefined;
|
|
401
401
|
authorName?: string | undefined;
|
|
@@ -408,14 +408,14 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
408
408
|
status: "draft" | "published" | "archived";
|
|
409
409
|
id: string;
|
|
410
410
|
title: string;
|
|
411
|
-
category: "news" | "tips" | "guides" | "updates" | "community";
|
|
412
411
|
slug: string;
|
|
412
|
+
category: "news" | "tips" | "guides" | "updates" | "community";
|
|
413
413
|
content?: string | undefined;
|
|
414
414
|
createdAt?: string | undefined;
|
|
415
415
|
updatedAt?: string | undefined;
|
|
416
416
|
tags?: string[] | undefined;
|
|
417
|
-
isFeatured?: boolean | undefined;
|
|
418
417
|
publishedAt?: string | undefined;
|
|
418
|
+
isFeatured?: boolean | undefined;
|
|
419
419
|
excerpt?: string | undefined;
|
|
420
420
|
coverImage?: string | {
|
|
421
421
|
type: "video" | "image" | "file";
|
|
@@ -100,6 +100,7 @@ export class CartRepository extends BaseRepository {
|
|
|
100
100
|
...(input.isAuctionWin !== undefined && { isAuctionWin: input.isAuctionWin }),
|
|
101
101
|
...(input.auctionId !== undefined && { auctionId: input.auctionId }),
|
|
102
102
|
...(input.bidId !== undefined && { bidId: input.bidId }),
|
|
103
|
+
...(input.isBuyout !== undefined && { isBuyout: input.isBuyout }),
|
|
103
104
|
...(input.lockedPrice !== undefined && {
|
|
104
105
|
lockedPrice: input.lockedPrice,
|
|
105
106
|
}),
|
|
@@ -38,6 +38,19 @@ export interface CartItemDocument {
|
|
|
38
38
|
auctionId?: string;
|
|
39
39
|
/** The winning bid, so the resulting order can be linked back to it. */
|
|
40
40
|
bidId?: string;
|
|
41
|
+
/**
|
|
42
|
+
* True when this auction line came from Buy Now rather than from settlement.
|
|
43
|
+
*
|
|
44
|
+
* The two are NOT interchangeable even though both sit in the auction lane:
|
|
45
|
+
* - a settlement line is a FINISHED auction — the item is already sold, the
|
|
46
|
+
* buyer owes it, and the 48h window costs nobody anything;
|
|
47
|
+
* - a buyout line is a CLAIM on an auction that is still live and still
|
|
48
|
+
* taking bids. The item is not sold yet; it becomes sold only when
|
|
49
|
+
* `claimAuctionForCheckout` commits at order time. Hence the 1h window,
|
|
50
|
+
* and hence a lapse that cancels the bid rather than forfeiting it —
|
|
51
|
+
* nothing was won, so nothing is forfeited, and the auction is untouched.
|
|
52
|
+
*/
|
|
53
|
+
isBuyout?: boolean;
|
|
41
54
|
/** Locked offer/winning-bid price — overrides normal product price at checkout */
|
|
42
55
|
lockedPrice?: number;
|
|
43
56
|
/**
|
|
@@ -158,6 +171,8 @@ export type AddToCartInput = {
|
|
|
158
171
|
isAuctionWin?: boolean;
|
|
159
172
|
auctionId?: string;
|
|
160
173
|
bidId?: string;
|
|
174
|
+
/** See `CartItemDocument.isBuyout` — a claim on a live auction, not a win. */
|
|
175
|
+
isBuyout?: boolean;
|
|
161
176
|
lockedPrice?: number;
|
|
162
177
|
checkoutDeadline?: Date;
|
|
163
178
|
locked?: boolean;
|
|
@@ -13,8 +13,8 @@ export declare const createCatalogueItemSchema: z.ZodObject<{
|
|
|
13
13
|
}, "strip", z.ZodTypeAny, {
|
|
14
14
|
title: string;
|
|
15
15
|
images: string[];
|
|
16
|
-
visibility: "public" | "private";
|
|
17
16
|
quantity: number;
|
|
17
|
+
visibility: "public" | "private";
|
|
18
18
|
description?: string | undefined;
|
|
19
19
|
categorySlugs?: string[] | undefined;
|
|
20
20
|
brandSlug?: string | undefined;
|
|
@@ -30,8 +30,8 @@ export declare const createCatalogueItemSchema: z.ZodObject<{
|
|
|
30
30
|
mainImage?: string | undefined;
|
|
31
31
|
images?: string[] | undefined;
|
|
32
32
|
condition?: "new" | "used" | "refurbished" | "like_new" | "good" | "fair" | "poor" | "broken" | "graded" | undefined;
|
|
33
|
-
visibility?: "public" | "private" | undefined;
|
|
34
33
|
quantity?: number | undefined;
|
|
34
|
+
visibility?: "public" | "private" | undefined;
|
|
35
35
|
}>;
|
|
36
36
|
export declare const updateCatalogueItemSchema: z.ZodObject<{
|
|
37
37
|
title: z.ZodOptional<z.ZodString>;
|
|
@@ -53,8 +53,8 @@ export declare const updateCatalogueItemSchema: z.ZodObject<{
|
|
|
53
53
|
mainImage?: string | undefined;
|
|
54
54
|
images?: string[] | undefined;
|
|
55
55
|
condition?: "new" | "used" | "refurbished" | "like_new" | "good" | "fair" | "poor" | "broken" | "graded" | undefined;
|
|
56
|
-
visibility?: "public" | "private" | undefined;
|
|
57
56
|
quantity?: number | undefined;
|
|
57
|
+
visibility?: "public" | "private" | undefined;
|
|
58
58
|
}, {
|
|
59
59
|
title?: string | undefined;
|
|
60
60
|
description?: string | undefined;
|
|
@@ -64,8 +64,8 @@ export declare const updateCatalogueItemSchema: z.ZodObject<{
|
|
|
64
64
|
mainImage?: string | undefined;
|
|
65
65
|
images?: string[] | undefined;
|
|
66
66
|
condition?: "new" | "used" | "refurbished" | "like_new" | "good" | "fair" | "poor" | "broken" | "graded" | undefined;
|
|
67
|
-
visibility?: "public" | "private" | undefined;
|
|
68
67
|
quantity?: number | undefined;
|
|
68
|
+
visibility?: "public" | "private" | undefined;
|
|
69
69
|
}>;
|
|
70
70
|
export type CreateCatalogueItemInput = z.infer<typeof createCatalogueItemSchema>;
|
|
71
71
|
export type UpdateCatalogueItemInput = z.infer<typeof updateCatalogueItemSchema>;
|
|
@@ -12,6 +12,10 @@ import { CategoryGrid } from "./CategoryGrid";
|
|
|
12
12
|
import { CategoryHighlightsAndFaqSection } from "./CategoryHighlightsAndFaqSection";
|
|
13
13
|
import { GroupedListingsCarousel } from "../../grouped/components/GroupedListingsCarousel";
|
|
14
14
|
import { getGroupsForBrand } from "../../../_internal/server/features/grouped/data";
|
|
15
|
+
import { listingTabCounts, } from "../../../_internal/server/features/products/listing-tab-counts";
|
|
16
|
+
import { CATEGORY_PAGE_TABS } from "../../products/constants/listing-tabs";
|
|
17
|
+
import { enabledCategoryTypes, enabledListingTypes } from "../../../_internal/shared/listing-types/feature-flags";
|
|
18
|
+
import { siteSettingsRepository } from "../../../repositories";
|
|
15
19
|
const __O = {
|
|
16
20
|
hidden: "overflow-hidden",
|
|
17
21
|
};
|
|
@@ -20,7 +24,7 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
|
|
|
20
24
|
.getCategoryBySlug(slug)
|
|
21
25
|
.catch(() => undefined)));
|
|
22
26
|
const brandName = brand?.name;
|
|
23
|
-
const [productsResult,
|
|
27
|
+
const [productsResult, tabCounts, allBundles, activeBrands, groupedListings, settings] = await Promise.all([
|
|
24
28
|
brandName
|
|
25
29
|
? productRepository
|
|
26
30
|
.list({
|
|
@@ -31,36 +35,12 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
|
|
|
31
35
|
})
|
|
32
36
|
.catch(() => null)
|
|
33
37
|
: Promise.resolve(null),
|
|
38
|
+
// One count per tab, derived from CATEGORY_PAGE_TABS. Four of the nine tabs
|
|
39
|
+
// (classifieds, digital codes, live, art) had no count at all and could
|
|
40
|
+
// therefore never hide, however empty the brand was.
|
|
34
41
|
brandName
|
|
35
|
-
?
|
|
36
|
-
|
|
37
|
-
filters: sieveAnd(sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("brand", SIEVE_OP.EQ, brandName), sieveFilter("listingType", SIEVE_OP.EQ, "auction")),
|
|
38
|
-
sorts: sortBy("auctionEndDate", "ASC"),
|
|
39
|
-
page: 1,
|
|
40
|
-
pageSize: 1,
|
|
41
|
-
})
|
|
42
|
-
.catch(() => null)
|
|
43
|
-
: Promise.resolve(null),
|
|
44
|
-
brandName
|
|
45
|
-
? productRepository
|
|
46
|
-
.list({
|
|
47
|
-
filters: sieveAnd(sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("brand", SIEVE_OP.EQ, brandName), sieveFilter("listingType", SIEVE_OP.EQ, "pre-order")),
|
|
48
|
-
sorts: sortBy("createdAt", "DESC"),
|
|
49
|
-
page: 1,
|
|
50
|
-
pageSize: 1,
|
|
51
|
-
})
|
|
52
|
-
.catch(() => null)
|
|
53
|
-
: Promise.resolve(null),
|
|
54
|
-
brandName
|
|
55
|
-
? productRepository
|
|
56
|
-
.list({
|
|
57
|
-
filters: sieveAnd(sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("brand", SIEVE_OP.EQ, brandName), sieveFilter("listingType", SIEVE_OP.EQ, "prize-draw")),
|
|
58
|
-
sorts: sortBy("createdAt", "DESC"),
|
|
59
|
-
page: 1,
|
|
60
|
-
pageSize: 1,
|
|
61
|
-
})
|
|
62
|
-
.catch(() => null)
|
|
63
|
-
: Promise.resolve(null),
|
|
42
|
+
? listingTabCounts(CATEGORY_PAGE_TABS, { brandName })
|
|
43
|
+
: Promise.resolve({}),
|
|
64
44
|
// SB-UNI-D — bundles are categoryType:"bundle" rows on the categories
|
|
65
45
|
// collection, tagged to a brand via the real brandSlug field (added
|
|
66
46
|
// 2026-08-21 — previously a fragile seo.keywords string-match heuristic).
|
|
@@ -74,6 +54,9 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
|
|
|
74
54
|
// Related brands — every other active brand row, excluding this one.
|
|
75
55
|
categoriesRepository.findActiveBrands().catch(() => []),
|
|
76
56
|
brand?.slug ? getGroupsForBrand(brand.slug).catch(() => []) : Promise.resolve([]),
|
|
57
|
+
// Feature flags — accepted by BrandDetailTabs all along but never passed,
|
|
58
|
+
// so a disabled listing type still showed its tab here.
|
|
59
|
+
siteSettingsRepository.findById("global").catch(() => null),
|
|
77
60
|
]);
|
|
78
61
|
const brandBundles = brand?.slug
|
|
79
62
|
? allBundles.filter((b) => b.brandSlug === brand.slug)
|
|
@@ -85,17 +68,17 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
|
|
|
85
68
|
// configured one. The value is consumed as `style={{ backgroundColor }}` so it
|
|
86
69
|
// accepts both CSS vars and raw hex written by an admin.
|
|
87
70
|
const brandColor = brand?.display?.color || "var(--appkit-color-primary)";
|
|
71
|
+
// Keyed by tabSlug. `bundles` is overridden with the brand-scoped figure —
|
|
72
|
+
// `listingTabCounts` cannot narrow bundles by brand (a bundle's brand lives on
|
|
73
|
+
// `brandSlug`, which this page already filters on locally).
|
|
88
74
|
const counts = {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
prizeDraws: prizeDrawsResult?.total ?? 0,
|
|
75
|
+
...tabCounts,
|
|
76
|
+
products: tabCounts.products ?? brand?.metrics?.productCount ?? 0,
|
|
77
|
+
auctions: tabCounts.auctions ?? brand?.metrics?.auctionCount ?? 0,
|
|
93
78
|
bundles: brandBundles.length,
|
|
94
79
|
};
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
counts.bundles;
|
|
100
|
-
return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-[var(--appkit-color-bg)]"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "absolute inset-0", children: _jsx(MediaImage, { src: coverImage, alt: "", size: "banner" }) }), _jsx(Div, { surface: "overlay-md", className: "absolute inset-0" })] })), !hasCover && (_jsx(DynamicBgDiv, { color: brandColor, className: "absolute inset-0 opacity-10" })), _jsxs(Div, { className: "relative z-10 max-w-7xl mx-auto", paddingY: hasCover ? "y-3xl" : "y-xl", padding: "x-md", children: [_jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "sm", className: "mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.BRANDS), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Brands" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Span, { weight: "medium", className: hasCover ? "text-white" : "text-[var(--appkit-color-text)]", children: brand?.name ?? slug })] }), _jsxs(Row, { className: "mb-3", align: "center", gap: "md", children: [brand?.display?.icon && (_jsx(Span, { className: "leading-none", size: "5xl", children: brand.display.icon })), _jsxs(_Fragment, { children: [_jsx(Heading, { color: "inverse", level: 1, className: `${hasCover ? "" : "text-[var(--appkit-color-text)]"}`, size: "3xl", mdSize: "4xl", weight: "bold", children: brand?.name ?? slug }), brand?.description && typeof brand.description === "string" && !brand.description.startsWith("{") && (_jsx(Text, { color: hasCover ? "inverse" : "muted", className: `max-w-2xl mt-1 ${hasCover ? "/80" : ""}`, size: "base", children: brand.description }))] })] }), _jsxs(Row, { gap: "sm", wrap: true, className: "mt-3", children: [counts.products > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: "inverse", size: "xs", weight: "medium", className: `${hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, rounded: "full", padding: "pill-sm-tall", children: [counts.products.toLocaleString(), " ", counts.products === 1 ? "product" : "products"] })), counts.auctions > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "warning", surface: hasCover ? undefined : "warning-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [counts.auctions.toLocaleString(), " ", counts.auctions === 1 ? "auction" : "auctions"] })), counts.preOrders > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "info", surface: hasCover ? undefined : "info-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [counts.preOrders.toLocaleString(), " ", counts.preOrders === 1 ? "pre-order" : "pre-orders"] })), totalItems === 0 && (_jsx(Span, { size: "sm", className: hasCover ? "text-white/60" : "text-zinc-400", children: "No items listed yet" }))] })] })] }), (brand?.brandWebsite || brand?.brandCountry || brand?.brandFounded) && (_jsx(Section, { border: "subtle", surface: "default", className: "border-b", padding: "y-md", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "sm", weight: "semibold", color: "muted", transform: "uppercase", children: "About this brand" }), _jsxs(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden max-w-lg", children: [brand?.brandWebsite && (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-32 flex-shrink-0", color: "primary", weight: "medium", children: "Website" }), _jsx(Dd, { className: "flex-1", children: _jsx(Anchor, { href: brand.brandWebsite, tone: "brand", underline: "hover", children: brand.brandWebsite }) })] })), brand?.brandCountry && (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-32 flex-shrink-0", color: "primary", weight: "medium", children: "Country" }), _jsx(Dd, { className: "flex-1", color: "muted", children: brand.brandCountry })] })), brand?.brandFounded && (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-32 flex-shrink-0", color: "primary", weight: "medium", children: "Founded" }), _jsx(Dd, { className: "flex-1", color: "muted", children: brand.brandFounded })] }))] })] }) }) })), _jsx(CategoryHighlightsAndFaqSection, { highlights: brand?.highlights, faqs: brand?.faqs }), _jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: brandName ? (_jsx(BrandDetailTabs, { brandName: brandName, initialProductsData: productsResult ?? undefined, initialBundles: brandBundles, counts: counts })) : (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "Brand not found." })) }) }), groupedListings.length > 0 && (_jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: _jsx(GroupedListingsCarousel, { groups: groupedListings }) }) })), relatedBrands.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-t", padding: "y-lg", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 2, className: "mb-4", size: "xl", weight: "semibold", children: "Related Brands" }), _jsx(CategoryGrid, { categories: relatedBrands, getHref: (b) => String(ROUTES.PUBLIC.BRAND_DETAIL(b.slug)) })] }) }))] }));
|
|
80
|
+
// Header total — `?? 0` is right here (a pill just doesn't render), unlike the
|
|
81
|
+
// tab bar where an unknown count must keep its tab visible.
|
|
82
|
+
const totalItems = Object.values(counts).reduce((sum, n) => sum + (n ?? 0), 0);
|
|
83
|
+
return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-[var(--appkit-color-bg)]"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "absolute inset-0", children: _jsx(MediaImage, { src: coverImage, alt: "", size: "banner" }) }), _jsx(Div, { surface: "overlay-md", className: "absolute inset-0" })] })), !hasCover && (_jsx(DynamicBgDiv, { color: brandColor, className: "absolute inset-0 opacity-10" })), _jsxs(Div, { className: "relative z-10 max-w-7xl mx-auto", paddingY: hasCover ? "y-3xl" : "y-xl", padding: "x-md", children: [_jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "sm", className: "mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.BRANDS), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Brands" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Span, { weight: "medium", className: hasCover ? "text-white" : "text-[var(--appkit-color-text)]", children: brand?.name ?? slug })] }), _jsxs(Row, { className: "mb-3", align: "center", gap: "md", children: [brand?.display?.icon && (_jsx(Span, { className: "leading-none", size: "5xl", children: brand.display.icon })), _jsxs(_Fragment, { children: [_jsx(Heading, { color: "inverse", level: 1, className: `${hasCover ? "" : "text-[var(--appkit-color-text)]"}`, size: "3xl", mdSize: "4xl", weight: "bold", children: brand?.name ?? slug }), brand?.description && typeof brand.description === "string" && !brand.description.startsWith("{") && (_jsx(Text, { color: hasCover ? "inverse" : "muted", className: `max-w-2xl mt-1 ${hasCover ? "/80" : ""}`, size: "base", children: brand.description }))] })] }), _jsxs(Row, { gap: "sm", wrap: true, className: "mt-3", children: [(counts.products ?? 0) > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: "inverse", size: "xs", weight: "medium", className: `${hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, rounded: "full", padding: "pill-sm-tall", children: [counts.products.toLocaleString(), " ", counts.products === 1 ? "product" : "products"] })), (counts.auctions ?? 0) > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "warning", surface: hasCover ? undefined : "warning-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [counts.auctions.toLocaleString(), " ", counts.auctions === 1 ? "auction" : "auctions"] })), (counts["pre-orders"] ?? 0) > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "info", surface: hasCover ? undefined : "info-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [counts["pre-orders"].toLocaleString(), " ", counts["pre-orders"] === 1 ? "pre-order" : "pre-orders"] })), totalItems === 0 && (_jsx(Span, { size: "sm", className: hasCover ? "text-white/60" : "text-zinc-400", children: "No items listed yet" }))] })] })] }), (brand?.brandWebsite || brand?.brandCountry || brand?.brandFounded) && (_jsx(Section, { border: "subtle", surface: "default", className: "border-b", padding: "y-md", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "sm", weight: "semibold", color: "muted", transform: "uppercase", children: "About this brand" }), _jsxs(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden max-w-lg", children: [brand?.brandWebsite && (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-32 flex-shrink-0", color: "primary", weight: "medium", children: "Website" }), _jsx(Dd, { className: "flex-1", children: _jsx(Anchor, { href: brand.brandWebsite, tone: "brand", underline: "hover", children: brand.brandWebsite }) })] })), brand?.brandCountry && (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-32 flex-shrink-0", color: "primary", weight: "medium", children: "Country" }), _jsx(Dd, { className: "flex-1", color: "muted", children: brand.brandCountry })] })), brand?.brandFounded && (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-32 flex-shrink-0", color: "primary", weight: "medium", children: "Founded" }), _jsx(Dd, { className: "flex-1", color: "muted", children: brand.brandFounded })] }))] })] }) }) })), _jsx(CategoryHighlightsAndFaqSection, { highlights: brand?.highlights, faqs: brand?.faqs }), _jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: brandName ? (_jsx(BrandDetailTabs, { brandName: brandName, initialProductsData: productsResult ?? undefined, initialBundles: brandBundles, counts: counts, enabledListingTypes: enabledListingTypes(settings), enabledCategoryTypes: enabledCategoryTypes(settings) })) : (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "Brand not found." })) }) }), groupedListings.length > 0 && (_jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: _jsx(GroupedListingsCarousel, { groups: groupedListings }) }) })), relatedBrands.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-t", padding: "y-lg", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 2, className: "mb-4", size: "xl", weight: "semibold", children: "Related Brands" }), _jsx(CategoryGrid, { categories: relatedBrands, getHref: (b) => String(ROUTES.PUBLIC.BRAND_DETAIL(b.slug)) })] }) }))] }));
|
|
101
84
|
}
|
|
@@ -4,13 +4,13 @@ export interface BrandDetailTabsProps {
|
|
|
4
4
|
brandName: string;
|
|
5
5
|
initialProductsData?: any;
|
|
6
6
|
initialBundles?: CategoryDocument[];
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
/**
|
|
8
|
+
* tabSlug -> row count. `undefined` means the count could not be resolved,
|
|
9
|
+
* NOT zero — such a tab stays visible so a failed query can never hide real
|
|
10
|
+
* inventory (Root Cause #59). Keyed by tab id; the previous hand-named shape
|
|
11
|
+
* covered only 5 of 9 tabs (Root Cause #61).
|
|
12
|
+
*/
|
|
13
|
+
counts?: Record<string, number | undefined>;
|
|
14
14
|
/** Enabled listing types (e.g. ["standard","auction"]). When omitted, all tabs shown. */
|
|
15
15
|
enabledListingTypes?: string[];
|
|
16
16
|
/** Enabled category types (e.g. ["category","brand","bundle"]). When omitted, all tabs shown. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
|
-
import { Tabs, TabsList, TabsTrigger } from "../../../ui";
|
|
4
|
+
import { EmptyState, Tabs, TabsList, TabsTrigger } from "../../../ui";
|
|
5
5
|
import { CategoryProductsListing } from "./CategoryProductsListing";
|
|
6
6
|
import { AuctionsIndexListing } from "../../products/components/AuctionsIndexListing";
|
|
7
7
|
import { PreOrdersIndexListing } from "../../pre-orders/components/PreOrdersIndexListing";
|
|
@@ -37,16 +37,7 @@ const GENERIC_TAB_LISTING_TYPES = {
|
|
|
37
37
|
[pluginFor("art").tabSlug]: ART_STICKERS_LISTING_TYPES,
|
|
38
38
|
};
|
|
39
39
|
export function BrandDetailTabs({ brandName, initialProductsData, initialBundles = [], counts, enabledListingTypes, enabledCategoryTypes, }) {
|
|
40
|
-
const countFor = (id) =>
|
|
41
|
-
switch (id) {
|
|
42
|
-
case "products": return counts?.products;
|
|
43
|
-
case "auctions": return counts?.auctions;
|
|
44
|
-
case "pre-orders": return counts?.preOrders;
|
|
45
|
-
case "prize-draws": return counts?.prizeDraws;
|
|
46
|
-
case "bundles": return counts?.bundles;
|
|
47
|
-
default: return undefined;
|
|
48
|
-
}
|
|
49
|
-
};
|
|
40
|
+
const countFor = (id) => counts?.[id];
|
|
50
41
|
const visibleTabs = CATEGORY_PAGE_TABS.filter((t) => {
|
|
51
42
|
const mapping = TAB_TYPE_MAP[t.id];
|
|
52
43
|
// A tab with no mapping has no render branch below and would show a blank
|
|
@@ -63,11 +54,16 @@ export function BrandDetailTabs({ brandName, initialProductsData, initialBundles
|
|
|
63
54
|
if (!enabledCategoryTypes.includes(mapping.type))
|
|
64
55
|
return false;
|
|
65
56
|
}
|
|
66
|
-
// Hide a tab only when its count is known and explicitly zero.
|
|
57
|
+
// Hide a tab only when its count is known and explicitly zero. `undefined`
|
|
58
|
+
// now means the count query FAILED (every tab is counted), so the tab stays
|
|
59
|
+
// visible rather than hiding real listings behind a swallowed error.
|
|
67
60
|
const count = countFor(t.id);
|
|
68
61
|
return count === undefined || count > 0;
|
|
69
62
|
});
|
|
70
63
|
const firstTabId = (visibleTabs[0]?.id ?? "products");
|
|
71
64
|
const [activeTab, setActiveTab] = useState(firstTabId);
|
|
65
|
+
if (visibleTabs.length === 0) {
|
|
66
|
+
return (_jsx(EmptyState, { title: "Nothing listed here yet", description: "This brand has no active listings right now. Check back soon." }));
|
|
67
|
+
}
|
|
72
68
|
return (_jsxs(_Fragment, { children: [_jsx(Tabs, { value: activeTab, onChange: (v) => setActiveTab(v), className: "mb-6", children: _jsx(TabsList, { children: visibleTabs.map((t) => (_jsx(TabsTrigger, { value: t.id, badge: countFor(t.id), children: t.label }, t.id))) }) }), GENERIC_TAB_LISTING_TYPES[activeTab] && (_jsx(CategoryProductsListing, { categorySlug: "", brandName: brandName, listingTypes: GENERIC_TAB_LISTING_TYPES[activeTab], initialData: activeTab === pluginFor("standard").tabSlug ? initialProductsData : undefined })), activeTab === pluginFor("auction").tabSlug && (_jsx(AuctionsIndexListing, { brandName: brandName })), activeTab === pluginFor("pre-order").tabSlug && (_jsx(PreOrdersIndexListing, { brandName: brandName })), activeTab === pluginFor("prize-draw").tabSlug && (_jsx(PrizeDrawsIndexListing, { brandName: brandName })), activeTab === "bundles" && (_jsx(CategoryBundlesListing, { initialBundles: initialBundles, brandName: brandName }))] }));
|
|
73
69
|
}
|
|
@@ -43,7 +43,7 @@ export function BundleCollage({ members, onItemClick }) {
|
|
|
43
43
|
return (_jsxs(_Fragment, { children: [_jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-2 sm:grid-cols-3 md:grid-cols-4", children: members.map((p, idx) => {
|
|
44
44
|
const cover = p.mainImage ?? p.images?.[0];
|
|
45
45
|
const href = String(ROUTES.PUBLIC.PRODUCT_DETAIL?.(p.slug ?? p.id) ?? "#");
|
|
46
|
-
return (_jsxs(Div, { className: "group relative overflow-hidden border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] transition-transform hover:scale-[1.01]", rounded: "lg", children: [
|
|
46
|
+
return (_jsxs(Div, { layout: "flex-col", className: "group relative overflow-hidden border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] transition-transform hover:scale-[1.01]", rounded: "lg", children: [_jsx(Button, { variant: "ghost", type: "button", onClick: makeBundleItemClickHandler(p, idx, onItemClick, setLightboxIndex), paddingX: "none", paddingY: "none", rounded: "none", className: "relative aspect-square w-full overflow-hidden focus-visible:ring-2 focus-visible:ring-[var(--appkit-color-primary)]", "aria-label": `View ${p.title} in lightbox`, children: _jsxs(Div, { className: "relative aspect-square w-full", children: [cover ? (_jsx(MediaImage, { src: cover, alt: p.title, size: "card", className: "transition-transform group-hover:scale-105" })) : (_jsx(Row, { textSize: "3xl", surface: "muted", className: "absolute inset-0", align: "center", justify: "center", children: PLACEHOLDER_EMOJI })), _jsxs(Div, { textWeight: "semibold", textSize: "xs", surface: "overlay-xl", padding: "chip-2xs", className: "absolute left-2 top-2 text-white", rounded: "default", children: ["#", idx + 1] })] }) }), _jsxs(Div, { padding: "xs", children: [_jsx(Link, { href: href, className: "line-clamp-2 text-[length:var(--appkit-text-sm)] font-medium hover:underline", children: p.title }), _jsx(Text, { className: "mt-0.5 text-[var(--appkit-color-text-muted)]", size: "xs", children: formatCurrency(p.price ?? 0, p.currency ?? "INR") })] })] }, p.id));
|
|
47
47
|
}) }), _jsx(ImageLightbox, { images: galleryImages, activeIndex: lightboxIndex, onClose: () => setLightboxIndex(null), onNavigate: (i) => setLightboxIndex(i), showThumbnails: true })] }));
|
|
48
48
|
}
|
|
49
49
|
export default BundleCollage;
|
|
@@ -11,6 +11,10 @@ import { CategoryGrid } from "./CategoryGrid";
|
|
|
11
11
|
import { CategoryHighlightsAndFaqSection } from "./CategoryHighlightsAndFaqSection";
|
|
12
12
|
import { GroupedListingsCarousel } from "../../grouped/components/GroupedListingsCarousel";
|
|
13
13
|
import { getGroupsForCategory } from "../../../_internal/server/features/grouped/data";
|
|
14
|
+
import { listingTabCounts, } from "../../../_internal/server/features/products/listing-tab-counts";
|
|
15
|
+
import { CATEGORY_PAGE_TABS } from "../../products/constants/listing-tabs";
|
|
16
|
+
import { enabledCategoryTypes, enabledListingTypes } from "../../../_internal/shared/listing-types/feature-flags";
|
|
17
|
+
import { siteSettingsRepository } from "../../../repositories";
|
|
14
18
|
const __O = {
|
|
15
19
|
hidden: "overflow-hidden",
|
|
16
20
|
xAuto: "overflow-x-auto",
|
|
@@ -19,16 +23,15 @@ export async function CategoryDetailPageView({ slug }) {
|
|
|
19
23
|
const category = await categoriesRepository
|
|
20
24
|
.getCategoryBySlug(slug)
|
|
21
25
|
.catch(() => undefined);
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// array-contains
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
const [productsResult, auctionsCountResult, preOrdersCountResult, prizeDrawsCountResult, bundlesResult, childCategories, rootSiblingCategories, groupedListings] = await Promise.all([
|
|
26
|
+
// Products carry their FULL ancestor chain in `categorySlugs`, so matching on
|
|
27
|
+
// this category's own id already returns the whole subtree — no descendant
|
|
28
|
+
// expansion needed. Expanding it was also a latent outage: `categoriesIn` is
|
|
29
|
+
// applied as `array-contains-any`, which Firestore caps at 30 values, and
|
|
30
|
+
// every caller here wraps the query in `.catch(() => null)` — so a category
|
|
31
|
+
// tree deeper than a couple of levels would have turned into a silently blank
|
|
32
|
+
// page rather than an error (Root Cause #59).
|
|
33
|
+
const categoriesIn = category?.id ? [category.id] : null;
|
|
34
|
+
const [productsResult, tabCounts, bundlesResult, childCategories, rootSiblingCategories, groupedListings, settings] = await Promise.all([
|
|
32
35
|
categoriesIn
|
|
33
36
|
? productRepository
|
|
34
37
|
.list({
|
|
@@ -39,36 +42,12 @@ export async function CategoryDetailPageView({ slug }) {
|
|
|
39
42
|
}, { categoriesIn })
|
|
40
43
|
.catch(() => null)
|
|
41
44
|
: Promise.resolve(null),
|
|
45
|
+
// One count per tab, derived from CATEGORY_PAGE_TABS itself — so every
|
|
46
|
+
// listing type is counted and a type with nothing in this category hides.
|
|
47
|
+
// Four of the ten tabs used to have no count at all and could never hide.
|
|
42
48
|
categoriesIn
|
|
43
|
-
?
|
|
44
|
-
|
|
45
|
-
filters: sieveAnd(sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "auction")),
|
|
46
|
-
sorts: sortBy("auctionEndDate", "ASC"),
|
|
47
|
-
page: 1,
|
|
48
|
-
pageSize: 1,
|
|
49
|
-
}, { categoriesIn })
|
|
50
|
-
.catch(() => null)
|
|
51
|
-
: Promise.resolve(null),
|
|
52
|
-
categoriesIn
|
|
53
|
-
? productRepository
|
|
54
|
-
.list({
|
|
55
|
-
filters: sieveAnd(sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "pre-order")),
|
|
56
|
-
sorts: sortBy("createdAt", "DESC"),
|
|
57
|
-
page: 1,
|
|
58
|
-
pageSize: 1,
|
|
59
|
-
}, { categoriesIn })
|
|
60
|
-
.catch(() => null)
|
|
61
|
-
: Promise.resolve(null),
|
|
62
|
-
categoriesIn
|
|
63
|
-
? productRepository
|
|
64
|
-
.list({
|
|
65
|
-
filters: sieveAnd(sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "prize-draw")),
|
|
66
|
-
sorts: sortBy("createdAt", "DESC"),
|
|
67
|
-
page: 1,
|
|
68
|
-
pageSize: 1,
|
|
69
|
-
}, { categoriesIn })
|
|
70
|
-
.catch(() => null)
|
|
71
|
-
: Promise.resolve(null),
|
|
49
|
+
? listingTabCounts(CATEGORY_PAGE_TABS, { categoriesIn })
|
|
50
|
+
: Promise.resolve({}),
|
|
72
51
|
// SB-UNI-D — bundles fetched from the categories collection. We pull
|
|
73
52
|
// all active bundle rows; the carousel filters by category affinity.
|
|
74
53
|
categoriesIn
|
|
@@ -86,15 +65,34 @@ export async function CategoryDetailPageView({ slug }) {
|
|
|
86
65
|
? categoriesRepository.getCategoriesByRootId(category.rootId).catch(() => [])
|
|
87
66
|
: Promise.resolve([]),
|
|
88
67
|
category?.slug ? getGroupsForCategory(category.slug).catch(() => []) : Promise.resolve([]),
|
|
68
|
+
// Listing/category-type feature flags. This page accepted the props all
|
|
69
|
+
// along but never passed them, so an admin disabling a listing type still
|
|
70
|
+
// saw its tab here while the store page correctly hid it.
|
|
71
|
+
siteSettingsRepository.findById("global").catch(() => null),
|
|
89
72
|
]);
|
|
90
73
|
const relatedCategories = rootSiblingCategories.filter((c) => c.id !== category?.id && (!c.categoryType || c.categoryType === "category"));
|
|
91
|
-
// Stores tab —
|
|
74
|
+
// Stores tab — stores whose storeCategory is this category or ANY descendant.
|
|
75
|
+
//
|
|
76
|
+
// `storeCategory` is a single slug with no ancestor chain (unlike a product's
|
|
77
|
+
// categorySlugs), so the descendant list genuinely is needed here. It used to
|
|
78
|
+
// use direct children only, which meant a store filed under a tier-3 category
|
|
79
|
+
// was invisible on its root's Stores tab, and it issued one query per slug —
|
|
80
|
+
// an unbounded N+1. Both are fixed by pipe-joining the slugs into OR-groups:
|
|
81
|
+
// the enhanced Sieve adapter turns a same-field OR into a Firestore `in`,
|
|
82
|
+
// which caps at 30 values, hence the chunking.
|
|
92
83
|
const storeCategorySlugs = [
|
|
93
84
|
slug,
|
|
94
|
-
...
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
85
|
+
...(await categoriesRepository
|
|
86
|
+
.getDescendantIds(category?.id ?? "")
|
|
87
|
+
.catch(() => [])),
|
|
88
|
+
].filter(Boolean);
|
|
89
|
+
const SLUG_CHUNK = 30;
|
|
90
|
+
const slugChunks = [];
|
|
91
|
+
for (let i = 0; i < storeCategorySlugs.length; i += SLUG_CHUNK) {
|
|
92
|
+
slugChunks.push(storeCategorySlugs.slice(i, i + SLUG_CHUNK));
|
|
93
|
+
}
|
|
94
|
+
const storeResults = await Promise.all(slugChunks.map((chunk) => storeRepository
|
|
95
|
+
.listStores({ filters: sieveFilter("storeCategory", SIEVE_OP.EQ, chunk.join("|")), page: 1, pageSize: 50 }, true)
|
|
98
96
|
.catch(() => null)));
|
|
99
97
|
const seen = new Set();
|
|
100
98
|
const categoryStores = storeResults
|
|
@@ -122,22 +120,20 @@ export async function CategoryDetailPageView({ slug }) {
|
|
|
122
120
|
averageRating: s.stats?.averageRating,
|
|
123
121
|
createdAt: s.createdAt,
|
|
124
122
|
}));
|
|
125
|
-
const productCount = productsResult?.total ?? category?.metrics?.totalProductCount ?? category?.metrics?.productCount ?? 0;
|
|
126
|
-
const auctionCount = auctionsCountResult?.total ?? category?.metrics?.totalAuctionCount ?? category?.metrics?.auctionCount ?? 0;
|
|
127
|
-
const preOrderCount = preOrdersCountResult?.total ?? 0;
|
|
128
|
-
const prizeDrawCount = prizeDrawsCountResult?.total ?? 0;
|
|
129
|
-
const bundleCount = bundlesResult?.length ?? 0;
|
|
130
123
|
const storeCount = categoryStores.length;
|
|
124
|
+
// `tabCounts` is keyed by tabSlug and already covers every listing type plus
|
|
125
|
+
// bundles; `stores` is added here because this page resolves its store list
|
|
126
|
+
// eagerly to render the tab body anyway.
|
|
127
|
+
const counts = { ...tabCounts, stores: storeCount };
|
|
128
|
+
// Header pills read the same numbers the tabs do. `?? 0` is safe here (a pill
|
|
129
|
+
// simply doesn't render) — unlike the tab bar, where an unknown count must
|
|
130
|
+
// stay visible rather than collapse to "hide me".
|
|
131
|
+
const productCount = counts.products ?? category?.metrics?.totalProductCount ?? category?.metrics?.productCount ?? 0;
|
|
132
|
+
const auctionCount = counts.auctions ?? category?.metrics?.totalAuctionCount ?? category?.metrics?.auctionCount ?? 0;
|
|
133
|
+
const preOrderCount = counts["pre-orders"] ?? 0;
|
|
131
134
|
const coverImage = category?.display?.coverImage;
|
|
132
135
|
const hasCover = Boolean(coverImage);
|
|
133
136
|
return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-[var(--appkit-color-bg)]"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "absolute inset-0", children: _jsx(MediaImage, { src: coverImage, alt: "", size: "banner" }) }), _jsx(Div, { surface: "overlay-md", className: "absolute inset-0" })] })), _jsxs(Div, { className: "relative z-10 max-w-7xl mx-auto", paddingY: hasCover ? "y-3xl" : "y-xl", padding: "x-md", children: [_jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "sm", className: "mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORIES), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-[var(--appkit-color-text-muted)] hover:text-primary-600 transition-colors", children: "Categories" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Span, { weight: "medium", className: hasCover ? "text-white" : "text-[var(--appkit-color-text)]", children: category?.name ?? slug })] }), _jsx(Heading, { color: "inverse", level: 1, className: `mb-2 ${hasCover ? "" : "text-[var(--appkit-color-text)]"}`, size: "3xl", mdSize: "4xl", weight: "bold", children: category?.name ?? slug }), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx(Text, { color: hasCover ? "inverse" : "muted", className: `max-w-2xl mb-4 ${hasCover ? "/80" : ""}`, size: "base", children: category.description })), _jsxs(Row, { wrap: true, gap: "sm", children: [productCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: "inverse", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400", rounded: "full", padding: "pill-sm-tall", children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), auctionCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "warning", surface: hasCover ? undefined : "warning-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "info", surface: hasCover ? undefined : "info-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] })), storeCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "success", surface: hasCover ? undefined : "success-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [storeCount.toLocaleString(), " ", storeCount === 1 ? "store" : "stores"] }))] })] })] }), _jsx(CategoryHighlightsAndFaqSection, { highlights: category?.highlights, faqs: category?.faqs }), childCategories.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-b", children: _jsx(Div, { className: "max-w-7xl mx-auto", padding: "inline", children: _jsx(Div, { layout: "flex", gap: "2", className: `.5 ${__O.xAuto} [scrollbar-width:none]`, padding: "b-2xs", children: childCategories.map((child) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(child.slug)),
|
|
134
137
|
// audit-responsive-wrap-ok: deliberate horizontal-scroll chip row (parent has overflow-x-auto) — each chip staying single-line is the intended design, not hidden content
|
|
135
|
-
className: "flex-shrink-0 flex items-center gap-[var(--appkit-space-1-5)] rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-elevated)] px-[var(--appkit-space-4)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-text-muted)] hover:border-primary hover:text-primary transition-colors whitespace-nowrap", children: [child.display?.icon && (_jsx(Span, { className: "leading-none", children: child.display.icon })), child.name, (child.metrics?.productCount ?? 0) > 0 && (_jsx(Span, { size: "xs", color: "muted", children: (child.metrics?.productCount ?? 0).toLocaleString() }))] }, child.id))) }) }) })), _jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: _jsx(CategoryDetailTabs, { categorySlug: slug, categoryId: category?.id, initialProductsData: productsResult ?? undefined, initialBundles: bundlesResult ?? [], initialStores: categoryStores, counts: {
|
|
136
|
-
products: productCount,
|
|
137
|
-
auctions: auctionCount,
|
|
138
|
-
preOrders: preOrderCount,
|
|
139
|
-
prizeDraws: prizeDrawCount,
|
|
140
|
-
bundles: bundleCount,
|
|
141
|
-
stores: storeCount,
|
|
142
|
-
} }) }) }), groupedListings.length > 0 && (_jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: _jsx(GroupedListingsCarousel, { groups: groupedListings }) }) })), relatedCategories.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-t", padding: "y-lg", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 2, className: "mb-4", size: "xl", weight: "semibold", children: "Related Categories" }), _jsx(CategoryGrid, { categories: relatedCategories, getHref: (c) => String(ROUTES.PUBLIC.CATEGORY_DETAIL(c.slug)) })] }) }))] }));
|
|
138
|
+
className: "flex-shrink-0 flex items-center gap-[var(--appkit-space-1-5)] rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-elevated)] px-[var(--appkit-space-4)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-text-muted)] hover:border-primary hover:text-primary transition-colors whitespace-nowrap", children: [child.display?.icon && (_jsx(Span, { className: "leading-none", children: child.display.icon })), child.name, (child.metrics?.productCount ?? 0) > 0 && (_jsx(Span, { size: "xs", color: "muted", children: (child.metrics?.productCount ?? 0).toLocaleString() }))] }, child.id))) }) }) })), _jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: _jsx(CategoryDetailTabs, { categorySlug: slug, categoryId: category?.id, initialProductsData: productsResult ?? undefined, initialBundles: bundlesResult ?? [], initialStores: categoryStores, counts: counts, enabledListingTypes: enabledListingTypes(settings), enabledCategoryTypes: enabledCategoryTypes(settings) }) }) }), groupedListings.length > 0 && (_jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: _jsx(GroupedListingsCarousel, { groups: groupedListings }) }) })), relatedCategories.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-t", padding: "y-lg", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 2, className: "mb-4", size: "xl", weight: "semibold", children: "Related Categories" }), _jsx(CategoryGrid, { categories: relatedCategories, getHref: (c) => String(ROUTES.PUBLIC.CATEGORY_DETAIL(c.slug)) })] }) }))] }));
|
|
143
139
|
}
|