@mohasinac/appkit 4.13.1 → 4.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +14 -5
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +3 -0
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +6 -0
- package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +8 -0
- package/dist/_internal/client/features/lottery/LotteryDetailView.js +4 -1
- package/dist/_internal/client/features/lottery/LotteryListView.d.ts +7 -0
- package/dist/_internal/client/features/lottery/LotteryListView.js +1 -1
- package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +4 -1
- package/dist/_internal/client/features/lottery/slot-collage-items.d.ts +15 -0
- package/dist/_internal/client/features/lottery/slot-collage-items.js +13 -0
- package/dist/_internal/server/features/auctions/actions.js +8 -2
- package/dist/_internal/server/features/bundles/data.d.ts +16 -0
- package/dist/_internal/server/features/bundles/data.js +26 -0
- package/dist/_internal/server/features/bundles/index.d.ts +1 -1
- package/dist/_internal/server/features/bundles/index.js +1 -1
- package/dist/_internal/server/features/cart/service.d.ts +3 -3
- package/dist/_internal/server/features/checkout/actions.js +25 -0
- package/dist/_internal/server/features/checkout/locked-lines.d.ts +38 -0
- package/dist/_internal/server/features/checkout/locked-lines.js +171 -0
- package/dist/_internal/server/features/checkout/source-context.d.ts +5 -0
- package/dist/_internal/server/features/checkout/source-context.js +157 -0
- package/dist/_internal/server/features/homepage/index.d.ts +0 -1
- package/dist/_internal/server/features/homepage/index.js +7 -1
- package/dist/_internal/server/features/lottery/adapters.js +4 -0
- package/dist/_internal/server/features/offers/adapters.d.ts +103 -0
- package/dist/_internal/server/features/offers/adapters.js +88 -0
- package/dist/_internal/server/features/offers/index.d.ts +2 -0
- package/dist/_internal/server/features/offers/index.js +1 -0
- package/dist/_internal/server/features/orders/adapters.js +24 -0
- package/dist/_internal/server/features/products/data.js +15 -37
- package/dist/_internal/server/features/products/index.d.ts +1 -1
- package/dist/_internal/server/features/products/index.js +1 -1
- package/dist/_internal/server/features/products/list-public.d.ts +46 -20
- package/dist/_internal/server/features/products/list-public.js +266 -90
- package/dist/_internal/server/features/products/listing-tab-counts.d.ts +44 -0
- package/dist/_internal/server/features/products/listing-tab-counts.js +97 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +51 -0
- package/dist/_internal/server/features/site-settings/adapters.js +75 -0
- package/dist/_internal/server/features/site-settings/index.d.ts +1 -0
- package/dist/_internal/server/features/site-settings/index.js +1 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +56 -0
- package/dist/_internal/server/features/stores/adapters.js +91 -0
- package/dist/_internal/server/features/stores/index.d.ts +1 -0
- package/dist/_internal/server/features/stores/index.js +1 -0
- package/dist/_internal/server/jobs/core/auctionSettlement.js +56 -2
- package/dist/_internal/server/jobs/core/offerExpiry.js +55 -19
- package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.d.ts +5 -0
- package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.js +33 -1
- package/dist/_internal/server/jobs/handlers/messages.d.ts +7 -0
- package/dist/_internal/server/jobs/handlers/messages.js +10 -0
- package/dist/_internal/shared/actions/action-registry.js +17 -1
- package/dist/_internal/shared/checkout/lanes.d.ts +25 -0
- package/dist/_internal/shared/checkout/lanes.js +26 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +27 -0
- package/dist/_internal/shared/features/auctions/config.js +27 -0
- package/dist/_internal/shared/features/auctions/schema.d.ts +14 -2
- package/dist/_internal/shared/features/auctions/schema.js +13 -1
- package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
- package/dist/_internal/shared/features/cart/schema.d.ts +20 -15
- package/dist/_internal/shared/features/cart/schema.js +15 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
- package/dist/_internal/shared/features/emi/schedule.d.ts +24 -3
- package/dist/_internal/shared/features/emi/schedule.js +20 -5
- package/dist/_internal/shared/features/events/schema.d.ts +2 -2
- package/dist/_internal/shared/features/homepage/section-gate.d.ts +14 -0
- package/dist/_internal/shared/features/homepage/section-gate.js +51 -0
- package/dist/_internal/shared/features/offers/config.d.ts +30 -0
- package/dist/_internal/shared/features/offers/config.js +59 -0
- package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
- package/dist/_internal/shared/features/products/schema.d.ts +30 -30
- package/dist/_internal/shared/features/products/to-product-item.js +6 -0
- package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
- package/dist/_internal/shared/features/site-settings/fees.d.ts +60 -0
- package/dist/_internal/shared/features/site-settings/fees.js +39 -0
- package/dist/_internal/shared/features/site-settings/types.d.ts +40 -0
- package/dist/_internal/shared/features/site-settings/types.js +1 -0
- package/dist/_internal/shared/history/history.d.ts +88 -0
- package/dist/_internal/shared/history/history.js +198 -0
- package/dist/_internal/shared/history/index.d.ts +4 -0
- package/dist/_internal/shared/history/index.js +2 -0
- package/dist/_internal/shared/history/types.d.ts +61 -0
- package/dist/_internal/shared/history/types.js +6 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +54 -1
- package/dist/_internal/shared/listing-types/_registry.js +67 -0
- package/dist/_internal/shared/listing-types/art/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/art/config.js +5 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +13 -0
- package/dist/_internal/shared/listing-types/auction/config.js +21 -0
- package/dist/_internal/shared/listing-types/availability.d.ts +79 -0
- package/dist/_internal/shared/listing-types/availability.js +104 -0
- package/dist/_internal/shared/listing-types/capabilities.d.ts +15 -0
- package/dist/_internal/shared/listing-types/capabilities.js +12 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/classified/config.js +5 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +11 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +22 -2
- package/dist/_internal/shared/listing-types/live/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/live/config.js +5 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +19 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +11 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +15 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/standard/config.js +5 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +5 -0
- package/dist/client.d.ts +20 -7
- package/dist/client.js +22 -4
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/constants/field-names.d.ts +57 -8
- package/dist/constants/field-names.js +101 -8
- package/dist/constants/table-keys.d.ts +17 -0
- package/dist/constants/table-keys.js +17 -0
- package/dist/errors/error-codes.d.ts +13 -1
- package/dist/errors/error-codes.js +13 -1
- package/dist/errors/error-display-map.js +12 -3
- package/dist/errors/messages.d.ts +10 -2
- package/dist/errors/messages.js +10 -2
- package/dist/features/about/components/AboutView.d.ts +3 -0
- package/dist/features/about/components/AboutView.js +3 -2
- package/dist/features/about/components/PolicyPageView.d.ts +2 -1
- package/dist/features/about/components/PolicyPageView.js +61 -23
- package/dist/features/about/components/PublicProfileView.js +5 -0
- package/dist/features/about/schemas/firestore.d.ts +3 -0
- package/dist/features/account/components/UserBidsView.js +12 -4
- package/dist/features/account/components/UserOrdersView.js +7 -0
- package/dist/features/account/components/UserReturnsView.d.ts +3 -0
- package/dist/features/account/components/UserReturnsView.js +17 -1
- package/dist/features/account/components/index.d.ts +0 -2
- package/dist/features/account/components/index.js +0 -1
- package/dist/features/account/schemas/index.d.ts +2 -2
- package/dist/features/account/types/index.d.ts +21 -2
- package/dist/features/addresses/schemas/index.d.ts +4 -4
- package/dist/features/admin/components/AdminArtView.js +8 -3
- package/dist/features/admin/components/AdminAuditLogView.js +1 -0
- package/dist/features/admin/components/AdminBidsView.js +15 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +65 -33
- package/dist/features/admin/components/AdminClassifiedView.js +8 -3
- package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
- package/dist/features/admin/components/AdminFeatureEditorView.js +35 -6
- package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
- package/dist/features/admin/components/AdminLiveView.js +8 -3
- package/dist/features/admin/components/AdminNavEditorView.js +15 -5
- package/dist/features/admin/components/AdminNewsletterView.js +18 -2
- package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
- package/dist/features/admin/components/AdminOffersView.js +230 -0
- package/dist/features/admin/components/AdminOrdersView.js +10 -1
- package/dist/features/admin/components/AdminPaymentMethodsView.js +24 -4
- package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
- package/dist/features/admin/components/AdminProductsView.js +16 -29
- package/dist/features/admin/components/AdminScammerEditorView.d.ts +2 -1
- package/dist/features/admin/components/AdminScammerEditorView.js +3 -13
- package/dist/features/admin/components/AdminScammersView.js +3 -8
- package/dist/features/admin/components/AdminSectionsView.js +32 -23
- package/dist/features/admin/components/AdminSessionsView.js +17 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +31 -13
- package/dist/features/admin/components/AdminSiteView.js +6 -2
- package/dist/features/admin/components/AdminStickersView.js +8 -3
- package/dist/features/admin/components/ViewAuditLogEntryModal.js +1 -0
- package/dist/features/admin/components/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -2
- package/dist/features/admin/components/sections/adminSectionsTypes.js +45 -27
- package/dist/features/admin/constants/filter-tabs.d.ts +89 -0
- package/dist/features/admin/constants/filter-tabs.js +56 -0
- package/dist/features/admin/schemas/firestore.d.ts +122 -3
- package/dist/features/admin/schemas/firestore.js +1 -0
- package/dist/features/admin/schemas/homepage-section-form.d.ts +22 -0
- package/dist/features/admin/schemas/homepage-section-form.js +68 -0
- package/dist/features/admin/schemas/nav-item-form.d.ts +74 -0
- package/dist/features/admin/schemas/nav-item-form.js +72 -0
- package/dist/features/auctions/actions/bid-actions.d.ts +20 -5
- package/dist/features/auctions/actions/bid-actions.js +93 -20
- package/dist/features/auctions/components/AuctionBidsTable.d.ts +10 -1
- package/dist/features/auctions/components/AuctionBidsTable.js +22 -4
- package/dist/features/auctions/components/AuctionDetailPageView.js +11 -6
- package/dist/features/auctions/components/AuctionsListView.js +5 -6
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +7 -0
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +19 -2
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +8 -4
- package/dist/features/auctions/components/bid-detail-fields.d.ts +29 -0
- package/dist/features/auctions/components/bid-detail-fields.js +103 -0
- package/dist/features/auctions/repository/bid.repository.d.ts +13 -0
- package/dist/features/auctions/repository/bid.repository.js +23 -0
- package/dist/features/auctions/schemas/firestore.d.ts +17 -0
- package/dist/features/auctions/schemas/index.d.ts +12 -12
- package/dist/features/audit-log/schemas/firestore.d.ts +14 -0
- package/dist/features/audit-log/schemas/firestore.js +7 -0
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +10 -1
- package/dist/features/blog/actions/blog-actions.d.ts +7 -7
- package/dist/features/blog/schemas/blog-form.d.ts +80 -0
- package/dist/features/blog/schemas/blog-form.js +100 -0
- package/dist/features/blog/schemas/index.d.ts +6 -6
- package/dist/features/cart/repository/cart.repository.js +1 -0
- package/dist/features/cart/schemas/firestore.d.ts +15 -0
- package/dist/features/catalogue/schemas/validation.d.ts +4 -4
- package/dist/features/categories/components/BrandDetailPageView.js +23 -40
- package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -7
- package/dist/features/categories/components/BrandDetailTabs.js +8 -12
- package/dist/features/categories/components/BundleCollage.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +53 -57
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +10 -8
- package/dist/features/categories/components/CategoryDetailTabs.js +11 -16
- package/dist/features/categories/components/CategoryProductsListing.js +11 -1
- package/dist/features/categories/components/ConcernCard.js +7 -1
- package/dist/features/categories/schemas/firestore.d.ts +13 -0
- package/dist/features/categories/schemas/index.d.ts +26 -26
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +16 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.js +23 -3
- package/dist/features/events/components/AdminEventEditorView.js +32 -54
- package/dist/features/events/components/AdminEventEntriesView.js +22 -3
- package/dist/features/events/components/AdminEventsView.js +15 -11
- package/dist/features/events/components/EventFilters.d.ts +9 -1
- package/dist/features/events/components/EventFilters.js +45 -21
- package/dist/features/events/components/EventsIndexListing.d.ts +6 -1
- package/dist/features/events/components/EventsIndexListing.js +2 -2
- package/dist/features/events/components/EventsListPageView.js +13 -4
- package/dist/features/events/repository/events.repository.d.ts +17 -0
- package/dist/features/events/repository/events.repository.js +27 -0
- package/dist/features/events/schemas/event-form.d.ts +113 -0
- package/dist/features/events/schemas/event-form.js +82 -0
- package/dist/features/events/schemas/firestore.d.ts +12 -0
- package/dist/features/events/schemas/firestore.js +12 -0
- package/dist/features/events/schemas/index.d.ts +50 -50
- package/dist/features/events/types/index.d.ts +11 -0
- package/dist/features/events/types/index.js +57 -1
- package/dist/features/faq/actions/faq-actions.d.ts +12 -12
- package/dist/features/faq/schemas/index.d.ts +8 -8
- package/dist/features/filters/FilterFacetSection.d.ts +7 -1
- package/dist/features/filters/FilterFacetSection.js +11 -3
- package/dist/features/grouped/schemas/grouped-listing-form.d.ts +101 -0
- package/dist/features/grouped/schemas/grouped-listing-form.js +89 -0
- package/dist/features/grouped/schemas/index.d.ts +4 -4
- package/dist/features/homepage/actions/homepage-section-actions.d.ts +11 -3
- package/dist/features/homepage/actions/homepage-section-actions.js +33 -1
- package/dist/features/homepage/components/AnnouncementBar.d.ts +7 -1
- package/dist/features/homepage/components/AnnouncementBar.js +3 -3
- package/dist/features/homepage/components/MarketplaceHomepageView.js +20 -6
- package/dist/features/homepage/hooks/homepage-query.d.ts +16 -0
- package/dist/features/homepage/hooks/homepage-query.js +45 -0
- package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +10 -0
- package/dist/features/homepage/hooks/useFeaturedAuctions.js +28 -8
- package/dist/features/homepage/hooks/useFeaturedPreOrders.js +17 -8
- package/dist/features/homepage/hooks/useFeaturedProducts.js +15 -8
- package/dist/features/homepage/schemas/index.d.ts +33 -2
- package/dist/features/homepage/schemas/index.js +24 -0
- package/dist/features/layout/AppLayoutShell.d.ts +11 -1
- package/dist/features/layout/AppLayoutShell.js +15 -14
- package/dist/features/layout/BackToTop.d.ts +9 -0
- package/dist/features/layout/BackToTop.js +10 -1
- package/dist/features/layout/BottomActions.js +28 -25
- package/dist/features/layout/BottomChrome.d.ts +34 -0
- package/dist/features/layout/BottomChrome.js +55 -0
- package/dist/features/layout/BottomNavLayout.d.ts +6 -0
- package/dist/features/layout/BottomNavLayout.js +7 -1
- package/dist/features/layout/BottomNavbar.js +15 -2
- package/dist/features/layout/FooterLayout.js +11 -1
- package/dist/features/layout/NavbarLayout.js +1 -1
- package/dist/features/layout/TitleBarLayout.d.ts +7 -0
- package/dist/features/layout/TitleBarLayout.js +28 -5
- package/dist/features/layout/index.d.ts +2 -0
- package/dist/features/layout/index.js +1 -0
- package/dist/features/lottery/types/index.d.ts +3 -0
- package/dist/features/media/MediaPickerModal.js +5 -1
- package/dist/features/messages/schemas/index.d.ts +2 -2
- package/dist/features/orders/components/OrderScopeTabs.d.ts +20 -0
- package/dist/features/orders/components/OrderScopeTabs.js +31 -0
- package/dist/features/orders/components/OrderStatusTimeline.d.ts +5 -1
- package/dist/features/orders/components/OrderStatusTimeline.js +39 -20
- package/dist/features/orders/constants/order-scope.d.ts +48 -0
- package/dist/features/orders/constants/order-scope.js +111 -0
- package/dist/features/orders/repository/orders.repository.d.ts +54 -7
- package/dist/features/orders/repository/orders.repository.js +121 -22
- package/dist/features/orders/schemas/firestore.d.ts +152 -0
- package/dist/features/orders/schemas/firestore.js +30 -0
- package/dist/features/orders/schemas/index.d.ts +18 -18
- package/dist/features/orders/types/index.d.ts +35 -7
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +14 -9
- package/dist/features/pre-orders/components/PreOrdersListView.js +6 -5
- package/dist/features/pre-orders/schemas/index.d.ts +1 -1
- package/dist/features/products/actions/product-actions.d.ts +14 -0
- package/dist/features/products/actions/product-actions.js +49 -11
- package/dist/features/products/api/route.d.ts +12 -0
- package/dist/features/products/api/route.js +42 -100
- package/dist/features/products/components/ArtStickersListView.js +2 -2
- package/dist/features/products/components/AuctionsIndexListing.js +13 -11
- package/dist/features/products/components/AvailabilityTabs.d.ts +11 -0
- package/dist/features/products/components/AvailabilityTabs.js +47 -0
- package/dist/features/products/components/GroupSettingsPanel.js +5 -1
- package/dist/features/products/components/MakeOfferButton.js +48 -49
- package/dist/features/products/components/PrizeDrawCollage.d.ts +20 -4
- package/dist/features/products/components/PrizeDrawCollage.js +9 -1
- package/dist/features/products/components/PrizeDrawsIndexListing.js +14 -12
- package/dist/features/products/components/PrizeDrawsListingView.js +2 -2
- package/dist/features/products/components/PrizeDrawsSection.d.ts +7 -1
- package/dist/features/products/components/PrizeDrawsSection.js +23 -18
- package/dist/features/products/components/ProductDetailPageView.d.ts +4 -1
- package/dist/features/products/components/ProductDetailPageView.js +18 -4
- package/dist/features/products/components/ProductForm.js +5 -2
- package/dist/features/products/components/ProductGrid.js +6 -1
- package/dist/features/products/components/ProductsIndexListing.js +22 -32
- package/dist/features/products/components/ProductsIndexPageView.js +6 -9
- package/dist/features/products/components/index.d.ts +2 -2
- package/dist/features/products/components/index.js +1 -1
- package/dist/features/products/config/listing-type-listing-config.d.ts +20 -0
- package/dist/features/products/config/listing-type-listing-config.js +22 -10
- package/dist/features/products/constants/action-defs.d.ts +2 -0
- package/dist/features/products/constants/action-defs.js +6 -0
- package/dist/features/products/hooks/useAvailabilityScope.d.ts +13 -0
- package/dist/features/products/hooks/useAvailabilityScope.js +19 -0
- package/dist/features/products/hooks/useProducts.d.ts +5 -0
- package/dist/features/products/hooks/useProducts.js +13 -2
- package/dist/features/products/repository/products.repository.d.ts +31 -2
- package/dist/features/products/repository/products.repository.js +53 -21
- package/dist/features/products/schemas/firestore.d.ts +17 -3
- package/dist/features/products/schemas/firestore.js +7 -2
- package/dist/features/products/schemas/index.d.ts +64 -64
- package/dist/features/products/schemas/product-features.validators.d.ts +79 -6
- package/dist/features/products/schemas/product-features.validators.js +63 -0
- package/dist/features/products/types/index.d.ts +16 -1
- package/dist/features/promotions/schemas/index.d.ts +2 -2
- package/dist/features/reviews/schemas/index.d.ts +16 -16
- package/dist/features/scams/constants/scammer-status-display.d.ts +28 -0
- package/dist/features/scams/constants/scammer-status-display.js +79 -0
- package/dist/features/search/schemas/index.d.ts +4 -4
- package/dist/features/seller/actions/offer-actions.js +57 -4
- package/dist/features/seller/components/OfferPhaseTimeline.d.ts +59 -0
- package/dist/features/seller/components/OfferPhaseTimeline.js +107 -0
- package/dist/features/seller/components/SellerArtView.js +5 -0
- package/dist/features/seller/components/SellerAuctionsView.js +13 -15
- package/dist/features/seller/components/SellerBidsView.js +11 -4
- package/dist/features/seller/components/SellerClassifiedView.js +5 -0
- package/dist/features/seller/components/SellerCouponEditorView.js +17 -17
- package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
- package/dist/features/seller/components/SellerLiveView.js +5 -0
- package/dist/features/seller/components/SellerOffersView.js +2 -1
- package/dist/features/seller/components/SellerOrdersView.js +31 -9
- package/dist/features/seller/components/SellerPayoutSettingsView.js +56 -31
- package/dist/features/seller/components/SellerPreOrdersView.js +5 -0
- package/dist/features/seller/components/SellerPrizeDrawsView.js +14 -16
- package/dist/features/seller/components/SellerProductShell.js +7 -1
- package/dist/features/seller/components/SellerProductsView.js +11 -8
- package/dist/features/seller/components/SellerStickersView.js +5 -0
- package/dist/features/seller/components/index.d.ts +0 -2
- package/dist/features/seller/components/index.js +0 -1
- package/dist/features/seller/repository/offer.repository.d.ts +142 -24
- package/dist/features/seller/repository/offer.repository.js +165 -26
- package/dist/features/seller/schemas/coupon-form.d.ts +72 -0
- package/dist/features/seller/schemas/coupon-form.js +116 -0
- package/dist/features/seller/schemas/firestore.d.ts +56 -2
- package/dist/features/seller/schemas/firestore.js +27 -0
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/seller/schemas/offer-forms.d.ts +46 -0
- package/dist/features/seller/schemas/offer-forms.js +42 -0
- package/dist/features/seller/server.d.ts +0 -1
- package/dist/features/seller/server.js +0 -1
- package/dist/features/shell/SectionForm.d.ts +99 -0
- package/dist/features/shell/SectionForm.js +128 -0
- package/dist/features/shell/build-sections.d.ts +63 -0
- package/dist/features/shell/build-sections.js +207 -0
- package/dist/features/shell/entity-form.d.ts +109 -0
- package/dist/features/shell/entity-form.js +96 -0
- package/dist/features/shell/field-ui-meta.d.ts +115 -0
- package/dist/features/shell/field-ui-meta.js +324 -0
- package/dist/features/shell/index.d.ts +8 -0
- package/dist/features/shell/index.js +4 -0
- package/dist/features/status-history/components/StatusTimeline.d.ts +74 -0
- package/dist/features/status-history/components/StatusTimeline.js +46 -0
- package/dist/features/status-history/index.d.ts +11 -0
- package/dist/features/status-history/index.js +10 -0
- package/dist/features/store-extensions/components/ReviewDecisionModal.d.ts +27 -0
- package/dist/features/store-extensions/components/ReviewDecisionModal.js +82 -0
- package/dist/features/store-extensions/index.d.ts +12 -0
- package/dist/features/store-extensions/index.js +12 -0
- package/dist/features/store-extensions/schemas/admin-notification-form.d.ts +36 -0
- package/dist/features/store-extensions/schemas/admin-notification-form.js +114 -0
- package/dist/features/store-extensions/schemas/analytics-forms.d.ts +57 -0
- package/dist/features/store-extensions/schemas/analytics-forms.js +158 -0
- package/dist/features/store-extensions/schemas/custom-role-form.d.ts +85 -0
- package/dist/features/store-extensions/schemas/custom-role-form.js +118 -0
- package/dist/features/store-extensions/schemas/firestore.d.ts +21 -1
- package/dist/features/store-extensions/schemas/firestore.js +60 -0
- package/dist/features/store-extensions/schemas/google-config-form.d.ts +53 -0
- package/dist/features/store-extensions/schemas/google-config-form.js +105 -0
- package/dist/features/store-extensions/schemas/item-request-create-form.d.ts +28 -0
- package/dist/features/store-extensions/schemas/item-request-create-form.js +80 -0
- package/dist/features/store-extensions/schemas/listing-template-form.d.ts +78 -0
- package/dist/features/store-extensions/schemas/listing-template-form.js +87 -0
- package/dist/features/store-extensions/schemas/moderation-review-form.d.ts +49 -0
- package/dist/features/store-extensions/schemas/moderation-review-form.js +101 -0
- package/dist/features/store-extensions/schemas/payout-method-form.d.ts +153 -0
- package/dist/features/store-extensions/schemas/payout-method-form.js +155 -0
- package/dist/features/store-extensions/schemas/report-create-form.d.ts +25 -0
- package/dist/features/store-extensions/schemas/report-create-form.js +79 -0
- package/dist/features/store-extensions/schemas/report-review-form.d.ts +47 -0
- package/dist/features/store-extensions/schemas/report-review-form.js +111 -0
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +148 -0
- package/dist/features/store-extensions/schemas/shipping-config-form.js +104 -0
- package/dist/features/store-extensions/schemas/store-category-form.d.ts +54 -0
- package/dist/features/store-extensions/schemas/store-category-form.js +73 -0
- package/dist/features/stores/api/[storeSlug]/route.js +2 -25
- package/dist/features/stores/components/StoreAuctionsListing.js +15 -1
- package/dist/features/stores/components/StoreClassifiedsListing.js +12 -2
- package/dist/features/stores/components/StoreDetailLayoutView.js +24 -71
- package/dist/features/stores/components/StoreDigitalCodesListing.js +12 -2
- package/dist/features/stores/components/StoreLiveItemsListing.js +12 -2
- package/dist/features/stores/components/StorePreOrdersListing.js +12 -1
- package/dist/features/stores/components/StoreProductsListing.js +11 -1
- package/dist/features/stores/repository/store.repository.js +6 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- package/dist/features/support/schemas/index.d.ts +4 -4
- package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
- package/dist/features/tester/seed-data/offers-tester-seed-data.js +119 -3
- package/dist/features/tester/seed-data/products-tester-seed-data.js +131 -1
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +431 -1
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +25 -12
- package/dist/index.js +26 -11
- package/dist/next/routing/route-map.d.ts +23 -3
- package/dist/next/routing/route-map.js +14 -2
- package/dist/providers/db-firebase/base.repository.d.ts +30 -0
- package/dist/providers/db-firebase/base.repository.js +50 -1
- package/dist/providers/db-firebase/index.d.ts +1 -1
- package/dist/providers/db-firebase/index.js +1 -1
- package/dist/providers/db-firebase/sieve.d.ts +25 -0
- package/dist/providers/db-firebase/sieve.js +52 -18
- package/dist/react/hooks/useUrlTable.js +10 -1
- package/dist/schemas/firestore-value.d.ts +8 -0
- package/dist/schemas/firestore-value.js +44 -0
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/index.js +1 -1
- package/dist/schemas/registry.d.ts +702 -69
- package/dist/schemas/registry.js +64 -1
- package/dist/seed/_helpers/category-forest.d.ts +2 -0
- package/dist/seed/_helpers/category-forest.js +309 -0
- package/dist/seed/_helpers/category-tree.d.ts +27 -0
- package/dist/seed/_helpers/category-tree.js +103 -0
- package/dist/seed/bids-seed-data.js +30 -0
- package/dist/seed/catalogue-seed-data.js +12 -12
- package/dist/seed/categories-seed-data.js +177 -162
- package/dist/seed/events-seed-data.js +61 -10
- package/dist/seed/homepage-sections-seed-data.js +5 -1
- package/dist/seed/offers-seed-data.js +244 -34
- package/dist/seed/orders-seed-data.js +336 -1
- package/dist/seed/products-art-seed-data.js +50 -12
- package/dist/seed/products-auctions-seed-data.js +138 -18
- package/dist/seed/products-classifieds-seed-data.js +80 -16
- package/dist/seed/products-digital-codes-seed-data.js +66 -18
- package/dist/seed/products-live-items-seed-data.js +64 -13
- package/dist/seed/products-preorders-seed-data.d.ts +0 -1
- package/dist/seed/products-preorders-seed-data.js +52 -13
- package/dist/seed/products-prize-draws-seed-data.js +63 -14
- package/dist/seed/products-standard-seed-data.js +99 -26
- package/dist/seed/products-stickers-seed-data.js +47 -12
- package/dist/seed/reviews-seed-data.js +6 -1
- package/dist/seed/site-settings-seed-data.d.ts +1 -38
- package/dist/seed/site-settings-seed-data.js +38 -76
- package/dist/seed/stores-seed-data.js +71 -1
- package/dist/seed/users-seed-data.js +15 -5
- package/dist/server-entry.d.ts +1 -2
- package/dist/server-entry.js +4 -3
- package/dist/server.d.ts +6 -3
- package/dist/server.js +13 -4
- package/dist/styles.css +78 -12
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Button.style.css +19 -4
- package/dist/ui/components/CollapsibleSection.d.ts +17 -1
- package/dist/ui/components/CollapsibleSection.js +6 -2
- package/dist/ui/components/FilterChipGroup.d.ts +25 -1
- package/dist/ui/components/FilterChipGroup.js +21 -4
- package/dist/ui/components/HandMode.style.css +47 -0
- package/dist/ui/components/Layout.d.ts +9 -2
- package/dist/ui/components/Layout.js +1 -1
- package/dist/ui/components/ListingLayout.js +8 -8
- package/dist/ui/components/ListingLayout.style.css +8 -7
- package/dist/ui/components/RecordDetailModal.d.ts +10 -1
- package/dist/ui/components/RecordDetailModal.js +2 -2
- package/dist/ui/components/StickyToolbar.d.ts +3 -1
- package/dist/ui/components/StickyToolbar.js +62 -20
- package/dist/ui/components/bottom-chrome-slot.d.ts +21 -0
- package/dist/ui/components/bottom-chrome-slot.js +29 -0
- package/dist/ui/components/index.style.css +3 -0
- package/dist/ui/forms/FormShell.d.ts +43 -13
- package/dist/ui/forms/FormShell.js +22 -11
- package/dist/ui/forms/index.d.ts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +2 -1
- package/scripts/node-esm-loader.mjs +65 -0
- package/scripts/seed-cli-loader.mjs +6 -49
- package/scripts/seed-cli.mjs +1 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracked-field diffing and capped append for entity status history.
|
|
3
|
+
*
|
|
4
|
+
* These are deliberately plain functions rather than a `BaseRepository`
|
|
5
|
+
* override: the repository method that owns a transition already knows the
|
|
6
|
+
* actor and the trigger, and a generic hook would have to guess both. The
|
|
7
|
+
* repository calls `buildHistoryEntry` + `appendHistoryEntry` inside its own
|
|
8
|
+
* write primitive — which is the point, since that is what keeps the 32
|
|
9
|
+
* call sites above it unchanged.
|
|
10
|
+
*/
|
|
11
|
+
import { normalizeError } from "../../../errors/normalize";
|
|
12
|
+
import { STATUS_HISTORY_MAX, } from "./types";
|
|
13
|
+
/**
|
|
14
|
+
* Structural equality for field values.
|
|
15
|
+
*
|
|
16
|
+
* Tracked fields are not all scalars, so identity comparison would flag an
|
|
17
|
+
* unchanged object as changed on every write. Dates compare by instant,
|
|
18
|
+
* because Firestore hands back a fresh object on every read — without this,
|
|
19
|
+
* re-saving a document with no edits would append a history entry.
|
|
20
|
+
*/
|
|
21
|
+
function isSameValue(a, b) {
|
|
22
|
+
if (a === b)
|
|
23
|
+
return true;
|
|
24
|
+
if (a == null || b == null)
|
|
25
|
+
return a == null && b == null;
|
|
26
|
+
const aDate = toDateOrNull(a);
|
|
27
|
+
const bDate = toDateOrNull(b);
|
|
28
|
+
if (aDate && bDate)
|
|
29
|
+
return aDate.getTime() === bDate.getTime();
|
|
30
|
+
if (aDate || bDate)
|
|
31
|
+
return false;
|
|
32
|
+
if (typeof a !== "object" || typeof b !== "object")
|
|
33
|
+
return false;
|
|
34
|
+
try {
|
|
35
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
void normalizeError(err);
|
|
39
|
+
// Circular or non-serialisable — treat as changed rather than throwing
|
|
40
|
+
// inside a write path.
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Firestore Timestamps expose `toDate()`; plain Dates are used directly. */
|
|
45
|
+
function toDateOrNull(v) {
|
|
46
|
+
if (v instanceof Date)
|
|
47
|
+
return v;
|
|
48
|
+
if (typeof v === "object" && v !== null && typeof v.toDate === "function") {
|
|
49
|
+
try {
|
|
50
|
+
return v.toDate();
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
void normalizeError(err);
|
|
54
|
+
// A Timestamp-like object whose toDate() threw is not a usable date;
|
|
55
|
+
// the caller treats null as "not a date" and falls through to structural
|
|
56
|
+
// comparison, so there is nothing to report.
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
/** Read a possibly-dotted path (`emiInstallments.0.status`) off an object. */
|
|
63
|
+
function readPath(source, path) {
|
|
64
|
+
if (!source)
|
|
65
|
+
return undefined;
|
|
66
|
+
if (!path.includes("."))
|
|
67
|
+
return source[path];
|
|
68
|
+
let cursor = source;
|
|
69
|
+
for (const segment of path.split(".")) {
|
|
70
|
+
if (cursor == null || typeof cursor !== "object")
|
|
71
|
+
return undefined;
|
|
72
|
+
cursor = cursor[segment];
|
|
73
|
+
}
|
|
74
|
+
return cursor;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Diff only the fields an entity declares as tracked.
|
|
78
|
+
*
|
|
79
|
+
* The tracked list is explicit per entity, never "every field": a whole-
|
|
80
|
+
* document diff would record the `updatedAt` bump on every single write and
|
|
81
|
+
* exhaust the 50-entry cap within days, burying the transitions anyone
|
|
82
|
+
* actually wants to read.
|
|
83
|
+
*
|
|
84
|
+
* A field absent from `patch` is untouched — this diffs the patch against
|
|
85
|
+
* the current document, not two full documents.
|
|
86
|
+
*/
|
|
87
|
+
export function diffTrackedFields(current, patch, tracked) {
|
|
88
|
+
const changes = {};
|
|
89
|
+
for (const field of tracked) {
|
|
90
|
+
if (!(field in patch) && !field.includes("."))
|
|
91
|
+
continue;
|
|
92
|
+
const to = readPath(patch, field);
|
|
93
|
+
if (to === undefined)
|
|
94
|
+
continue;
|
|
95
|
+
const from = readPath(current, field);
|
|
96
|
+
if (isSameValue(from, to))
|
|
97
|
+
continue;
|
|
98
|
+
changes[field] = { from: from ?? null, to };
|
|
99
|
+
}
|
|
100
|
+
return changes;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Remove PII-named keys from a `changes` map, at any depth.
|
|
104
|
+
*
|
|
105
|
+
* Two passes are needed because PII can arrive two ways: as a tracked field
|
|
106
|
+
* that happens to be PII (`buyerEmail` in `changes`), or nested inside a
|
|
107
|
+
* value a caller contributed via `extraChanges` (a refund's `reason`, an
|
|
108
|
+
* offer note object). `encryptPiiFields` catches neither — it only ever
|
|
109
|
+
* looks at top-level string properties of the document itself.
|
|
110
|
+
*
|
|
111
|
+
* Scrubbed rather than redacted-in-place: an entry that silently omits a
|
|
112
|
+
* field is honest, whereas one containing `"[redacted]"` invites a reader to
|
|
113
|
+
* assume the real value is recoverable somewhere.
|
|
114
|
+
*/
|
|
115
|
+
function scrubPii(changes, piiFields) {
|
|
116
|
+
if (!piiFields?.length)
|
|
117
|
+
return changes;
|
|
118
|
+
const pii = new Set(piiFields);
|
|
119
|
+
const stripNested = (v) => {
|
|
120
|
+
if (v == null || typeof v !== "object")
|
|
121
|
+
return v;
|
|
122
|
+
if (v instanceof Date)
|
|
123
|
+
return v;
|
|
124
|
+
if (Array.isArray(v))
|
|
125
|
+
return v.map(stripNested);
|
|
126
|
+
const out = {};
|
|
127
|
+
for (const [k, val] of Object.entries(v)) {
|
|
128
|
+
if (pii.has(k))
|
|
129
|
+
continue;
|
|
130
|
+
out[k] = stripNested(val);
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
};
|
|
134
|
+
const result = {};
|
|
135
|
+
for (const [field, change] of Object.entries(changes)) {
|
|
136
|
+
if (pii.has(field))
|
|
137
|
+
continue; // the tracked field itself is PII
|
|
138
|
+
result[field] = { from: stripNested(change.from), to: stripNested(change.to) };
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
export function buildHistoryEntry(input) {
|
|
143
|
+
const entry = {
|
|
144
|
+
at: input.now ?? new Date(),
|
|
145
|
+
actorRole: input.actor.role,
|
|
146
|
+
changes: input.changes,
|
|
147
|
+
trigger: input.trigger,
|
|
148
|
+
};
|
|
149
|
+
if (input.actor.uid)
|
|
150
|
+
entry.actorUid = input.actor.uid;
|
|
151
|
+
if (input.reason)
|
|
152
|
+
entry.reason = input.reason;
|
|
153
|
+
if (input.note)
|
|
154
|
+
entry.note = input.note;
|
|
155
|
+
return entry;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Append with a FIFO cap. Returns the new array plus the running count of
|
|
159
|
+
* entries dropped over the document's lifetime, so the caller can persist it
|
|
160
|
+
* and the UI can be honest about what it is not showing.
|
|
161
|
+
*/
|
|
162
|
+
export function appendHistoryEntry(existing, entry, previousTruncatedCount = 0, cap = STATUS_HISTORY_MAX) {
|
|
163
|
+
const next = [...(existing ?? []), entry];
|
|
164
|
+
if (next.length <= cap) {
|
|
165
|
+
return { history: next, truncatedCount: previousTruncatedCount };
|
|
166
|
+
}
|
|
167
|
+
const overflow = next.length - cap;
|
|
168
|
+
return {
|
|
169
|
+
history: next.slice(overflow),
|
|
170
|
+
truncatedCount: previousTruncatedCount + overflow,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The whole flow in one call: diff, and return the patch additions needed to
|
|
175
|
+
* record it. Returns `null` when no tracked field changed — the caller then
|
|
176
|
+
* writes its patch unchanged, so an untracked update never grows the array.
|
|
177
|
+
*/
|
|
178
|
+
export function withHistory(current, patch, opts) {
|
|
179
|
+
const changes = scrubPii({ ...diffTrackedFields(current, patch, opts.tracked), ...opts.extraChanges }, opts.piiFields);
|
|
180
|
+
if (Object.keys(changes).length === 0)
|
|
181
|
+
return null;
|
|
182
|
+
const historyField = opts.historyField ?? "statusHistory";
|
|
183
|
+
const truncatedField = opts.truncatedField ?? "statusHistoryTruncated";
|
|
184
|
+
const entry = buildHistoryEntry({
|
|
185
|
+
actor: opts.actor,
|
|
186
|
+
trigger: opts.trigger,
|
|
187
|
+
changes,
|
|
188
|
+
reason: opts.reason,
|
|
189
|
+
note: opts.note,
|
|
190
|
+
now: opts.now,
|
|
191
|
+
});
|
|
192
|
+
const { history, truncatedCount } = appendHistoryEntry(readPath(current, historyField) ?? [], entry, readPath(current, truncatedField) ?? 0, opts.cap);
|
|
193
|
+
return {
|
|
194
|
+
...patch,
|
|
195
|
+
[historyField]: history,
|
|
196
|
+
...(truncatedCount > 0 ? { [truncatedField]: truncatedCount } : {}),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { HistoryActorRole, HistoryActor, FieldChange, StatusChangeEntry, AppendHistoryResult, } from "./types";
|
|
2
|
+
export { STATUS_HISTORY_MAX } from "./types";
|
|
3
|
+
export type { BuildHistoryEntryInput } from "./history";
|
|
4
|
+
export { diffTrackedFields, buildHistoryEntry, appendHistoryEntry, withHistory, } from "./history";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { FirestoreValue } from "../../../schemas/types";
|
|
2
|
+
/**
|
|
3
|
+
* Generic entity status history — the shape shared by every entity that
|
|
4
|
+
* records how it got to its current state.
|
|
5
|
+
*
|
|
6
|
+
* Built for orders first (W2) and adopted by stores, payouts, offers, bids,
|
|
7
|
+
* support tickets, scammer profiles, catalogue approvals, shipments and
|
|
8
|
+
* returns in W15. Before this existed, exactly ONE of ~40 status-bearing
|
|
9
|
+
* entities had any history at all — `ads`, built inline in a route handler
|
|
10
|
+
* with no type and no reuse.
|
|
11
|
+
*/
|
|
12
|
+
export type HistoryActorRole = "buyer" | "seller" | "admin" | "system";
|
|
13
|
+
export interface HistoryActor {
|
|
14
|
+
/** Firebase Auth uid. Absent for `system` (cron jobs, Firestore triggers). */
|
|
15
|
+
uid?: string;
|
|
16
|
+
role: HistoryActorRole;
|
|
17
|
+
}
|
|
18
|
+
/** A single field's before/after. `from` is `null` on the creation entry. */
|
|
19
|
+
export interface FieldChange {
|
|
20
|
+
from: FirestoreValue;
|
|
21
|
+
to: FirestoreValue;
|
|
22
|
+
}
|
|
23
|
+
export interface StatusChangeEntry {
|
|
24
|
+
at: Date;
|
|
25
|
+
actorUid?: string;
|
|
26
|
+
actorRole: HistoryActorRole;
|
|
27
|
+
/**
|
|
28
|
+
* DELTA ONLY — the tracked fields that actually changed, never a copy of
|
|
29
|
+
* the whole document. A full snapshot per transition would duplicate the
|
|
30
|
+
* record on every status change; the whole point of this shape is that it
|
|
31
|
+
* does not.
|
|
32
|
+
*/
|
|
33
|
+
changes: Record<string, FieldChange>;
|
|
34
|
+
/** Why, when a human supplied a reason (cancellation, rejection). */
|
|
35
|
+
reason?: string;
|
|
36
|
+
/** Free-text note from an admin/seller action. */
|
|
37
|
+
note?: string;
|
|
38
|
+
/**
|
|
39
|
+
* The function, route or job that caused the change — `"customShipOrder"`,
|
|
40
|
+
* `"paymentWindowTimeout"`, `"adminVerifyPayment"`. This is what the
|
|
41
|
+
* timeline renders by, which is why per-path events (proof uploaded,
|
|
42
|
+
* installment paid, winner revealed) need NO new status values.
|
|
43
|
+
*/
|
|
44
|
+
trigger: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Cap for any history array. Matches `HISTORY_MAX`'s reasoning: an unbounded
|
|
48
|
+
* array on a hot document inflates every read of that document (Rule #6), and
|
|
49
|
+
* an order that changes status more than this has a different problem.
|
|
50
|
+
*/
|
|
51
|
+
export declare const STATUS_HISTORY_MAX = 50;
|
|
52
|
+
export interface AppendHistoryResult {
|
|
53
|
+
history: StatusChangeEntry[];
|
|
54
|
+
/**
|
|
55
|
+
* How many entries have been dropped off the front across the document's
|
|
56
|
+
* lifetime. Surfaced so the UI can say "earlier history trimmed" instead
|
|
57
|
+
* of implying none existed — silent truncation reads as "this is all of
|
|
58
|
+
* it", which is the same lie as a silently dropped field.
|
|
59
|
+
*/
|
|
60
|
+
truncatedCount: number;
|
|
61
|
+
}
|
|
@@ -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;
|