@mohasinac/appkit 4.13.0 → 4.14.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 +6 -0
- 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/checkout/actions.js +6 -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/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/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 +45 -9
- 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 +15 -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/blog/schema.d.ts +2 -2
- package/dist/_internal/shared/features/cart/schema.d.ts +12 -12
- 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 +32 -32
- 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 +66 -0
- package/dist/_internal/shared/history/history.js +158 -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 +7 -5
- package/dist/client.js +2 -2
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/constants/field-names.d.ts +49 -2
- package/dist/constants/field-names.js +53 -2
- 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/UserOrdersView.js +7 -0
- 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/AdminClassifiedView.js +8 -3
- package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
- package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
- package/dist/features/admin/components/AdminLiveView.js +8 -3
- package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
- package/dist/features/admin/components/AdminOffersView.js +186 -0
- package/dist/features/admin/components/AdminOrdersView.js +10 -1
- package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
- package/dist/features/admin/components/AdminProductsView.js +16 -29
- 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/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +86 -0
- package/dist/features/admin/constants/filter-tabs.js +55 -0
- package/dist/features/admin/schemas/firestore.d.ts +122 -3
- package/dist/features/admin/schemas/firestore.js +1 -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/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/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/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/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/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/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/index.d.ts +4 -4
- 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 +2 -2
- 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/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 +116 -22
- package/dist/features/orders/schemas/firestore.d.ts +117 -0
- package/dist/features/orders/schemas/firestore.js +30 -0
- package/dist/features/orders/schemas/index.d.ts +18 -18
- 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/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 +66 -66
- package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
- 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/search/schemas/index.d.ts +4 -4
- package/dist/features/seller/actions/offer-actions.js +25 -2
- package/dist/features/seller/components/SellerArtView.js +5 -0
- package/dist/features/seller/components/SellerAuctionsView.js +13 -15
- package/dist/features/seller/components/SellerClassifiedView.js +5 -0
- package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
- package/dist/features/seller/components/SellerLiveView.js +5 -0
- package/dist/features/seller/components/SellerOrdersView.js +31 -9
- package/dist/features/seller/components/SellerPayoutSettingsView.js +19 -18
- 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 +73 -16
- package/dist/features/seller/repository/offer.repository.js +38 -3
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/seller/schemas/offer-forms.d.ts +25 -0
- package/dist/features/seller/schemas/offer-forms.js +21 -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/entity-form.d.ts +109 -0
- package/dist/features/shell/entity-form.js +96 -0
- package/dist/features/shell/index.d.ts +4 -0
- package/dist/features/shell/index.js +2 -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 +105 -1
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +13 -10
- package/dist/index.js +14 -9
- package/dist/next/routing/route-map.d.ts +7 -1
- package/dist/next/routing/route-map.js +5 -0
- 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/registry.d.ts +68 -68
- 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/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 +32 -27
- package/dist/seed/products-art-seed-data.js +50 -12
- package/dist/seed/products-auctions-seed-data.js +97 -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 +4 -3
- package/dist/server.js +8 -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/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 -2
|
@@ -1,74 +1,71 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useTransition } from "react";
|
|
4
|
-
import { Button, Div,
|
|
4
|
+
import { Button, Div, LoginRequiredModal, Modal, Span, Stack, Text, } from "../../../ui";
|
|
5
|
+
import { Form } from "../../../ui/components/Form";
|
|
6
|
+
import { FieldInput } from "../../../ui/forms/FieldInput";
|
|
7
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
8
|
+
import { applyZodIssues } from "../../../ui/forms/FormShell";
|
|
9
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
|
+
import { makeOfferFormSchema } from "../../seller/schemas/offer-forms";
|
|
11
|
+
import { DEFAULT_MIN_OFFER_PERCENT, minOfferAmount, } from "../../../_internal/shared/features/offers/config";
|
|
5
12
|
import { isAuthError } from "../../../utils/auth-error";
|
|
6
|
-
import { formatCurrency
|
|
13
|
+
import { formatCurrency } from "../../../utils/number.formatter";
|
|
7
14
|
import { normalizeError } from "../../../errors/normalize";
|
|
8
15
|
const CLS_SUCCESS_BOX = "rounded-xl border border-success dark:border-success bg-success-surface p-[var(--appkit-space-4)] text-left space-y-1";
|
|
9
16
|
const CLS_PENDING_BOX = "rounded-xl border border-warning dark:border-warning bg-warning-surface dark:bg-warning-surface p-[var(--appkit-space-4)] text-left space-y-1";
|
|
10
17
|
const CLS_PENDING_TITLE = "text-[length:var(--appkit-text-sm)] font-medium text-warning dark:text-warning";
|
|
11
18
|
const CLS_PENDING_BODY = "text-[length:var(--appkit-text-xs)] text-warning dark:text-warning";
|
|
19
|
+
/**
|
|
20
|
+
* "You already have an offer on this listing" is a legitimate, expected outcome
|
|
21
|
+
* rather than a failure, so it gets its own panel instead of an error message —
|
|
22
|
+
* but the server signals it through an ordinary rejection, so it has to be
|
|
23
|
+
* recognised from the message. Matched on both the human string and the error
|
|
24
|
+
* code because `makeOffer` throws `ERROR_MESSAGES.OFFER.ACTIVE_OFFER_EXISTS`
|
|
25
|
+
* while `OFFER_ERROR_CODES.ALREADY_ACTIVE` is what a coded rejection carries.
|
|
26
|
+
*/
|
|
12
27
|
function isActiveOfferError(msg) {
|
|
13
28
|
return msg.includes("active offer") || msg.includes("ACTIVE_OFFER");
|
|
14
29
|
}
|
|
15
|
-
export function MakeOfferButton({ productId, listedPrice, currency, minOfferPercent =
|
|
30
|
+
export function MakeOfferButton({ productId, listedPrice, currency, minOfferPercent = DEFAULT_MIN_OFFER_PERCENT, onMakeOffer, className = "", }) {
|
|
16
31
|
const [state, setState] = useState("idle");
|
|
17
|
-
const [errorMsg, setErrorMsg] = useState("");
|
|
18
32
|
const [showLoginModal, setShowLoginModal] = useState(false);
|
|
19
33
|
const [isPending, startTransition] = useTransition();
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
const fmt = (n) => (currency ? formatCurrency(n, currency) : `₹${n.toLocaleString()}`);
|
|
35
|
+
// The SAME floor the server enforces — shared so the input can never seed or
|
|
36
|
+
// accept an amount `makeOffer` would then reject.
|
|
37
|
+
const minOffer = minOfferAmount(listedPrice, minOfferPercent);
|
|
38
|
+
const schema = makeOfferFormSchema({ listedPrice, minOffer, formatAmount: fmt });
|
|
39
|
+
const [offerAmount, setOfferAmount] = useState(String(Math.max(minOfferAmount(listedPrice, 90), minOffer)));
|
|
23
40
|
const [buyerNote, setBuyerNote] = useState("");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
setState("idle");
|
|
30
|
-
}
|
|
31
|
-
function handleAmountChange(raw) {
|
|
32
|
-
const parsed = parseInt(raw.replace(/[^0-9]/g, ""), 10);
|
|
33
|
-
if (!isNaN(parsed))
|
|
34
|
-
setOfferAmount(parsed);
|
|
35
|
-
}
|
|
36
|
-
function handleOfferError(err) {
|
|
37
|
-
if (isAuthError(err)) {
|
|
38
|
-
setState("idle");
|
|
39
|
-
setShowLoginModal(true);
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
43
|
-
if (isActiveOfferError(msg)) {
|
|
44
|
-
setState("pending");
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
setErrorMsg(msg || "Could not send offer. Please try again.");
|
|
48
|
-
setState("error");
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function handleSubmit() {
|
|
53
|
-
if (offerAmount < minOffer) {
|
|
54
|
-
setErrorMsg(`Minimum offer is ${fmt(minOffer)}`);
|
|
55
|
-
setState("error");
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
if (offerAmount >= listedPrice) {
|
|
59
|
-
setErrorMsg(`Offer must be below the listed price of ${fmt(listedPrice)}`);
|
|
60
|
-
setState("error");
|
|
41
|
+
function submit(setFieldError, clearErrors) {
|
|
42
|
+
clearErrors();
|
|
43
|
+
const parsed = schema.safeParse({ offerAmount, buyerNote: buyerNote || undefined });
|
|
44
|
+
if (!parsed.success) {
|
|
45
|
+
applyZodIssues(parsed.error.issues, setFieldError);
|
|
61
46
|
return;
|
|
62
47
|
}
|
|
63
|
-
setState("loading");
|
|
64
48
|
startTransition(async () => {
|
|
65
49
|
try {
|
|
66
|
-
await onMakeOffer(productId, offerAmount, buyerNote
|
|
50
|
+
await onMakeOffer(productId, parsed.data.offerAmount, parsed.data.buyerNote);
|
|
67
51
|
setState("success");
|
|
68
52
|
}
|
|
69
53
|
catch (err) {
|
|
70
54
|
void normalizeError(err);
|
|
71
|
-
|
|
55
|
+
if (isAuthError(err)) {
|
|
56
|
+
setState("idle");
|
|
57
|
+
setShowLoginModal(true);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
61
|
+
if (isActiveOfferError(msg)) {
|
|
62
|
+
setState("pending");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Server-side rejections land on the amount field — it is the only
|
|
66
|
+
// field the server can actually be objecting to, and Rule #9.6 wants
|
|
67
|
+
// them inline rather than in a banner.
|
|
68
|
+
setFieldError("offerAmount", msg || "Could not send offer. Please try again.");
|
|
72
69
|
}
|
|
73
70
|
});
|
|
74
71
|
}
|
|
@@ -78,6 +75,8 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
|
|
|
78
75
|
if (state === "pending") {
|
|
79
76
|
return (_jsxs(Div, { className: `${CLS_PENDING_BOX} ${className}`, children: [_jsx(Span, { size: "lg", children: "\u23F3" }), _jsx(Text, { className: CLS_PENDING_TITLE, children: "Offer Pending" }), _jsx(Text, { className: CLS_PENDING_BODY, children: "You already have an offer on this item. Check My Offers for updates." })] }));
|
|
80
77
|
}
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", border: "strong", className: `w-full ${className}`, onClick: () => setState("open"), children: "Make Offer" }), _jsx(Modal, { isOpen: state === "open", onClose: () => setState("idle"), size: "md", title: "Make an offer", children: _jsx(Form, { onSubmit: (e) => e.preventDefault(), children: ({ setFieldError, clearErrors }) => (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsx(FieldInput, { name: "offerAmount", type: "number", label: "Your offer amount", required: true, value: offerAmount, onChange: (value) => {
|
|
79
|
+
setOfferAmount(value);
|
|
80
|
+
clearErrors();
|
|
81
|
+
}, min: minOffer, max: listedPrice - 1, step: 1, "aria-label": "Offer amount" }), _jsxs(Text, { size: "xs", color: "faint", children: ["Must be between ", fmt(minOffer), " and ", fmt(listedPrice - 1)] }), _jsx(FieldInput, { name: "buyerNote", type: "text", label: "Note to seller (optional)", value: buyerNote, onChange: setBuyerNote, placeholder: "E.g. Bundle deal, long-time fan\u2026", maxLength: 300, "aria-label": "Note to seller" }), _jsx(Text, { size: "xs", color: "muted", children: "The seller will accept, decline, or suggest a counter price." }), _jsx(FormErrorSummary, {}), _jsxs(Stack, { gap: "xs", children: [_jsx(Button, { action: ACTIONS.PRODUCT["make-offer"], variant: "primary", size: "sm", type: "button", isLoading: isPending, disabled: isPending, onClick: () => submit(setFieldError, clearErrors), children: isPending ? "Sending…" : `Send offer of ${fmt(Number(offerAmount) || 0)}` }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setState("idle"), disabled: isPending, children: "Cancel" })] })] })) }) })] }));
|
|
83
82
|
}
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { PrizeDrawItem } from "../schemas/firestore";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* The subset of a prize this collage actually renders.
|
|
5
|
+
*
|
|
6
|
+
* Declared structurally, and deliberately narrower than `PrizeDrawItem`, so a
|
|
7
|
+
* lottery slot can be mapped onto it without inventing the fields it does not
|
|
8
|
+
* have (`condition` is required on `PrizeDrawItem`; a lottery slot has no such
|
|
9
|
+
* concept, and faking one would be a shape lie). `PrizeDrawItem` already
|
|
10
|
+
* satisfies this interface, so every existing caller is unaffected.
|
|
11
|
+
*/
|
|
12
|
+
export interface CollagePrizeItem {
|
|
13
|
+
itemNumber: number;
|
|
14
|
+
title: string;
|
|
15
|
+
images?: string[];
|
|
16
|
+
estimatedValue?: number;
|
|
17
|
+
isWon?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface PrizeDrawCollageProps<T extends CollagePrizeItem = PrizeDrawItem> {
|
|
20
|
+
items: T[];
|
|
5
21
|
highlightItemNumber?: number;
|
|
6
22
|
/** Custom click handler — overrides the built-in lightbox. */
|
|
7
|
-
onItemClick?: (item:
|
|
23
|
+
onItemClick?: (item: T) => void;
|
|
8
24
|
/** Defaults to "Won". Use for localisation. */
|
|
9
25
|
wonLabel?: string;
|
|
10
26
|
/**
|
|
@@ -15,5 +31,5 @@ export interface PrizeDrawCollageProps {
|
|
|
15
31
|
*/
|
|
16
32
|
hideWonState?: boolean;
|
|
17
33
|
}
|
|
18
|
-
export declare function PrizeDrawCollage({ items, highlightItemNumber, onItemClick, wonLabel, hideWonState, }: PrizeDrawCollageProps): React.JSX.Element;
|
|
34
|
+
export declare function PrizeDrawCollage<T extends CollagePrizeItem = PrizeDrawItem>({ items, highlightItemNumber, onItemClick, wonLabel, hideWonState, }: PrizeDrawCollageProps<T>): React.JSX.Element;
|
|
19
35
|
export default PrizeDrawCollage;
|
|
@@ -55,7 +55,15 @@ export function PrizeDrawCollage({ items, highlightItemNumber, onItemClick, wonL
|
|
|
55
55
|
const cover = it.images?.[0];
|
|
56
56
|
const isHighlight = highlightItemNumber === it.itemNumber;
|
|
57
57
|
return (_jsxs(Button, { variant: "ghost", type: "button", onClick: makePrizeItemClickHandler(it, idx, onItemClick, setLightboxIndex), rounded: "lg", paddingX: "none", paddingY: "none", className: [
|
|
58
|
-
|
|
58
|
+
// `flex-col items-stretch` reaches the real children only because
|
|
59
|
+
// `.appkit-button__content` declares `flex-direction: inherit` /
|
|
60
|
+
// `align-items: inherit`. Without them the square tile and the caption
|
|
61
|
+
// become side-by-side row items and the tile shrinks to ~half width.
|
|
62
|
+
// `items-stretch` is load-bearing: the span would otherwise inherit
|
|
63
|
+
// `center` from `.appkit-button` and the caption would ignore `text-left`.
|
|
64
|
+
// Never add a bare `block` / `flex` / `hidden` utility here — see
|
|
65
|
+
// Root Cause #68 and scripts/audit-primitive-child-wrappers.mjs.
|
|
66
|
+
"group relative flex-col items-stretch overflow-hidden border bg-[var(--appkit-color-surface)] text-left transition-transform",
|
|
59
67
|
"border-[var(--appkit-color-border)]",
|
|
60
68
|
isHighlight
|
|
61
69
|
? "ring-2 ring-offset-2 ring-[var(--appkit-color-primary)] scale-[1.02]"
|
|
@@ -11,7 +11,9 @@ import { MarketplacePrizeDrawCard } from "./MarketplacePrizeDrawCard";
|
|
|
11
11
|
import { ProductFilters } from "./ProductFilters";
|
|
12
12
|
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
13
13
|
import { PRIZE_DRAW_SORT_OPTIONS } from "../constants/sieve";
|
|
14
|
-
import {
|
|
14
|
+
import { AVAILABILITY_VALUES } from "../../../constants/field-names";
|
|
15
|
+
import { AvailabilityTabs } from "./AvailabilityTabs";
|
|
16
|
+
const PRIZE_DRAW_LISTING_TYPES = ["prize-draw"];
|
|
15
17
|
const __P = {
|
|
16
18
|
p3: "p-[var(--appkit-space-3)]",
|
|
17
19
|
};
|
|
@@ -28,7 +30,8 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
|
|
|
28
30
|
const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
|
|
29
31
|
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
30
32
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
31
|
-
const
|
|
33
|
+
const availability = table.get(TABLE_KEYS.AVAILABILITY) ||
|
|
34
|
+
AVAILABILITY_VALUES.AVAILABLE;
|
|
32
35
|
const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
|
|
33
36
|
const { categories } = useCategoryTree();
|
|
34
37
|
const categoryOptions = categoriesToFacetOptions(categories);
|
|
@@ -43,11 +46,11 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
|
|
|
43
46
|
setFilterOpen(false);
|
|
44
47
|
}, [onFilterApply]);
|
|
45
48
|
const resetAll = useCallback(() => {
|
|
46
|
-
onResetAll({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.
|
|
49
|
+
onResetAll({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.AVAILABILITY]: "" });
|
|
47
50
|
setSearchInput("");
|
|
48
51
|
}, [onResetAll]);
|
|
49
52
|
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
50
|
-
|
|
53
|
+
availability !== AVAILABILITY_VALUES.AVAILABLE ||
|
|
51
54
|
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
52
55
|
filterActiveCount > 0;
|
|
53
56
|
const revealFilter = (table.get(TABLE_KEYS.PRIZE_REVEAL_STATUS) || undefined);
|
|
@@ -64,13 +67,14 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
|
|
|
64
67
|
page: table.getNumber(TABLE_KEYS.PAGE, 1),
|
|
65
68
|
perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, 24),
|
|
66
69
|
listingType: "prize-draw",
|
|
70
|
+
availability,
|
|
67
71
|
};
|
|
68
72
|
const { products: draws, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
// Closed draws used to be filtered out of the page HERE, client-side, after
|
|
74
|
+
// pagination — which silently shrank the grid below the page size and left
|
|
75
|
+
// `totalPages` counting rows the user could never see. The availability
|
|
76
|
+
// scope does it in the query instead, where the count is computed.
|
|
77
|
+
const filteredDraws = draws;
|
|
74
78
|
const commitSearch = useCallback(() => {
|
|
75
79
|
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
76
80
|
}, [searchInput, table]);
|
|
@@ -86,9 +90,7 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
|
|
|
86
90
|
return;
|
|
87
91
|
setView(v);
|
|
88
92
|
table.set(TABLE_KEYS.VIEW, v);
|
|
89
|
-
}, onResetAll: resetAll, hasActiveState: hasActiveState,
|
|
90
|
-
{ label: "Show closed", active: showClosed, onChange: (next) => table.set(TABLE_KEYS.SHOW_CLOSED, next ? "true" : "") },
|
|
91
|
-
] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : filteredDraws.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No prize draws found." })) : (_jsx(Div, { className: gridClass, children: filteredDraws.map((product) => (_jsx(MarketplacePrizeDrawCard, { product: product, variant: view }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsxs(Stack, { gap: "md", children: [_jsxs(_Fragment, { children: [_jsx(Label, { htmlFor: "prizeRevealStatusFilter", size: "xs", weight: "semibold", color: "muted", className: "block mb-1.5", children: "Reveal status" }), _jsx(Select, { id: "prizeRevealStatusFilter", options: [
|
|
93
|
+
}, onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: PRIZE_DRAW_LISTING_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : filteredDraws.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No prize draws found." })) : (_jsx(Div, { className: gridClass, children: filteredDraws.map((product) => (_jsx(MarketplacePrizeDrawCard, { product: product, variant: view }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsxs(Stack, { gap: "md", children: [_jsxs(_Fragment, { children: [_jsx(Label, { htmlFor: "prizeRevealStatusFilter", size: "xs", weight: "semibold", color: "muted", className: "block mb-1.5", children: "Reveal status" }), _jsx(Select, { id: "prizeRevealStatusFilter", options: [
|
|
92
94
|
{ value: "", label: "Any" },
|
|
93
95
|
{ value: "pending", label: "Reveal pending" },
|
|
94
96
|
{ value: "open", label: "Reveal open" },
|
|
@@ -3,7 +3,7 @@ import { Container, Main, Heading, Section, Text } from "../../../ui";
|
|
|
3
3
|
import { AdSlot } from "../../homepage/components/AdSlot";
|
|
4
4
|
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
5
5
|
import { sortBy } from "../../../constants/sort";
|
|
6
|
-
import { listPublicProducts, parsePublicProductParams,
|
|
6
|
+
import { listPublicProducts, parsePublicProductParams, defaultAvailabilityForListingTypes, } from "../../../_internal/server/features/products/list-public";
|
|
7
7
|
import { PrizeDrawsIndexListing } from "./PrizeDrawsIndexListing";
|
|
8
8
|
const LISTING_TYPES = ["prize-draw"];
|
|
9
9
|
const DEFAULT_PAGE_SIZE = 24;
|
|
@@ -29,7 +29,7 @@ export async function PrizeDrawsListingView({ searchParams = {}, }) {
|
|
|
29
29
|
listingTypes: LISTING_TYPES,
|
|
30
30
|
pageSize: DEFAULT_PAGE_SIZE,
|
|
31
31
|
sorts: DEFAULT_SORT,
|
|
32
|
-
...
|
|
32
|
+
...defaultAvailabilityForListingTypes(LISTING_TYPES),
|
|
33
33
|
}));
|
|
34
34
|
return (_jsx(Main, { children: _jsx(Section, { padding: "y-2xl", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-2", color: "primary", size: "3xl", weight: "semibold", children: "Prize Draws" }), _jsx(Text, { className: "mb-6 text-[var(--appkit-color-text-muted)]", size: "sm", children: "Fair-RNG draws for sealed Pok\u00E9mon, Hot Wheels Super Treasure Hunts, Gundam kits and more. Every winner picked by crypto.randomInt \u2014 proof on GitHub." }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(PrizeDrawsIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
|
|
35
35
|
}
|
|
@@ -4,6 +4,12 @@ export interface PrizeDrawsSectionProps {
|
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
6
|
* Renders a prize-draws strip on the homepage.
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
|
+
* Until 2026-08-24 this read `config.title` / `config.subtitle` and ignored
|
|
9
|
+
* every other field it was given — `revealStatus`, `maxItems`, `storeId`,
|
|
10
|
+
* `showCountdown` and `showEntriesRemaining` were all inert, with the count
|
|
11
|
+
* hardcoded to 8. `revealStatus` mattering most: the seeded value is `"all"`,
|
|
12
|
+
* which is why a closed, already-revealed draw sat on the homepage advertising
|
|
13
|
+
* entries nobody could buy.
|
|
8
14
|
*/
|
|
9
15
|
export declare function PrizeDrawsSection({ config, }: PrizeDrawsSectionProps): Promise<import("react").JSX.Element | null>;
|
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { normalizeError } from "../../../errors/normalize";
|
|
3
|
-
import { sieveFilter, sieveAnd, SIEVE_OP } from "@mohasinac/appkit/client";
|
|
4
2
|
import { sortBy } from "@mohasinac/appkit/client";
|
|
5
3
|
import { Container, Div, Heading, Section, Stack, Text, } from "../../../ui";
|
|
6
4
|
import { ROUTES } from "../../../next";
|
|
7
|
-
import {
|
|
5
|
+
import { listPublicProducts } from "../../../_internal/server/features/products/list-public";
|
|
6
|
+
import { AVAILABILITY_VALUES, PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
8
7
|
import { InteractiveProductCard } from "./InteractiveProductCard";
|
|
9
8
|
/**
|
|
10
9
|
* Renders a prize-draws strip on the homepage.
|
|
11
|
-
*
|
|
10
|
+
*
|
|
11
|
+
* Until 2026-08-24 this read `config.title` / `config.subtitle` and ignored
|
|
12
|
+
* every other field it was given — `revealStatus`, `maxItems`, `storeId`,
|
|
13
|
+
* `showCountdown` and `showEntriesRemaining` were all inert, with the count
|
|
14
|
+
* hardcoded to 8. `revealStatus` mattering most: the seeded value is `"all"`,
|
|
15
|
+
* which is why a closed, already-revealed draw sat on the homepage advertising
|
|
16
|
+
* entries nobody could buy.
|
|
12
17
|
*/
|
|
13
18
|
export async function PrizeDrawsSection({ config, }) {
|
|
14
19
|
const title = config.title ?? "Prize Draws";
|
|
15
20
|
const subtitle = config.subtitle ?? "Enter for a chance to win rare collectibles";
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
const revealStatus = config.revealStatus ?? "open";
|
|
22
|
+
const showAll = revealStatus === "all";
|
|
23
|
+
const result = await listPublicProducts({
|
|
24
|
+
listingTypes: [PRODUCT_FIELDS.LISTING_TYPE_VALUES.PRIZE_DRAW],
|
|
25
|
+
storeId: config.storeId,
|
|
26
|
+
// "all" means the admin deliberately wants closed draws too — an archive
|
|
27
|
+
// strip — so neither the status filter nor the availability scope applies.
|
|
28
|
+
prizeRevealStatus: showAll ? undefined : revealStatus,
|
|
29
|
+
availability: showAll ? AVAILABILITY_VALUES.ALL : AVAILABILITY_VALUES.AVAILABLE,
|
|
30
|
+
sorts: sortBy(PRODUCT_FIELDS.CREATED_AT, "DESC"),
|
|
31
|
+
page: 1,
|
|
32
|
+
pageSize: config.maxItems ?? 8,
|
|
33
|
+
});
|
|
34
|
+
const draws = (result?.items ?? []);
|
|
30
35
|
if (draws.length === 0)
|
|
31
36
|
return null;
|
|
32
37
|
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] }), _jsx(Div, { gap: "3", className: "fluid-grid-card", children: draws.map((draw) => (_jsx(InteractiveProductCard, { product: draw, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(draw.slug ?? draw.id ?? "")) }, draw.id))) })] }) }) }));
|
|
@@ -10,7 +10,10 @@ export interface ProductDetailPageViewProps {
|
|
|
10
10
|
initialProduct?: import("../schemas/firestore").ProductDocument | null;
|
|
11
11
|
/**
|
|
12
12
|
* Render prop for offer UI. Receives the resolved product fields.
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
|
+
* Called only when all three offer gates pass: the site-wide
|
|
15
|
+
* `featureFlags.offers`, the listing type's `canMakeOffer` capability, and the
|
|
16
|
+
* seller's own `product.allowOffers` opt-in.
|
|
14
17
|
*/
|
|
15
18
|
renderOfferAction?: (opts: {
|
|
16
19
|
productId: string;
|
|
@@ -40,6 +40,10 @@ import { RelatedItemsSection } from "./RelatedItemsSection";
|
|
|
40
40
|
import { computeRelatedItems, getReviewPageForProduct } from "../../../_internal/server/features/products/data";
|
|
41
41
|
import { GroupedListingsCarousel } from "../../grouped/components/GroupedListingsCarousel";
|
|
42
42
|
import { getGroupsWithItemsForProduct } from "../../../_internal/server/features/grouped/data";
|
|
43
|
+
import { getSiteSettingsGlobal } from "../../admin/utils/getSiteSettingsGlobal";
|
|
44
|
+
import { canMakeOffer } from "../../../_internal/shared/listing-types/capabilities";
|
|
45
|
+
import { normalizeListingType } from "../utils/listing-type";
|
|
46
|
+
import { DEFAULT_MIN_OFFER_PERCENT } from "../../../_internal/shared/features/offers/config";
|
|
43
47
|
// ---------------------------------------------------------------------------
|
|
44
48
|
// Helpers
|
|
45
49
|
// ---------------------------------------------------------------------------
|
|
@@ -145,8 +149,14 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
|
|
|
145
149
|
? p.customSections
|
|
146
150
|
: [];
|
|
147
151
|
const allowOffers = p.allowOffers === true;
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
// Was `p.type === "simple"`. `ProductDocument` has no `type` field at all —
|
|
153
|
+
// the check passed only because the `?? "simple"` fallback made it vacuously
|
|
154
|
+
// true, so it was a gate that could never fail and could never be right.
|
|
155
|
+
// `listingType` is the real discriminator.
|
|
156
|
+
const offerableType = canMakeOffer(normalizeListingType(p));
|
|
157
|
+
const minOfferPercent = typeof p.minOfferPercent === "number"
|
|
158
|
+
? p.minOfferPercent
|
|
159
|
+
: DEFAULT_MIN_OFFER_PERCENT;
|
|
150
160
|
const shippingInfo = typeof p.shippingInfo === "string" ? p.shippingInfo : null;
|
|
151
161
|
const returnPolicy = typeof p.returnPolicy === "string" ? p.returnPolicy : null;
|
|
152
162
|
const shippingPaidBy = p.shippingPaidBy;
|
|
@@ -166,11 +176,15 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
|
|
|
166
176
|
const storeId = typeof p.storeId === "string" ? p.storeId : null;
|
|
167
177
|
// -- Fetch reviews + the shared 4-signal related-items computation + grouped listings in parallel --
|
|
168
178
|
// Only page 1 of reviews is fetched here; <ReviewsListingPanel> pages the rest client-side.
|
|
169
|
-
const [initialReviews, related, groups] = await Promise.all([
|
|
179
|
+
const [initialReviews, related, groups, siteSettings] = await Promise.all([
|
|
170
180
|
getReviewPageForProduct(product.id),
|
|
171
181
|
computeRelatedItems(product),
|
|
172
182
|
getGroupsWithItemsForProduct(product.id),
|
|
183
|
+
getSiteSettingsGlobal().catch(() => null),
|
|
173
184
|
]);
|
|
185
|
+
// Site-wide kill switch. Absent/unreadable settings mean ON — a failed
|
|
186
|
+
// settings read must not silently remove a working feature.
|
|
187
|
+
const offersEnabled = siteSettings?.featureFlags?.offers !== false;
|
|
174
188
|
const { relatedItems, relatedByBrand, relatedByTags, relatedByStore } = related;
|
|
175
189
|
return (_jsxs(Main, { children: [_jsx(HistoryTracker, { productId: product.id, productType: "product", snapshot: {
|
|
176
190
|
title: product.title,
|
|
@@ -218,7 +232,7 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
|
|
|
218
232
|
storeName: typeof p.storeName === "string" ? p.storeName : undefined,
|
|
219
233
|
inStock,
|
|
220
234
|
variant: "desktop",
|
|
221
|
-
})) : (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: true, title: ACTION_NOT_WIRED, children: inStock ? "Buy Now" : "Out of Stock" }), _jsx(Button, { variant: "secondary", size: "md", className: "w-full", disabled: true, title: ACTION_NOT_WIRED, children: inStock ? "Add to Cart" : "Out of Stock" }), _jsx(Button, { variant: "ghost", size: "md", className: "w-full", disabled: true, title: ACTION_NOT_WIRED, children: "\u2661 Add to Wishlist" })] })), allowOffers &&
|
|
235
|
+
})) : (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: true, title: ACTION_NOT_WIRED, children: inStock ? "Buy Now" : "Out of Stock" }), _jsx(Button, { variant: "secondary", size: "md", className: "w-full", disabled: true, title: ACTION_NOT_WIRED, children: inStock ? "Add to Cart" : "Out of Stock" }), _jsx(Button, { variant: "ghost", size: "md", className: "w-full", disabled: true, title: ACTION_NOT_WIRED, children: "\u2661 Add to Wishlist" })] })), allowOffers && offerableType && offersEnabled && price !== null && renderOfferAction?.({
|
|
222
236
|
productId: product.id,
|
|
223
237
|
price,
|
|
224
238
|
currency,
|
|
@@ -10,6 +10,7 @@ import { ProductFeaturesSelector } from "./ProductFeaturesSelector";
|
|
|
10
10
|
import { ImageUpload, MediaUploadField, MediaUploadList, } from "../../media";
|
|
11
11
|
import { useMediaUpload } from "../../media";
|
|
12
12
|
import { resolveDate } from "../../../utils/date.formatter";
|
|
13
|
+
import { DEFAULT_MIN_OFFER_PERCENT } from "../../../_internal/shared/features/offers/config";
|
|
13
14
|
import { normalizeRichTextHtml } from "../../../utils/string.formatter";
|
|
14
15
|
import { useListingTypeFlags } from "../../../react/hooks/useListingTypeFlags";
|
|
15
16
|
import { isAuctionListing, isPreOrderListing, isPrizeDrawListing, normalizeListingType, } from "../utils/listing-type";
|
|
@@ -141,8 +142,10 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
|
|
|
141
142
|
? "Required for live listings — buyers must see the actual animal/plant moving before purchase."
|
|
142
143
|
: t("formVideoHelper"), onAbort: onMediaAbort })), _jsx(TagInput, { label: t("formTags"), value: product.tags || [], onChange: (tags) => update({ tags }), disabled: isReadonly, placeholder: t("formTagsPlaceholder") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(Checkbox, { label: t("formFeatured"), checked: !!product.featured, onChange: (e) => update({ featured: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formIsPromoted"), checked: !!product.isPromoted, onChange: (e) => update({ isPromoted: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formAllowShipBeforeEmiComplete"), checked: !!product.allowShipBeforeEmiComplete, onChange: (e) => update({ allowShipBeforeEmiComplete: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formAllowOffers"), checked: !!product.allowOffers, onChange: (e) => update({
|
|
143
144
|
allowOffers: e.target.checked,
|
|
144
|
-
minOfferPercent: e.target.checked
|
|
145
|
-
|
|
145
|
+
minOfferPercent: e.target.checked
|
|
146
|
+
? product.minOfferPercent ?? DEFAULT_MIN_OFFER_PERCENT
|
|
147
|
+
: undefined,
|
|
148
|
+
}), disabled: isReadonly })] }), product.allowOffers && (_jsxs(_Fragment, { children: [_jsx(Alert, { variant: "info", title: t("formAllowOffersHelp"), children: t("formAllowOffersHelp") }), _jsx(FormField, { name: "minOfferPercent", label: t("formMinOfferPercent"), type: "number", value: String(product.minOfferPercent ?? ""), onChange: (value) => update({ minOfferPercent: Number(value) }), disabled: isReadonly, placeholder: String(DEFAULT_MIN_OFFER_PERCENT) })] })), _jsx(Heading, { level: 4, className: "mt-4", children: t("sectionTaxGst") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "gstRate", label: t("formGstRate"), type: "select",
|
|
146
149
|
// Unset is semantically "exempt" (see ProductDocument.gstRate JSDoc),
|
|
147
150
|
// same as 0% — default the control to 0 so the select stays a fixed
|
|
148
151
|
// 5-option enum instead of adding a 6th "unset" placeholder entry.
|
|
@@ -25,6 +25,11 @@ const CLS_BADGE_NEW = "rounded-full bg-error-solid px-[var(--appkit-space-2)] py
|
|
|
25
25
|
const CLS_BADGE_SALE = "rounded-full bg-success-solid px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[10px] font-bold text-success-on-solid shadow-sm";
|
|
26
26
|
const CLS_BADGE_TRENDING = "rounded-full bg-indigo-600 px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[10px] font-bold text-white shadow-sm";
|
|
27
27
|
const CLS_BADGE_LIMITED = "rounded-full bg-info-solid px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[10px] font-bold text-info-on-solid shadow-sm";
|
|
28
|
+
// Overlay badge sitting on the product photo, so the pairing is `-solid` +
|
|
29
|
+
// `-on-solid` (Root Cause #67) — never `text-white` on a status colour, which
|
|
30
|
+
// inverts with the theme. `warning` because success/info/error are each already
|
|
31
|
+
// taken by a sibling pill in this same stack.
|
|
32
|
+
const CLS_BADGE_OFFERABLE = "rounded-full bg-warning-solid px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[10px] font-bold text-warning-on-solid shadow-sm";
|
|
28
33
|
const CLS_HEART_ACTIVE = "bg-[var(--appkit-color-surface-elevated)]/90 text-error hover:bg-[var(--appkit-color-surface-elevated)]";
|
|
29
34
|
const CLS_HEART_IDLE = "bg-[var(--appkit-color-surface-elevated)]/90 text-[var(--appkit-color-text-muted)] hover:text-error hover:bg-[var(--appkit-color-surface-elevated)]";
|
|
30
35
|
const CLS_STAR = "text-[11px] text-warning";
|
|
@@ -68,7 +73,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
|
|
|
68
73
|
.filter(Boolean)
|
|
69
74
|
.join(" "), children: [_jsxs(Div, { className: `relative ${__O.hidden} aspect-square`, surface: "subtle", children: [product.mainImage ? (_jsx(MediaImage, { src: product.mainImage, alt: product.title, size: "card", className: "transition-transform duration-500 group-hover:scale-105" })) : (_jsx(Row, { surface: "muted", className: "h-full w-full", align: "center", justify: "center", children: _jsx(Span, { className: "opacity-30", size: "4xl", children: "\uD83D\uDECD\uFE0F" }) })), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(product.id); }, label: isSelected ? "Deselect" : "Select", position: "top-2 left-2", className: selectionMode || isSelected ? "opacity-100" : "opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-opacity" })), _jsx(Stack, { className: `absolute top-2 ${onSelect ? "left-10" : "left-2"}`, gap: "xs", children: !selectionMode && !isSelected && (_jsxs(_Fragment, { children: [discount && (_jsxs(Span, { weight: "bold", className: CLS_BADGE_NEW, children: ["-", discount, "%"] })), typeBadge && (_jsx(Span, { weight: "bold", className: `text-[10px] ${typeBadge.cls}`, rounded: "full", padding: "pill-xs", shadow: "sm", children: typeBadge.label })), product.partOfBundleIds && product.partOfBundleIds.length > 0 && (_jsx(Span, { className: CLS_BADGE_SALE, title: product.partOfBundleTitles && product.partOfBundleTitles.length > 0
|
|
70
75
|
? `In bundle: ${product.partOfBundleTitles[0]}`
|
|
71
|
-
: "In a bundle", children: "Bundled" })), product.groupId && (_jsx(Span, { className: CLS_BADGE_TRENDING, title: product.groupTitle ? `Part of set: ${product.groupTitle}` : "Part of a set", children: product.isGroupParent ? "Set Parent" : "In Set" })), product.sublistingCategoryId && (_jsx(Span, { className: CLS_BADGE_LIMITED, title: "Has variants or sub-listings", children: "Has Variants" }))] })) }), _jsx(BaseListingCard.IconOverlay, { groupIcon: product.groupIcon, sublistingIcon: product.sublistingIcon, position: "bottom-2 right-2" }), onAddToWishlist && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => {
|
|
76
|
+
: "In a bundle", children: "Bundled" })), product.groupId && (_jsx(Span, { className: CLS_BADGE_TRENDING, title: product.groupTitle ? `Part of set: ${product.groupTitle}` : "Part of a set", children: product.isGroupParent ? "Set Parent" : "In Set" })), product.allowOffers && (_jsx(Span, { className: CLS_BADGE_OFFERABLE, title: "The seller accepts offers on this listing", children: "Taking Offers" })), product.sublistingCategoryId && (_jsx(Span, { className: CLS_BADGE_LIMITED, title: "Has variants or sub-listings", children: "Has Variants" }))] })) }), _jsx(BaseListingCard.IconOverlay, { groupIcon: product.groupIcon, sublistingIcon: product.sublistingIcon, position: "bottom-2 right-2" }), onAddToWishlist && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => {
|
|
72
77
|
e.stopPropagation();
|
|
73
78
|
e.preventDefault();
|
|
74
79
|
onAddToWishlist(product.id);
|
|
@@ -29,7 +29,9 @@ import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
|
29
29
|
import { useBottomActions } from "../../layout";
|
|
30
30
|
import { GENERIC_PRODUCT_LISTING_TYPES, PRODUCT_TYPE_FILTER_TABS } from "../constants/listing-tabs";
|
|
31
31
|
import { parseSelectedListingTypes } from "../utils/listing-type";
|
|
32
|
-
import { commonSortOptionsFor
|
|
32
|
+
import { commonSortOptionsFor } from "../../../_internal/shared/listing-types/_registry";
|
|
33
|
+
import { AVAILABILITY_VALUES } from "../../../constants/field-names";
|
|
34
|
+
import { AvailabilityTabs } from "./AvailabilityTabs";
|
|
33
35
|
import { TextLink } from "../../../ui";
|
|
34
36
|
const __P = {
|
|
35
37
|
p3: "p-[var(--appkit-space-3)]",
|
|
@@ -47,7 +49,6 @@ export function ProductsIndexListing({ initialData, listingTypes = GENERIC_PRODU
|
|
|
47
49
|
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
48
50
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
49
51
|
const [compareIds, setCompareIds] = useState([]);
|
|
50
|
-
const showSold = table.get(TABLE_KEYS.SHOW_SOLD) === "true";
|
|
51
52
|
const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
|
|
52
53
|
// ── Type filter (multi-select) ───────────────────────────────────────────
|
|
53
54
|
// The URL value is a pipe-joined set; empty means "every type this page
|
|
@@ -68,12 +69,11 @@ export function ProductsIndexListing({ initialData, listingTypes = GENERIC_PRODU
|
|
|
68
69
|
const effectiveSort = sortOptions.some((o) => o.value === sortValue)
|
|
69
70
|
? sortValue
|
|
70
71
|
: DEFAULT_SORT;
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
const showEnded = table.get(TABLE_KEYS.SHOW_ENDED) === "true";
|
|
72
|
+
// The availability scope. Absent means "available" — the same default
|
|
73
|
+
// `defaultAvailabilityForListingTypes` resolves server-side, which is what
|
|
74
|
+
// keeps the SSR paint and the first refetch in agreement (Root Cause #30).
|
|
75
|
+
const availability = table.get(TABLE_KEYS.AVAILABILITY) ||
|
|
76
|
+
AVAILABILITY_VALUES.AVAILABLE;
|
|
77
77
|
// "Full <type> filters →" — a single selected type with a dedicated browse
|
|
78
78
|
// page gets a link to it, since that page carries facets this generic one
|
|
79
79
|
// can't (bid range, delivery-date window, reveal status).
|
|
@@ -96,15 +96,13 @@ export function ProductsIndexListing({ initialData, listingTypes = GENERIC_PRODU
|
|
|
96
96
|
onResetAll({
|
|
97
97
|
[TABLE_KEYS.QUERY]: "",
|
|
98
98
|
[TABLE_KEYS.SORT]: "",
|
|
99
|
-
[TABLE_KEYS.
|
|
100
|
-
[TABLE_KEYS.SHOW_ENDED]: "",
|
|
99
|
+
[TABLE_KEYS.AVAILABILITY]: "",
|
|
101
100
|
[TABLE_KEYS.LISTING_TYPE]: "",
|
|
102
101
|
});
|
|
103
102
|
setSearchInput("");
|
|
104
103
|
}, [onResetAll]);
|
|
105
104
|
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
106
|
-
|
|
107
|
-
table.get(TABLE_KEYS.SHOW_ENDED) === "true" ||
|
|
105
|
+
availability !== AVAILABILITY_VALUES.AVAILABLE ||
|
|
108
106
|
!!typeParam ||
|
|
109
107
|
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
110
108
|
filterActiveCount > 0;
|
|
@@ -136,16 +134,12 @@ export function ProductsIndexListing({ initialData, listingTypes = GENERIC_PRODU
|
|
|
136
134
|
// Pipe-joined OR-group — sievejs parses it as a same-field OR and the
|
|
137
135
|
// Firebase adapter upgrades it to a `.where(…, "in", …)` query.
|
|
138
136
|
listingType: effectiveTypes.join("|"),
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
// across types), which `dateFieldFor` already enforces server-side.
|
|
146
|
-
dateFrom: showsEndedToggle && !showEnded && selectedTypes.length === 1
|
|
147
|
-
? new Date().toISOString()
|
|
148
|
-
: undefined,
|
|
137
|
+
// One scope for every type. The per-type meaning of "unavailable" — sold
|
|
138
|
+
// out, ended, closed, depleted — is resolved server-side from the listing
|
|
139
|
+
// -type registry, which is why this no longer needs the old
|
|
140
|
+
// "only when exactly one type is selected" caveat that let ended auctions
|
|
141
|
+
// leak into the default /products view.
|
|
142
|
+
availability,
|
|
149
143
|
};
|
|
150
144
|
const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
151
145
|
const selection = useBulkSelection({ items: products, keyExtractor: (p) => p.id });
|
|
@@ -259,21 +253,17 @@ export function ProductsIndexListing({ initialData, listingTypes = GENERIC_PRODU
|
|
|
259
253
|
},
|
|
260
254
|
] } } : {});
|
|
261
255
|
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: searchPlaceholder, onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: effectiveSort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, toggles: [
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
// and
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
: []),
|
|
268
|
-
...(showsEndedToggle
|
|
269
|
-
? [{ label: "Show ended", active: showEnded, onChange: (next) => table.set(TABLE_KEYS.SHOW_ENDED, next ? "true" : "") }]
|
|
270
|
-
: []),
|
|
256
|
+
// "Show sold" / "Show ended" used to live here. They WIDENED the
|
|
257
|
+
// list rather than scoping it, so live and dead rows arrived mixed
|
|
258
|
+
// together and the archive was unbrowsable — replaced by the
|
|
259
|
+
// <AvailabilityTabs> scope bar below.
|
|
260
|
+
//
|
|
271
261
|
// Inline quick-filter: the highest-frequency drawer-only facet promoted
|
|
272
262
|
// to the sticky toolbar so it doesn't require open-drawer → check → Apply
|
|
273
263
|
// → close for every toggle. The full facet set (category/price/brand/…)
|
|
274
264
|
// remains in <FilterDrawer> below.
|
|
275
265
|
{ label: "Free shipping", active: table.get(TABLE_KEYS.FREE_SHIPPING) === "true", onChange: (next) => table.set(TABLE_KEYS.FREE_SHIPPING, next ? "true" : "") },
|
|
276
|
-
] }), _jsx(Div, { padding: "y-sm", children: _jsxs(Row, { justify: "between", align: "center", wrap: true, gap: "sm", children: [_jsx(FilterChipGroup, { multiple: true, label: "Type", tabs: typeTabs, value: selectedTypes.join("|"), onChange: (next) => {
|
|
266
|
+
] }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: effectiveTypes }) }), _jsx(Div, { padding: "y-sm", children: _jsxs(Row, { justify: "between", align: "center", wrap: true, gap: "sm", children: [_jsx(FilterChipGroup, { multiple: true, label: "Type", tabs: typeTabs, value: selectedTypes.join("|"), onChange: (next) => {
|
|
277
267
|
// Clear `sort` alongside the type change: a sort valid for the
|
|
278
268
|
// old selection (say -auctionEndDate) can be invalid for the new
|
|
279
269
|
// one, and useUrlTable's single router.replace means we must set
|