@mohasinac/appkit 4.13.1 → 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 +1 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cap for any history array. Matches `HISTORY_MAX`'s reasoning: an unbounded
|
|
3
|
+
* array on a hot document inflates every read of that document (Rule #6), and
|
|
4
|
+
* an order that changes status more than this has a different problem.
|
|
5
|
+
*/
|
|
6
|
+
export const STATUS_HISTORY_MAX = 50;
|
|
@@ -22,8 +22,10 @@
|
|
|
22
22
|
* that doesn't cover the full union.
|
|
23
23
|
*/
|
|
24
24
|
import { LISTING_TYPE_CAPABILITIES } from "./capabilities";
|
|
25
|
+
import { type AvailabilityRow, type UnavailableClause } from "./availability";
|
|
25
26
|
import type { ListingType } from "../../../features/products/types/index";
|
|
26
27
|
import type { SortOption } from "../../../features/products/constants/sieve";
|
|
28
|
+
import { type AvailabilityFilter } from "../../../constants/field-names";
|
|
27
29
|
/**
|
|
28
30
|
* Which "Show X" quick-filter a listing type's browse surfaces default to
|
|
29
31
|
* hiding. Drives both the toolbar toggle label and the SSR default filter,
|
|
@@ -78,8 +80,29 @@ export interface ListingTypePlugin {
|
|
|
78
80
|
* /products when exactly this type is selected.
|
|
79
81
|
*/
|
|
80
82
|
browseRoute: string | null;
|
|
81
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* Which "Show X" default this type's browse surfaces apply. Since 2026-08-24
|
|
85
|
+
* this drives the LABEL of the availability tab bar's middle tab
|
|
86
|
+
* (`availabilityTabsFor`) rather than a toggle pill's wording.
|
|
87
|
+
*/
|
|
82
88
|
hideDefault: ListingHideDefault;
|
|
89
|
+
/**
|
|
90
|
+
* Is this row still buyable / biddable / enterable? Type-specific checks
|
|
91
|
+
* ONLY — `baseAvailable` (isSold / availableQuantity / stockQuantity) is
|
|
92
|
+
* applied to every type by `isListingRowAvailable` before this runs, so a
|
|
93
|
+
* type with no extra rule returns `true`.
|
|
94
|
+
*
|
|
95
|
+
* Must stay pure and client-safe: the same predicate runs inside the SSR
|
|
96
|
+
* query, the homepage fetch and the related-items carousel.
|
|
97
|
+
*/
|
|
98
|
+
isAvailable: (row: AvailabilityRow, now: Date) => boolean;
|
|
99
|
+
/**
|
|
100
|
+
* The query half of the same question — see `UnavailableClause`. Used to
|
|
101
|
+
* front-load the "Sold & Ended" scope, which is sparse and therefore
|
|
102
|
+
* expressible as an OR of equalities, unlike "available", which is dense and
|
|
103
|
+
* negation-shaped and stays an in-memory predicate.
|
|
104
|
+
*/
|
|
105
|
+
unavailableClauses: readonly UnavailableClause[];
|
|
83
106
|
/** Sort set offered on admin/seller dashboards for this type. */
|
|
84
107
|
sortOptions: readonly SortOption[];
|
|
85
108
|
/** Narrower sort set offered on public browse surfaces for this type. */
|
|
@@ -124,4 +147,34 @@ export declare function commonSortOptionsFor(types: readonly ListingType[], vari
|
|
|
124
147
|
* so this returns every distinct default across the set.
|
|
125
148
|
*/
|
|
126
149
|
export declare function hideDefaultsFor(types: readonly ListingType[]): ListingHideDefault[];
|
|
150
|
+
export interface AvailabilityTab {
|
|
151
|
+
id: AvailabilityFilter;
|
|
152
|
+
label: string;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The three-tab availability bar for a set of listing types.
|
|
156
|
+
*
|
|
157
|
+
* The middle label is derived, not a constant: `/auctions` reads "Ended",
|
|
158
|
+
* `/art` reads "Sold", and `/products` — which spans both kinds — reads
|
|
159
|
+
* "Sold & Ended". Ordering is fixed rather than set-iteration order so the
|
|
160
|
+
* label can never flip to "Ended & Sold" between renders.
|
|
161
|
+
*/
|
|
162
|
+
export declare function availabilityTabsFor(types: readonly ListingType[]): AvailabilityTab[];
|
|
163
|
+
/**
|
|
164
|
+
* THE definition of "this listing is still available", shared by the listing
|
|
165
|
+
* query, the homepage fetch and the related-items carousel. Before this there
|
|
166
|
+
* were four partial versions of it and they disagreed — see the header of
|
|
167
|
+
* `./availability`.
|
|
168
|
+
*
|
|
169
|
+
* An unrecognised `listingType` falls back to `standard` rather than throwing:
|
|
170
|
+
* these rows come from Firestore and a bad value must degrade to the base
|
|
171
|
+
* checks, not blank the page.
|
|
172
|
+
*/
|
|
173
|
+
export declare function isListingRowAvailable(row: AvailabilityRow, now: Date): boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Every distinct `UnavailableClause` across a set of listing types, deduped by
|
|
176
|
+
* field+op+value. `/products` (all nine types) collapses to five clauses, so
|
|
177
|
+
* the "Sold & Ended" scope costs five bounded queries, not nine.
|
|
178
|
+
*/
|
|
179
|
+
export declare function unavailableClausesFor(types: readonly ListingType[]): UnavailableClause[];
|
|
127
180
|
export { LISTING_TYPE_CAPABILITIES };
|
|
@@ -33,6 +33,8 @@ import * as live from "./live/config";
|
|
|
33
33
|
import * as art from "./art/config";
|
|
34
34
|
import * as stickers from "./stickers/config";
|
|
35
35
|
import { LISTING_TYPE_CAPABILITIES } from "./capabilities";
|
|
36
|
+
import { baseAvailable, } from "./availability";
|
|
37
|
+
import { AVAILABILITY_VALUES, } from "../../../constants/field-names";
|
|
36
38
|
// SB-UNI-D — bundle entry removed; bundles are a categoryType, not a listingType.
|
|
37
39
|
// SB-UNI-F 2026-05-13 — classified / digital-code / live added.
|
|
38
40
|
// EMI/art-stickers session — art / stickers added.
|
|
@@ -112,6 +114,71 @@ export function hideDefaultsFor(types) {
|
|
|
112
114
|
seen.add(LISTING_TYPE_REGISTRY[type].hideDefault);
|
|
113
115
|
return [...seen];
|
|
114
116
|
}
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Availability scope
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
/**
|
|
121
|
+
* The user-facing noun for each hide-default. "ended" and "closed" are one
|
|
122
|
+
* query mechanic and one word to a buyer — an auction that ended and a prize
|
|
123
|
+
* draw that closed are both simply over.
|
|
124
|
+
*
|
|
125
|
+
* `Record<ListingHideDefault, …>` on purpose: adding a fourth hide-default is
|
|
126
|
+
* then a compile error rather than a tab that silently renders `undefined`.
|
|
127
|
+
*/
|
|
128
|
+
const HIDE_DEFAULT_NOUN = {
|
|
129
|
+
sold: "Sold",
|
|
130
|
+
ended: "Ended",
|
|
131
|
+
closed: "Ended",
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* The three-tab availability bar for a set of listing types.
|
|
135
|
+
*
|
|
136
|
+
* The middle label is derived, not a constant: `/auctions` reads "Ended",
|
|
137
|
+
* `/art` reads "Sold", and `/products` — which spans both kinds — reads
|
|
138
|
+
* "Sold & Ended". Ordering is fixed rather than set-iteration order so the
|
|
139
|
+
* label can never flip to "Ended & Sold" between renders.
|
|
140
|
+
*/
|
|
141
|
+
export function availabilityTabsFor(types) {
|
|
142
|
+
const nouns = new Set((types.length > 0 ? types : Object.keys(LISTING_TYPE_REGISTRY)).map((t) => HIDE_DEFAULT_NOUN[LISTING_TYPE_REGISTRY[t].hideDefault]));
|
|
143
|
+
const unavailableLabel = ["Sold", "Ended"].filter((n) => nouns.has(n)).join(" & ") || "Sold";
|
|
144
|
+
return [
|
|
145
|
+
{ id: AVAILABILITY_VALUES.AVAILABLE, label: "Available" },
|
|
146
|
+
{ id: AVAILABILITY_VALUES.UNAVAILABLE, label: unavailableLabel },
|
|
147
|
+
{ id: AVAILABILITY_VALUES.ALL, label: "All" },
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* THE definition of "this listing is still available", shared by the listing
|
|
152
|
+
* query, the homepage fetch and the related-items carousel. Before this there
|
|
153
|
+
* were four partial versions of it and they disagreed — see the header of
|
|
154
|
+
* `./availability`.
|
|
155
|
+
*
|
|
156
|
+
* An unrecognised `listingType` falls back to `standard` rather than throwing:
|
|
157
|
+
* these rows come from Firestore and a bad value must degrade to the base
|
|
158
|
+
* checks, not blank the page.
|
|
159
|
+
*/
|
|
160
|
+
export function isListingRowAvailable(row, now) {
|
|
161
|
+
if (!baseAvailable(row))
|
|
162
|
+
return false;
|
|
163
|
+
const raw = row.listingType;
|
|
164
|
+
const type = typeof raw === "string" && raw in LISTING_TYPE_REGISTRY ? raw : "standard";
|
|
165
|
+
return LISTING_TYPE_REGISTRY[type].isAvailable(row, now);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Every distinct `UnavailableClause` across a set of listing types, deduped by
|
|
169
|
+
* field+op+value. `/products` (all nine types) collapses to five clauses, so
|
|
170
|
+
* the "Sold & Ended" scope costs five bounded queries, not nine.
|
|
171
|
+
*/
|
|
172
|
+
export function unavailableClausesFor(types) {
|
|
173
|
+
const effective = types.length > 0 ? types : Object.keys(LISTING_TYPE_REGISTRY);
|
|
174
|
+
const seen = new Map();
|
|
175
|
+
for (const type of effective) {
|
|
176
|
+
for (const clause of LISTING_TYPE_REGISTRY[type].unavailableClauses) {
|
|
177
|
+
seen.set(`${clause.field}|${clause.op}|${String(clause.value)}`, clause);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return [...seen.values()];
|
|
181
|
+
}
|
|
115
182
|
// Re-export the capability map so consumers can pull both surfaces from
|
|
116
183
|
// the same module path.
|
|
117
184
|
export { LISTING_TYPE_CAPABILITIES };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { alwaysAvailable } from "../availability";
|
|
1
2
|
export declare const LISTING_TYPE: "art";
|
|
2
3
|
export declare const capability: import("../capabilities").ListingTypeCapability;
|
|
3
4
|
export declare const config: {
|
|
@@ -17,6 +18,8 @@ export declare const config: {
|
|
|
17
18
|
chipLabel: string;
|
|
18
19
|
browseRoute: string;
|
|
19
20
|
hideDefault: "sold";
|
|
21
|
+
isAvailable: typeof alwaysAvailable;
|
|
22
|
+
unavailableClauses: readonly import("../availability").UnavailableClause[];
|
|
20
23
|
sortOptions: readonly [{
|
|
21
24
|
readonly value: string;
|
|
22
25
|
readonly label: "Newest First";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LISTING_TYPE_CAPABILITIES } from "../capabilities";
|
|
2
|
+
import { alwaysAvailable, STOCK_ONLY_UNAVAILABLE_CLAUSES, } from "../availability";
|
|
2
3
|
import { ROUTES } from "../../../../next/routing/route-map";
|
|
3
4
|
import { STANDARD_SORT_OPTIONS, STANDARD_PUBLIC_SORT_OPTIONS, } from "../../../../features/products/constants/sieve";
|
|
4
5
|
export const LISTING_TYPE = "art";
|
|
@@ -20,6 +21,10 @@ export const config = {
|
|
|
20
21
|
// dedicated browse surface even though detailRoute points at /products.
|
|
21
22
|
browseRoute: String(ROUTES.PUBLIC.ART),
|
|
22
23
|
hideDefault: "sold",
|
|
24
|
+
// Runs out rather than ending — no time dimension, so the shared
|
|
25
|
+
// stock-only rules are the whole story for this type.
|
|
26
|
+
isAvailable: alwaysAvailable,
|
|
27
|
+
unavailableClauses: STOCK_ONLY_UNAVAILABLE_CLAUSES,
|
|
23
28
|
sortOptions: STANDARD_SORT_OPTIONS,
|
|
24
29
|
publicSortOptions: STANDARD_PUBLIC_SORT_OPTIONS,
|
|
25
30
|
extraFacetKeys: [],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AvailabilityRow } from "../availability";
|
|
1
2
|
export declare const LISTING_TYPE: "auction";
|
|
2
3
|
export declare const capability: import("../capabilities").ListingTypeCapability;
|
|
3
4
|
export declare const config: {
|
|
@@ -17,6 +18,18 @@ export declare const config: {
|
|
|
17
18
|
chipLabel: string;
|
|
18
19
|
browseRoute: string;
|
|
19
20
|
hideDefault: "ended";
|
|
21
|
+
isAvailable: (row: AvailabilityRow, now: Date) => boolean;
|
|
22
|
+
unavailableClauses: ({
|
|
23
|
+
field: "auctionEndDate";
|
|
24
|
+
op: "lt";
|
|
25
|
+
value: "NOW";
|
|
26
|
+
sortField: "auctionEndDate";
|
|
27
|
+
} | {
|
|
28
|
+
field: "isSold";
|
|
29
|
+
op: "eq";
|
|
30
|
+
value: boolean;
|
|
31
|
+
sortField?: undefined;
|
|
32
|
+
})[];
|
|
20
33
|
sortOptions: readonly [{
|
|
21
34
|
readonly value: string;
|
|
22
35
|
readonly label: "Ending Soon";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { LISTING_TYPE_CAPABILITIES } from "../capabilities";
|
|
2
|
+
import { toDate } from "../availability";
|
|
2
3
|
import { ROUTES } from "../../../../next/routing/route-map";
|
|
3
4
|
import { TABLE_KEYS } from "../../../../constants/table-keys";
|
|
5
|
+
import { PRODUCT_FIELDS } from "../../../../constants/field-names";
|
|
4
6
|
import { AUCTION_SORT_OPTIONS, AUCTION_PUBLIC_SORT_OPTIONS, } from "../../../../features/products/constants/sieve";
|
|
5
7
|
export const LISTING_TYPE = "auction";
|
|
6
8
|
export const capability = LISTING_TYPE_CAPABILITIES.auction;
|
|
@@ -20,6 +22,25 @@ export const config = {
|
|
|
20
22
|
// An auction is never "sold out" — it ends. Hiding by stock would hide
|
|
21
23
|
// every live auction, since auctions don't carry meaningful stock.
|
|
22
24
|
hideDefault: "ended",
|
|
25
|
+
// There is no `auctionStatus` field — "ended" is purely the end date having
|
|
26
|
+
// passed. An auction with no end date at all is treated as live rather than
|
|
27
|
+
// hidden; a missing date is a data defect, not a signal that it is over.
|
|
28
|
+
isAvailable: (row, now) => {
|
|
29
|
+
const end = toDate(row[PRODUCT_FIELDS.AUCTION_END_DATE]);
|
|
30
|
+
return end === null || end > now;
|
|
31
|
+
},
|
|
32
|
+
unavailableClauses: [
|
|
33
|
+
// The ONLY inequality in the registry — safe because it names the sort
|
|
34
|
+
// field the query must order by, which is also the natural sort for an
|
|
35
|
+
// "ended auctions" archive ("Recently Ended").
|
|
36
|
+
{
|
|
37
|
+
field: PRODUCT_FIELDS.AUCTION_END_DATE,
|
|
38
|
+
op: "lt",
|
|
39
|
+
value: "NOW",
|
|
40
|
+
sortField: PRODUCT_FIELDS.AUCTION_END_DATE,
|
|
41
|
+
},
|
|
42
|
+
{ field: PRODUCT_FIELDS.IS_SOLD, op: "eq", value: true },
|
|
43
|
+
],
|
|
23
44
|
sortOptions: AUCTION_SORT_OPTIONS,
|
|
24
45
|
publicSortOptions: AUCTION_PUBLIC_SORT_OPTIONS,
|
|
25
46
|
extraFacetKeys: [
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { FirestoreValue } from "../../../schemas/types";
|
|
2
|
+
/**
|
|
3
|
+
* A product row as it reaches a predicate.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately the project’s sanctioned loose Firestore-row alias rather than
|
|
6
|
+
* `ProductDocument`: the same row arrives here in three shapes — a Firestore
|
|
7
|
+
* document (Date / Timestamp fields), an already-serialised payload from the
|
|
8
|
+
* colocated `listingProcessor` Function (ISO strings), and JSON from
|
|
9
|
+
* `/api/products`. Narrowing to any one of them would just push a cast to
|
|
10
|
+
* every call site.
|
|
11
|
+
*/
|
|
12
|
+
export type AvailabilityRow = Record<string, FirestoreValue>;
|
|
13
|
+
/**
|
|
14
|
+
* Everything a timestamp field can be by the time it reaches `toDate`. The
|
|
15
|
+
* `{ toDate() }` arm is a Firestore `Timestamp`, which cannot be imported
|
|
16
|
+
* here — this module is client-safe and firebase-admin is not.
|
|
17
|
+
*/
|
|
18
|
+
export type DateLike = FirestoreValue | {
|
|
19
|
+
toDate: () => Date;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Firestore Timestamp | Date | ISO string | epoch millis → Date, or null when
|
|
23
|
+
* the value is absent or unparseable. Firestore hands back a `Timestamp` with
|
|
24
|
+
* a `toDate()` method server-side but a plain ISO string once serialised, and
|
|
25
|
+
* a predicate that handles only one of those silently returns the wrong answer
|
|
26
|
+
* on the other runtime.
|
|
27
|
+
*/
|
|
28
|
+
export declare function toDate(value: DateLike): Date | null;
|
|
29
|
+
/** Read a numeric field, or `null` when absent/non-numeric. */
|
|
30
|
+
export declare function num(value: DateLike): number | null;
|
|
31
|
+
/**
|
|
32
|
+
* The checks every listing type shares, regardless of `listingType`.
|
|
33
|
+
*
|
|
34
|
+
* Note what is NOT here: `status`. Publication state (draft / in_review /
|
|
35
|
+
* archived) is a different axis from availability — an archived listing is
|
|
36
|
+
* hidden from the public entirely, while a sold one is still browsable in the
|
|
37
|
+
* "Sold & Ended" scope. Conflating them is what made `isValidRelatedItem`
|
|
38
|
+
* test for a `status: "sold"` value that has never existed.
|
|
39
|
+
*
|
|
40
|
+
* A field that is absent is treated as "not a reason to hide" — most rows
|
|
41
|
+
* carry `isSold: false` and a real quantity, but older documents predate both
|
|
42
|
+
* and must not vanish from the default scope.
|
|
43
|
+
*/
|
|
44
|
+
export declare function baseAvailable(row: AvailabilityRow): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Whether a row may be shown on a public surface at all. Separate from
|
|
47
|
+
* availability on purpose — see `baseAvailable`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function isPubliclyVisible(row: AvailabilityRow): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The Firestore-expressible half of "this row is UNAVAILABLE".
|
|
52
|
+
*
|
|
53
|
+
* Every entry must be an EQUALITY, or an inequality that names its own
|
|
54
|
+
* `sortField` — because Firestore implicitly appends an inequality's field to
|
|
55
|
+
* the `orderBy`, so pairing one with an unrelated sort demands a composite
|
|
56
|
+
* index in an order nobody declares and fails with FAILED_PRECONDITION
|
|
57
|
+
* (Recurrent Root Cause #59). Equalities carry no such constraint, which is
|
|
58
|
+
* what makes the "Sold & Ended" fan-out safe on a mixed type selection.
|
|
59
|
+
*/
|
|
60
|
+
export interface UnavailableClause {
|
|
61
|
+
field: string;
|
|
62
|
+
op: "eq" | "lt";
|
|
63
|
+
/** `"NOW"` is substituted with the query's timestamp at build time. */
|
|
64
|
+
value: string | number | boolean | "NOW";
|
|
65
|
+
/** Required when `op !== "eq"` — the query MUST order by this field. */
|
|
66
|
+
sortField?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* For types whose only end-state is running out: standard, classified, live,
|
|
70
|
+
* art, stickers. Shared rather than copy-pasted into five plugin configs,
|
|
71
|
+
* which is exactly the duplication the registry exists to prevent.
|
|
72
|
+
*/
|
|
73
|
+
export declare const STOCK_ONLY_UNAVAILABLE_CLAUSES: readonly UnavailableClause[];
|
|
74
|
+
/**
|
|
75
|
+
* `isAvailable` for a type with no rule beyond `baseAvailable`. Named rather
|
|
76
|
+
* than an inline `() => true` so the five configs that use it read as
|
|
77
|
+
* deliberately sharing one decision, not as five independent stubs.
|
|
78
|
+
*/
|
|
79
|
+
export declare function alwaysAvailable(): boolean;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: There was no single definition of "this listing is still available".
|
|
3
|
+
* The closest thing was a private `isValidRelatedItem()` in
|
|
4
|
+
* `_internal/server/features/products/data.ts`, reachable only from the
|
|
5
|
+
* related-items carousel and carrying three bugs: it read
|
|
6
|
+
* `row.codesAvailable` at the top level when the schema nests it under
|
|
7
|
+
* `digitalCode.codesAvailable`; it filtered on `status` values
|
|
8
|
+
* ("sold", "out_of_stock", "discontinued") that are not members of the
|
|
9
|
+
* real `ProductStatus` union; and it had no branch for classified / live
|
|
10
|
+
* / art / stickers. Meanwhile the listing pages expressed the same idea a
|
|
11
|
+
* fourth way (an `inStock` boolean plus a `dateFrom` cutoff) and the
|
|
12
|
+
* homepage expressed it not at all — which is why the "Live Auctions"
|
|
13
|
+
* strip rendered the most-expired lots first.
|
|
14
|
+
*
|
|
15
|
+
* WHAT: The type-agnostic half of the availability predicate. The per-type
|
|
16
|
+
* half lives on each listing-type plugin as `isAvailable`; the two are
|
|
17
|
+
* combined by `isListingRowAvailable()` in `./_registry`.
|
|
18
|
+
*
|
|
19
|
+
* EXPORTS: AvailabilityRow, UnavailableClause, toDate, num, baseAvailable,
|
|
20
|
+
* isPubliclyVisible, alwaysAvailable, STOCK_ONLY_UNAVAILABLE_CLAUSES
|
|
21
|
+
*
|
|
22
|
+
* @tag domain:products
|
|
23
|
+
* @tag layer:shared
|
|
24
|
+
* @tag pattern:none
|
|
25
|
+
* @tag access:isomorphic
|
|
26
|
+
* @tag consumers:_registry,list-public,data,PrizeDrawsSection
|
|
27
|
+
* @tag sideEffects:none
|
|
28
|
+
*/
|
|
29
|
+
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
30
|
+
/**
|
|
31
|
+
* Firestore Timestamp | Date | ISO string | epoch millis → Date, or null when
|
|
32
|
+
* the value is absent or unparseable. Firestore hands back a `Timestamp` with
|
|
33
|
+
* a `toDate()` method server-side but a plain ISO string once serialised, and
|
|
34
|
+
* a predicate that handles only one of those silently returns the wrong answer
|
|
35
|
+
* on the other runtime.
|
|
36
|
+
*/
|
|
37
|
+
export function toDate(value) {
|
|
38
|
+
if (!value)
|
|
39
|
+
return null;
|
|
40
|
+
if (value instanceof Date)
|
|
41
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
42
|
+
if (typeof value === "object" && typeof value.toDate === "function") {
|
|
43
|
+
const converted = value.toDate();
|
|
44
|
+
return converted instanceof Date && !Number.isNaN(converted.getTime()) ? converted : null;
|
|
45
|
+
}
|
|
46
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
47
|
+
const parsed = new Date(value);
|
|
48
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/** Read a numeric field, or `null` when absent/non-numeric. */
|
|
53
|
+
export function num(value) {
|
|
54
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The checks every listing type shares, regardless of `listingType`.
|
|
58
|
+
*
|
|
59
|
+
* Note what is NOT here: `status`. Publication state (draft / in_review /
|
|
60
|
+
* archived) is a different axis from availability — an archived listing is
|
|
61
|
+
* hidden from the public entirely, while a sold one is still browsable in the
|
|
62
|
+
* "Sold & Ended" scope. Conflating them is what made `isValidRelatedItem`
|
|
63
|
+
* test for a `status: "sold"` value that has never existed.
|
|
64
|
+
*
|
|
65
|
+
* A field that is absent is treated as "not a reason to hide" — most rows
|
|
66
|
+
* carry `isSold: false` and a real quantity, but older documents predate both
|
|
67
|
+
* and must not vanish from the default scope.
|
|
68
|
+
*/
|
|
69
|
+
export function baseAvailable(row) {
|
|
70
|
+
if (row[PRODUCT_FIELDS.IS_SOLD] === true)
|
|
71
|
+
return false;
|
|
72
|
+
const available = num(row[PRODUCT_FIELDS.AVAILABLE_QUANTITY]);
|
|
73
|
+
if (available !== null && available <= 0)
|
|
74
|
+
return false;
|
|
75
|
+
const stock = num(row[PRODUCT_FIELDS.STOCK_QUANTITY]);
|
|
76
|
+
if (stock !== null && stock <= 0)
|
|
77
|
+
return false;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Whether a row may be shown on a public surface at all. Separate from
|
|
82
|
+
* availability on purpose — see `baseAvailable`.
|
|
83
|
+
*/
|
|
84
|
+
export function isPubliclyVisible(row) {
|
|
85
|
+
const status = row[PRODUCT_FIELDS.STATUS];
|
|
86
|
+
return status === undefined || status === PRODUCT_FIELDS.STATUS_VALUES.PUBLISHED;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* For types whose only end-state is running out: standard, classified, live,
|
|
90
|
+
* art, stickers. Shared rather than copy-pasted into five plugin configs,
|
|
91
|
+
* which is exactly the duplication the registry exists to prevent.
|
|
92
|
+
*/
|
|
93
|
+
export const STOCK_ONLY_UNAVAILABLE_CLAUSES = [
|
|
94
|
+
{ field: PRODUCT_FIELDS.AVAILABLE_QUANTITY, op: "eq", value: 0 },
|
|
95
|
+
{ field: PRODUCT_FIELDS.IS_SOLD, op: "eq", value: true },
|
|
96
|
+
];
|
|
97
|
+
/**
|
|
98
|
+
* `isAvailable` for a type with no rule beyond `baseAvailable`. Named rather
|
|
99
|
+
* than an inline `() => true` so the five configs that use it read as
|
|
100
|
+
* deliberately sharing one decision, not as five independent stubs.
|
|
101
|
+
*/
|
|
102
|
+
export function alwaysAvailable() {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
@@ -24,6 +24,20 @@ export interface ListingTypeCapability {
|
|
|
24
24
|
requiresJurisdictionCheck: boolean;
|
|
25
25
|
/** Listing fulfills immediately on purchase (no shipping label flow). */
|
|
26
26
|
hasInstantFulfillment: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Buyers can negotiate the price via the Make-an-Offer flow.
|
|
29
|
+
*
|
|
30
|
+
* A per-listing `allowOffers` flag is still required on top of this — the
|
|
31
|
+
* capability says the TYPE can support offers at all, the flag says this
|
|
32
|
+
* particular seller opted in. `makeOffer` checks both.
|
|
33
|
+
*
|
|
34
|
+
* False wherever a negotiated price would collide with the type's own pricing
|
|
35
|
+
* mechanism: an auction already has bidding, a prize-draw sells fixed-price
|
|
36
|
+
* entries, a digital code is a fungible pool item, a pre-order is a deposit
|
|
37
|
+
* against a future price, and a live item is jurisdiction-gated with a
|
|
38
|
+
* verified vendor on the hook for it.
|
|
39
|
+
*/
|
|
40
|
+
canMakeOffer: boolean;
|
|
27
41
|
}
|
|
28
42
|
export declare const LISTING_TYPE_CAPABILITIES: Record<ListingType, ListingTypeCapability>;
|
|
29
43
|
export declare function capabilityFor(type: ListingType): ListingTypeCapability;
|
|
@@ -33,6 +47,7 @@ export declare function supportsShipping(type: ListingType): boolean;
|
|
|
33
47
|
export declare function requiresVendorVerified(type: ListingType): boolean;
|
|
34
48
|
export declare function requiresJurisdictionCheck(type: ListingType): boolean;
|
|
35
49
|
export declare function hasInstantFulfillment(type: ListingType): boolean;
|
|
50
|
+
export declare function canMakeOffer(type: ListingType): boolean;
|
|
36
51
|
/**
|
|
37
52
|
* Exhaustive-switch helper. Use in `switch (type) { case ...: default: assertNever(type); }`
|
|
38
53
|
* so adding a new `ListingType` member is a TS error at every consumer
|
|
@@ -18,6 +18,7 @@ export const LISTING_TYPE_CAPABILITIES = {
|
|
|
18
18
|
requiresVendorVerified: false,
|
|
19
19
|
requiresJurisdictionCheck: false,
|
|
20
20
|
hasInstantFulfillment: false,
|
|
21
|
+
canMakeOffer: true, // the ordinary negotiable listing
|
|
21
22
|
},
|
|
22
23
|
auction: {
|
|
23
24
|
canAddToCart: false,
|
|
@@ -26,6 +27,7 @@ export const LISTING_TYPE_CAPABILITIES = {
|
|
|
26
27
|
requiresVendorVerified: false,
|
|
27
28
|
requiresJurisdictionCheck: false,
|
|
28
29
|
hasInstantFulfillment: false,
|
|
30
|
+
canMakeOffer: false, // bidding IS the price mechanism
|
|
29
31
|
},
|
|
30
32
|
"pre-order": {
|
|
31
33
|
canAddToCart: true,
|
|
@@ -34,6 +36,7 @@ export const LISTING_TYPE_CAPABILITIES = {
|
|
|
34
36
|
requiresVendorVerified: false,
|
|
35
37
|
requiresJurisdictionCheck: false,
|
|
36
38
|
hasInstantFulfillment: false,
|
|
39
|
+
canMakeOffer: false, // deposit against a future price, not a price to haggle
|
|
37
40
|
},
|
|
38
41
|
"prize-draw": {
|
|
39
42
|
canAddToCart: true,
|
|
@@ -42,6 +45,7 @@ export const LISTING_TYPE_CAPABILITIES = {
|
|
|
42
45
|
requiresVendorVerified: false,
|
|
43
46
|
requiresJurisdictionCheck: false,
|
|
44
47
|
hasInstantFulfillment: false,
|
|
48
|
+
canMakeOffer: false, // fixed-price entries; a discounted entry breaks the draw's economics
|
|
45
49
|
},
|
|
46
50
|
// SB-UNI-F 2026-05-13 — Phase 2 union extension.
|
|
47
51
|
classified: {
|
|
@@ -51,6 +55,7 @@ export const LISTING_TYPE_CAPABILITIES = {
|
|
|
51
55
|
requiresVendorVerified: false,
|
|
52
56
|
requiresJurisdictionCheck: false,
|
|
53
57
|
hasInstantFulfillment: false,
|
|
58
|
+
canMakeOffer: true, // haggling is the norm here — see the `negotiable` flag
|
|
54
59
|
},
|
|
55
60
|
"digital-code": {
|
|
56
61
|
canAddToCart: true,
|
|
@@ -59,6 +64,7 @@ export const LISTING_TYPE_CAPABILITIES = {
|
|
|
59
64
|
requiresVendorVerified: false,
|
|
60
65
|
requiresJurisdictionCheck: false,
|
|
61
66
|
hasInstantFulfillment: true, // atomic code claim at order paid
|
|
67
|
+
canMakeOffer: false, // fungible pool item, priced per code
|
|
62
68
|
},
|
|
63
69
|
live: {
|
|
64
70
|
canAddToCart: true, // but jurisdictionAllowed must include buyer's state
|
|
@@ -67,6 +73,7 @@ export const LISTING_TYPE_CAPABILITIES = {
|
|
|
67
73
|
requiresVendorVerified: true,
|
|
68
74
|
requiresJurisdictionCheck: true,
|
|
69
75
|
hasInstantFulfillment: false,
|
|
76
|
+
canMakeOffer: false, // jurisdiction-gated with a verified vendor accountable for the price
|
|
70
77
|
},
|
|
71
78
|
// Art/stickers session — printed-only physical goods, standard-like checkout.
|
|
72
79
|
art: {
|
|
@@ -76,6 +83,7 @@ export const LISTING_TYPE_CAPABILITIES = {
|
|
|
76
83
|
requiresVendorVerified: false,
|
|
77
84
|
requiresJurisdictionCheck: false,
|
|
78
85
|
hasInstantFulfillment: false,
|
|
86
|
+
canMakeOffer: true, // printed goods, standard-like
|
|
79
87
|
},
|
|
80
88
|
stickers: {
|
|
81
89
|
canAddToCart: true,
|
|
@@ -84,6 +92,7 @@ export const LISTING_TYPE_CAPABILITIES = {
|
|
|
84
92
|
requiresVendorVerified: false,
|
|
85
93
|
requiresJurisdictionCheck: false,
|
|
86
94
|
hasInstantFulfillment: false,
|
|
95
|
+
canMakeOffer: true, // printed goods, standard-like
|
|
87
96
|
},
|
|
88
97
|
};
|
|
89
98
|
export function capabilityFor(type) {
|
|
@@ -107,6 +116,9 @@ export function requiresJurisdictionCheck(type) {
|
|
|
107
116
|
export function hasInstantFulfillment(type) {
|
|
108
117
|
return LISTING_TYPE_CAPABILITIES[type].hasInstantFulfillment;
|
|
109
118
|
}
|
|
119
|
+
export function canMakeOffer(type) {
|
|
120
|
+
return LISTING_TYPE_CAPABILITIES[type].canMakeOffer;
|
|
121
|
+
}
|
|
110
122
|
/**
|
|
111
123
|
* Exhaustive-switch helper. Use in `switch (type) { case ...: default: assertNever(type); }`
|
|
112
124
|
* so adding a new `ListingType` member is a TS error at every consumer
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { alwaysAvailable } from "../availability";
|
|
1
2
|
export declare const LISTING_TYPE: "classified";
|
|
2
3
|
export declare const capability: import("../capabilities").ListingTypeCapability;
|
|
3
4
|
export declare const config: {
|
|
@@ -17,6 +18,8 @@ export declare const config: {
|
|
|
17
18
|
chipLabel: string;
|
|
18
19
|
browseRoute: string;
|
|
19
20
|
hideDefault: "sold";
|
|
21
|
+
isAvailable: typeof alwaysAvailable;
|
|
22
|
+
unavailableClauses: readonly import("../availability").UnavailableClause[];
|
|
20
23
|
sortOptions: readonly [{
|
|
21
24
|
readonly value: string;
|
|
22
25
|
readonly label: "Newest First";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LISTING_TYPE_CAPABILITIES } from "../capabilities";
|
|
2
|
+
import { alwaysAvailable, STOCK_ONLY_UNAVAILABLE_CLAUSES, } from "../availability";
|
|
2
3
|
import { ROUTES } from "../../../../next/routing/route-map";
|
|
3
4
|
import { TABLE_KEYS } from "../../../../constants/table-keys";
|
|
4
5
|
import { STANDARD_SORT_OPTIONS, STANDARD_PUBLIC_SORT_OPTIONS, } from "../../../../features/products/constants/sieve";
|
|
@@ -18,6 +19,10 @@ export const config = {
|
|
|
18
19
|
chipLabel: "Classifieds",
|
|
19
20
|
browseRoute: String(ROUTES.PUBLIC.CLASSIFIED),
|
|
20
21
|
hideDefault: "sold",
|
|
22
|
+
// Runs out rather than ending — no time dimension, so the shared
|
|
23
|
+
// stock-only rules are the whole story for this type.
|
|
24
|
+
isAvailable: alwaysAvailable,
|
|
25
|
+
unavailableClauses: STOCK_ONLY_UNAVAILABLE_CLAUSES,
|
|
21
26
|
sortOptions: STANDARD_SORT_OPTIONS,
|
|
22
27
|
publicSortOptions: STANDARD_PUBLIC_SORT_OPTIONS,
|
|
23
28
|
extraFacetKeys: [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AvailabilityRow } from "../availability";
|
|
1
2
|
export declare const LISTING_TYPE: "digital-code";
|
|
2
3
|
export declare const capability: import("../capabilities").ListingTypeCapability;
|
|
3
4
|
export declare const config: {
|
|
@@ -17,6 +18,16 @@ export declare const config: {
|
|
|
17
18
|
chipLabel: string;
|
|
18
19
|
browseRoute: string;
|
|
19
20
|
hideDefault: "sold";
|
|
21
|
+
isAvailable: (row: AvailabilityRow) => boolean;
|
|
22
|
+
unavailableClauses: ({
|
|
23
|
+
field: "digitalCode.codesAvailable";
|
|
24
|
+
op: "eq";
|
|
25
|
+
value: number;
|
|
26
|
+
} | {
|
|
27
|
+
field: "availableQuantity";
|
|
28
|
+
op: "eq";
|
|
29
|
+
value: number;
|
|
30
|
+
})[];
|
|
20
31
|
sortOptions: readonly [{
|
|
21
32
|
readonly value: string;
|
|
22
33
|
readonly label: "Newest First";
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { LISTING_TYPE_CAPABILITIES } from "../capabilities";
|
|
2
|
+
import { num } from "../availability";
|
|
2
3
|
import { ROUTES } from "../../../../next/routing/route-map";
|
|
3
4
|
import { TABLE_KEYS } from "../../../../constants/table-keys";
|
|
5
|
+
import { PRODUCT_FIELDS } from "../../../../constants/field-names";
|
|
4
6
|
import { STANDARD_SORT_OPTIONS, STANDARD_PUBLIC_SORT_OPTIONS, } from "../../../../features/products/constants/sieve";
|
|
5
7
|
export const LISTING_TYPE = "digital-code";
|
|
6
8
|
export const capability = LISTING_TYPE_CAPABILITIES["digital-code"];
|
|
9
|
+
/**
|
|
10
|
+
* Read the pool counter from either row shape. A Firestore document nests it
|
|
11
|
+
* under `digitalCode`; a row that has been through a Sieve projection or the
|
|
12
|
+
* listingProcessor Function can carry the flattened dotted key instead.
|
|
13
|
+
*/
|
|
14
|
+
function readCodesAvailable(row) {
|
|
15
|
+
const nested = row.digitalCode;
|
|
16
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
17
|
+
return nested.codesAvailable;
|
|
18
|
+
}
|
|
19
|
+
return row[PRODUCT_FIELDS.DIGITAL_CODES_AVAILABLE];
|
|
20
|
+
}
|
|
7
21
|
export const config = {
|
|
8
22
|
listingType: LISTING_TYPE,
|
|
9
23
|
slugPrefix: "digitalcode-",
|
|
@@ -17,9 +31,15 @@ export const config = {
|
|
|
17
31
|
pluralLabel: "Digital Codes",
|
|
18
32
|
chipLabel: "Digital Codes",
|
|
19
33
|
browseRoute: String(ROUTES.PUBLIC.DIGITAL_CODES),
|
|
20
|
-
// "Sold out" for a code pool means codesAvailable hit 0
|
|
21
|
-
// stockQuantity
|
|
34
|
+
// "Sold out" for a code pool means codesAvailable hit 0. That was ASSUMED to
|
|
35
|
+
// be mirrored by stockQuantity, and it isn't — seed and production rows exist
|
|
36
|
+
// with a drained pool and non-zero stock. The pool count is authoritative.
|
|
22
37
|
hideDefault: "sold",
|
|
38
|
+
isAvailable: (row) => num(readCodesAvailable(row)) !== 0,
|
|
39
|
+
unavailableClauses: [
|
|
40
|
+
{ field: PRODUCT_FIELDS.DIGITAL_CODES_AVAILABLE, op: "eq", value: 0 },
|
|
41
|
+
{ field: PRODUCT_FIELDS.AVAILABLE_QUANTITY, op: "eq", value: 0 },
|
|
42
|
+
],
|
|
23
43
|
sortOptions: STANDARD_SORT_OPTIONS,
|
|
24
44
|
publicSortOptions: STANDARD_PUBLIC_SORT_OPTIONS,
|
|
25
45
|
extraFacetKeys: [TABLE_KEYS.DELIVERY_METHOD],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { alwaysAvailable } from "../availability";
|
|
1
2
|
export declare const LISTING_TYPE: "live";
|
|
2
3
|
export declare const capability: import("../capabilities").ListingTypeCapability;
|
|
3
4
|
export declare const config: {
|
|
@@ -17,6 +18,8 @@ export declare const config: {
|
|
|
17
18
|
chipLabel: string;
|
|
18
19
|
browseRoute: string;
|
|
19
20
|
hideDefault: "sold";
|
|
21
|
+
isAvailable: typeof alwaysAvailable;
|
|
22
|
+
unavailableClauses: readonly import("../availability").UnavailableClause[];
|
|
20
23
|
sortOptions: readonly [{
|
|
21
24
|
readonly value: string;
|
|
22
25
|
readonly label: "Newest First";
|