@mohasinac/appkit 4.13.0 → 4.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +14 -5
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +3 -0
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +6 -0
- package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +8 -0
- package/dist/_internal/client/features/lottery/LotteryDetailView.js +4 -1
- package/dist/_internal/client/features/lottery/LotteryListView.d.ts +7 -0
- package/dist/_internal/client/features/lottery/LotteryListView.js +1 -1
- package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +4 -1
- package/dist/_internal/client/features/lottery/slot-collage-items.d.ts +15 -0
- package/dist/_internal/client/features/lottery/slot-collage-items.js +13 -0
- package/dist/_internal/server/features/auctions/actions.js +6 -0
- package/dist/_internal/server/features/bundles/data.d.ts +16 -0
- package/dist/_internal/server/features/bundles/data.js +26 -0
- package/dist/_internal/server/features/bundles/index.d.ts +1 -1
- package/dist/_internal/server/features/bundles/index.js +1 -1
- package/dist/_internal/server/features/checkout/actions.js +6 -0
- package/dist/_internal/server/features/checkout/locked-lines.d.ts +38 -0
- package/dist/_internal/server/features/checkout/locked-lines.js +171 -0
- package/dist/_internal/server/features/homepage/index.d.ts +0 -1
- package/dist/_internal/server/features/homepage/index.js +7 -1
- package/dist/_internal/server/features/lottery/adapters.js +4 -0
- package/dist/_internal/server/features/products/data.js +15 -37
- package/dist/_internal/server/features/products/index.d.ts +1 -1
- package/dist/_internal/server/features/products/index.js +1 -1
- package/dist/_internal/server/features/products/list-public.d.ts +46 -20
- package/dist/_internal/server/features/products/list-public.js +266 -90
- package/dist/_internal/server/features/products/listing-tab-counts.d.ts +44 -0
- package/dist/_internal/server/features/products/listing-tab-counts.js +97 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +51 -0
- package/dist/_internal/server/features/site-settings/adapters.js +75 -0
- package/dist/_internal/server/features/site-settings/index.d.ts +1 -0
- package/dist/_internal/server/features/site-settings/index.js +1 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +56 -0
- package/dist/_internal/server/features/stores/adapters.js +91 -0
- package/dist/_internal/server/features/stores/index.d.ts +1 -0
- package/dist/_internal/server/features/stores/index.js +1 -0
- package/dist/_internal/server/jobs/core/auctionSettlement.js +56 -2
- package/dist/_internal/server/jobs/core/offerExpiry.js +45 -9
- package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.d.ts +5 -0
- package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.js +33 -1
- package/dist/_internal/server/jobs/handlers/messages.d.ts +7 -0
- package/dist/_internal/server/jobs/handlers/messages.js +10 -0
- package/dist/_internal/shared/actions/action-registry.js +15 -1
- package/dist/_internal/shared/checkout/lanes.d.ts +25 -0
- package/dist/_internal/shared/checkout/lanes.js +26 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +27 -0
- package/dist/_internal/shared/features/auctions/config.js +27 -0
- package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
- package/dist/_internal/shared/features/cart/schema.d.ts +12 -12
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
- package/dist/_internal/shared/features/emi/schedule.d.ts +24 -3
- package/dist/_internal/shared/features/emi/schedule.js +20 -5
- package/dist/_internal/shared/features/events/schema.d.ts +2 -2
- package/dist/_internal/shared/features/homepage/section-gate.d.ts +14 -0
- package/dist/_internal/shared/features/homepage/section-gate.js +51 -0
- package/dist/_internal/shared/features/offers/config.d.ts +30 -0
- package/dist/_internal/shared/features/offers/config.js +59 -0
- package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
- package/dist/_internal/shared/features/products/schema.d.ts +32 -32
- package/dist/_internal/shared/features/products/to-product-item.js +6 -0
- package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
- package/dist/_internal/shared/features/site-settings/fees.d.ts +60 -0
- package/dist/_internal/shared/features/site-settings/fees.js +39 -0
- package/dist/_internal/shared/features/site-settings/types.d.ts +40 -0
- package/dist/_internal/shared/features/site-settings/types.js +1 -0
- package/dist/_internal/shared/history/history.d.ts +66 -0
- package/dist/_internal/shared/history/history.js +158 -0
- package/dist/_internal/shared/history/index.d.ts +4 -0
- package/dist/_internal/shared/history/index.js +2 -0
- package/dist/_internal/shared/history/types.d.ts +61 -0
- package/dist/_internal/shared/history/types.js +6 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +54 -1
- package/dist/_internal/shared/listing-types/_registry.js +67 -0
- package/dist/_internal/shared/listing-types/art/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/art/config.js +5 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +13 -0
- package/dist/_internal/shared/listing-types/auction/config.js +21 -0
- package/dist/_internal/shared/listing-types/availability.d.ts +79 -0
- package/dist/_internal/shared/listing-types/availability.js +104 -0
- package/dist/_internal/shared/listing-types/capabilities.d.ts +15 -0
- package/dist/_internal/shared/listing-types/capabilities.js +12 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/classified/config.js +5 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +11 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +22 -2
- package/dist/_internal/shared/listing-types/live/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/live/config.js +5 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +19 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +11 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +15 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/standard/config.js +5 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +3 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +5 -0
- package/dist/client.d.ts +7 -5
- package/dist/client.js +2 -2
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/constants/field-names.d.ts +49 -2
- package/dist/constants/field-names.js +53 -2
- package/dist/constants/table-keys.d.ts +17 -0
- package/dist/constants/table-keys.js +17 -0
- package/dist/errors/error-codes.d.ts +13 -1
- package/dist/errors/error-codes.js +13 -1
- package/dist/errors/error-display-map.js +12 -3
- package/dist/errors/messages.d.ts +10 -2
- package/dist/errors/messages.js +10 -2
- package/dist/features/about/components/AboutView.d.ts +3 -0
- package/dist/features/about/components/AboutView.js +3 -2
- package/dist/features/about/components/PolicyPageView.d.ts +2 -1
- package/dist/features/about/components/PolicyPageView.js +61 -23
- package/dist/features/about/components/PublicProfileView.js +5 -0
- package/dist/features/about/schemas/firestore.d.ts +3 -0
- package/dist/features/account/components/UserOrdersView.js +7 -0
- package/dist/features/account/components/index.d.ts +0 -2
- package/dist/features/account/components/index.js +0 -1
- package/dist/features/account/schemas/index.d.ts +2 -2
- package/dist/features/account/types/index.d.ts +21 -2
- package/dist/features/addresses/schemas/index.d.ts +4 -4
- package/dist/features/admin/components/AdminArtView.js +8 -3
- package/dist/features/admin/components/AdminClassifiedView.js +8 -3
- package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
- package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
- package/dist/features/admin/components/AdminLiveView.js +8 -3
- package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
- package/dist/features/admin/components/AdminOffersView.js +186 -0
- package/dist/features/admin/components/AdminOrdersView.js +10 -1
- package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
- package/dist/features/admin/components/AdminProductsView.js +16 -29
- package/dist/features/admin/components/AdminSiteSettingsView.js +31 -13
- package/dist/features/admin/components/AdminSiteView.js +6 -2
- package/dist/features/admin/components/AdminStickersView.js +8 -3
- package/dist/features/admin/components/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +86 -0
- package/dist/features/admin/constants/filter-tabs.js +55 -0
- package/dist/features/admin/schemas/firestore.d.ts +122 -3
- package/dist/features/admin/schemas/firestore.js +1 -0
- package/dist/features/auctions/actions/bid-actions.d.ts +20 -5
- package/dist/features/auctions/actions/bid-actions.js +93 -20
- package/dist/features/auctions/components/AuctionDetailPageView.js +11 -6
- package/dist/features/auctions/components/AuctionsListView.js +5 -6
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +7 -0
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +19 -2
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +8 -4
- package/dist/features/auctions/repository/bid.repository.d.ts +13 -0
- package/dist/features/auctions/repository/bid.repository.js +23 -0
- package/dist/features/auctions/schemas/firestore.d.ts +17 -0
- package/dist/features/auctions/schemas/index.d.ts +12 -12
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +10 -1
- package/dist/features/blog/actions/blog-actions.d.ts +7 -7
- package/dist/features/blog/schemas/index.d.ts +6 -6
- package/dist/features/cart/repository/cart.repository.js +1 -0
- package/dist/features/cart/schemas/firestore.d.ts +15 -0
- package/dist/features/catalogue/schemas/validation.d.ts +4 -4
- package/dist/features/categories/components/BrandDetailPageView.js +23 -40
- package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -7
- package/dist/features/categories/components/BrandDetailTabs.js +8 -12
- package/dist/features/categories/components/BundleCollage.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +53 -57
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +10 -8
- package/dist/features/categories/components/CategoryDetailTabs.js +11 -16
- package/dist/features/categories/components/CategoryProductsListing.js +11 -1
- package/dist/features/categories/components/ConcernCard.js +7 -1
- package/dist/features/categories/schemas/firestore.d.ts +13 -0
- package/dist/features/categories/schemas/index.d.ts +26 -26
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +16 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.js +23 -3
- package/dist/features/events/components/AdminEventsView.js +15 -11
- package/dist/features/events/components/EventFilters.d.ts +9 -1
- package/dist/features/events/components/EventFilters.js +45 -21
- package/dist/features/events/components/EventsIndexListing.d.ts +6 -1
- package/dist/features/events/components/EventsIndexListing.js +2 -2
- package/dist/features/events/components/EventsListPageView.js +13 -4
- package/dist/features/events/repository/events.repository.d.ts +17 -0
- package/dist/features/events/repository/events.repository.js +27 -0
- package/dist/features/events/schemas/firestore.d.ts +12 -0
- package/dist/features/events/schemas/firestore.js +12 -0
- package/dist/features/events/schemas/index.d.ts +50 -50
- package/dist/features/events/types/index.d.ts +11 -0
- package/dist/features/events/types/index.js +57 -1
- package/dist/features/faq/actions/faq-actions.d.ts +12 -12
- package/dist/features/faq/schemas/index.d.ts +8 -8
- package/dist/features/filters/FilterFacetSection.d.ts +7 -1
- package/dist/features/filters/FilterFacetSection.js +11 -3
- package/dist/features/grouped/schemas/index.d.ts +4 -4
- package/dist/features/homepage/components/AnnouncementBar.d.ts +7 -1
- package/dist/features/homepage/components/AnnouncementBar.js +3 -3
- package/dist/features/homepage/components/MarketplaceHomepageView.js +20 -6
- package/dist/features/homepage/hooks/homepage-query.d.ts +16 -0
- package/dist/features/homepage/hooks/homepage-query.js +45 -0
- package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +10 -0
- package/dist/features/homepage/hooks/useFeaturedAuctions.js +28 -8
- package/dist/features/homepage/hooks/useFeaturedPreOrders.js +17 -8
- package/dist/features/homepage/hooks/useFeaturedProducts.js +15 -8
- package/dist/features/homepage/schemas/index.d.ts +2 -2
- package/dist/features/layout/AppLayoutShell.d.ts +11 -1
- package/dist/features/layout/AppLayoutShell.js +15 -14
- package/dist/features/layout/BackToTop.d.ts +9 -0
- package/dist/features/layout/BackToTop.js +10 -1
- package/dist/features/layout/BottomActions.js +28 -25
- package/dist/features/layout/BottomChrome.d.ts +34 -0
- package/dist/features/layout/BottomChrome.js +55 -0
- package/dist/features/layout/BottomNavLayout.d.ts +6 -0
- package/dist/features/layout/BottomNavLayout.js +7 -1
- package/dist/features/layout/BottomNavbar.js +15 -2
- package/dist/features/layout/FooterLayout.js +11 -1
- package/dist/features/layout/NavbarLayout.js +1 -1
- package/dist/features/layout/TitleBarLayout.d.ts +7 -0
- package/dist/features/layout/TitleBarLayout.js +28 -5
- package/dist/features/layout/index.d.ts +2 -0
- package/dist/features/layout/index.js +1 -0
- package/dist/features/lottery/types/index.d.ts +3 -0
- package/dist/features/media/MediaPickerModal.js +5 -1
- package/dist/features/messages/schemas/index.d.ts +2 -2
- package/dist/features/orders/components/OrderScopeTabs.d.ts +20 -0
- package/dist/features/orders/components/OrderScopeTabs.js +31 -0
- package/dist/features/orders/constants/order-scope.d.ts +48 -0
- package/dist/features/orders/constants/order-scope.js +111 -0
- package/dist/features/orders/repository/orders.repository.d.ts +54 -7
- package/dist/features/orders/repository/orders.repository.js +116 -22
- package/dist/features/orders/schemas/firestore.d.ts +117 -0
- package/dist/features/orders/schemas/firestore.js +30 -0
- package/dist/features/orders/schemas/index.d.ts +18 -18
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +14 -9
- package/dist/features/pre-orders/components/PreOrdersListView.js +6 -5
- package/dist/features/pre-orders/schemas/index.d.ts +1 -1
- package/dist/features/products/actions/product-actions.d.ts +14 -0
- package/dist/features/products/actions/product-actions.js +49 -11
- package/dist/features/products/api/route.d.ts +12 -0
- package/dist/features/products/api/route.js +42 -100
- package/dist/features/products/components/ArtStickersListView.js +2 -2
- package/dist/features/products/components/AuctionsIndexListing.js +13 -11
- package/dist/features/products/components/AvailabilityTabs.d.ts +11 -0
- package/dist/features/products/components/AvailabilityTabs.js +47 -0
- package/dist/features/products/components/MakeOfferButton.js +48 -49
- package/dist/features/products/components/PrizeDrawCollage.d.ts +20 -4
- package/dist/features/products/components/PrizeDrawCollage.js +9 -1
- package/dist/features/products/components/PrizeDrawsIndexListing.js +14 -12
- package/dist/features/products/components/PrizeDrawsListingView.js +2 -2
- package/dist/features/products/components/PrizeDrawsSection.d.ts +7 -1
- package/dist/features/products/components/PrizeDrawsSection.js +23 -18
- package/dist/features/products/components/ProductDetailPageView.d.ts +4 -1
- package/dist/features/products/components/ProductDetailPageView.js +18 -4
- package/dist/features/products/components/ProductForm.js +5 -2
- package/dist/features/products/components/ProductGrid.js +6 -1
- package/dist/features/products/components/ProductsIndexListing.js +22 -32
- package/dist/features/products/components/ProductsIndexPageView.js +6 -9
- package/dist/features/products/components/index.d.ts +2 -2
- package/dist/features/products/components/index.js +1 -1
- package/dist/features/products/config/listing-type-listing-config.d.ts +20 -0
- package/dist/features/products/config/listing-type-listing-config.js +22 -10
- package/dist/features/products/constants/action-defs.d.ts +2 -0
- package/dist/features/products/constants/action-defs.js +6 -0
- package/dist/features/products/hooks/useAvailabilityScope.d.ts +13 -0
- package/dist/features/products/hooks/useAvailabilityScope.js +19 -0
- package/dist/features/products/hooks/useProducts.d.ts +5 -0
- package/dist/features/products/hooks/useProducts.js +13 -2
- package/dist/features/products/repository/products.repository.d.ts +31 -2
- package/dist/features/products/repository/products.repository.js +53 -21
- package/dist/features/products/schemas/firestore.d.ts +17 -3
- package/dist/features/products/schemas/firestore.js +7 -2
- package/dist/features/products/schemas/index.d.ts +66 -66
- package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
- package/dist/features/products/types/index.d.ts +16 -1
- package/dist/features/promotions/schemas/index.d.ts +2 -2
- package/dist/features/reviews/schemas/index.d.ts +16 -16
- package/dist/features/search/schemas/index.d.ts +4 -4
- package/dist/features/seller/actions/offer-actions.js +25 -2
- package/dist/features/seller/components/SellerArtView.js +5 -0
- package/dist/features/seller/components/SellerAuctionsView.js +13 -15
- package/dist/features/seller/components/SellerClassifiedView.js +5 -0
- package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
- package/dist/features/seller/components/SellerLiveView.js +5 -0
- package/dist/features/seller/components/SellerOrdersView.js +31 -9
- package/dist/features/seller/components/SellerPayoutSettingsView.js +19 -18
- package/dist/features/seller/components/SellerPreOrdersView.js +5 -0
- package/dist/features/seller/components/SellerPrizeDrawsView.js +14 -16
- package/dist/features/seller/components/SellerProductShell.js +7 -1
- package/dist/features/seller/components/SellerProductsView.js +11 -8
- package/dist/features/seller/components/SellerStickersView.js +5 -0
- package/dist/features/seller/components/index.d.ts +0 -2
- package/dist/features/seller/components/index.js +0 -1
- package/dist/features/seller/repository/offer.repository.d.ts +73 -16
- package/dist/features/seller/repository/offer.repository.js +38 -3
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/seller/schemas/offer-forms.d.ts +25 -0
- package/dist/features/seller/schemas/offer-forms.js +21 -0
- package/dist/features/seller/server.d.ts +0 -1
- package/dist/features/seller/server.js +0 -1
- package/dist/features/shell/SectionForm.d.ts +99 -0
- package/dist/features/shell/SectionForm.js +128 -0
- package/dist/features/shell/entity-form.d.ts +109 -0
- package/dist/features/shell/entity-form.js +96 -0
- package/dist/features/shell/index.d.ts +4 -0
- package/dist/features/shell/index.js +2 -0
- package/dist/features/stores/api/[storeSlug]/route.js +2 -25
- package/dist/features/stores/components/StoreAuctionsListing.js +15 -1
- package/dist/features/stores/components/StoreClassifiedsListing.js +12 -2
- package/dist/features/stores/components/StoreDetailLayoutView.js +24 -71
- package/dist/features/stores/components/StoreDigitalCodesListing.js +12 -2
- package/dist/features/stores/components/StoreLiveItemsListing.js +12 -2
- package/dist/features/stores/components/StorePreOrdersListing.js +12 -1
- package/dist/features/stores/components/StoreProductsListing.js +11 -1
- package/dist/features/stores/repository/store.repository.js +6 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- package/dist/features/support/schemas/index.d.ts +4 -4
- package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
- package/dist/features/tester/seed-data/offers-tester-seed-data.js +119 -3
- package/dist/features/tester/seed-data/products-tester-seed-data.js +131 -1
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +105 -1
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +13 -10
- package/dist/index.js +14 -9
- package/dist/next/routing/route-map.d.ts +7 -1
- package/dist/next/routing/route-map.js +5 -0
- package/dist/providers/db-firebase/base.repository.d.ts +30 -0
- package/dist/providers/db-firebase/base.repository.js +50 -1
- package/dist/providers/db-firebase/index.d.ts +1 -1
- package/dist/providers/db-firebase/index.js +1 -1
- package/dist/providers/db-firebase/sieve.d.ts +25 -0
- package/dist/providers/db-firebase/sieve.js +52 -18
- package/dist/react/hooks/useUrlTable.js +10 -1
- package/dist/schemas/registry.d.ts +68 -68
- package/dist/seed/_helpers/category-forest.d.ts +2 -0
- package/dist/seed/_helpers/category-forest.js +309 -0
- package/dist/seed/_helpers/category-tree.d.ts +27 -0
- package/dist/seed/_helpers/category-tree.js +103 -0
- package/dist/seed/catalogue-seed-data.js +12 -12
- package/dist/seed/categories-seed-data.js +177 -162
- package/dist/seed/events-seed-data.js +61 -10
- package/dist/seed/homepage-sections-seed-data.js +5 -1
- package/dist/seed/offers-seed-data.js +32 -27
- package/dist/seed/products-art-seed-data.js +50 -12
- package/dist/seed/products-auctions-seed-data.js +97 -18
- package/dist/seed/products-classifieds-seed-data.js +80 -16
- package/dist/seed/products-digital-codes-seed-data.js +66 -18
- package/dist/seed/products-live-items-seed-data.js +64 -13
- package/dist/seed/products-preorders-seed-data.d.ts +0 -1
- package/dist/seed/products-preorders-seed-data.js +52 -13
- package/dist/seed/products-prize-draws-seed-data.js +63 -14
- package/dist/seed/products-standard-seed-data.js +99 -26
- package/dist/seed/products-stickers-seed-data.js +47 -12
- package/dist/seed/reviews-seed-data.js +6 -1
- package/dist/seed/site-settings-seed-data.d.ts +1 -38
- package/dist/seed/site-settings-seed-data.js +38 -76
- package/dist/seed/stores-seed-data.js +71 -1
- package/dist/seed/users-seed-data.js +15 -5
- package/dist/server-entry.d.ts +1 -2
- package/dist/server-entry.js +4 -3
- package/dist/server.d.ts +4 -3
- package/dist/server.js +8 -4
- package/dist/styles.css +78 -12
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Button.style.css +19 -4
- package/dist/ui/components/CollapsibleSection.d.ts +17 -1
- package/dist/ui/components/CollapsibleSection.js +6 -2
- package/dist/ui/components/FilterChipGroup.d.ts +25 -1
- package/dist/ui/components/FilterChipGroup.js +21 -4
- package/dist/ui/components/HandMode.style.css +47 -0
- package/dist/ui/components/Layout.d.ts +9 -2
- package/dist/ui/components/Layout.js +1 -1
- package/dist/ui/components/ListingLayout.js +8 -8
- package/dist/ui/components/ListingLayout.style.css +8 -7
- package/dist/ui/components/StickyToolbar.d.ts +3 -1
- package/dist/ui/components/StickyToolbar.js +62 -20
- package/dist/ui/components/bottom-chrome-slot.d.ts +21 -0
- package/dist/ui/components/bottom-chrome-slot.js +29 -0
- package/dist/ui/components/index.style.css +3 -0
- package/dist/ui/forms/FormShell.d.ts +43 -13
- package/dist/ui/forms/FormShell.js +22 -11
- package/dist/ui/forms/index.d.ts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document fields with a proven `useSiteSettings()` reader. Every entry below
|
|
3
|
+
* was traced to a call site; anything without one belongs in the private list,
|
|
4
|
+
* because an unused public field is pure attack surface.
|
|
5
|
+
*/
|
|
6
|
+
export const PUBLIC_SITE_SETTINGS_FIELDS = [
|
|
7
|
+
"contact", // user/orders/[id]/payment — contact.whatsappNumber (narrowed: no address/upiVpa)
|
|
8
|
+
"payment", // CheckoutRouteClient — payment.otpCheckoutThreshold (narrowed)
|
|
9
|
+
"featureFlags", // useListingTypeFlags — featureFlags.listingTypes (narrowed: no adminCheckoutBypass)
|
|
10
|
+
"notificationChannels", // NotificationPreferencesPanel + CheckoutRouteClient (whatsapp.otpEnabled)
|
|
11
|
+
"announcementBar", // AdminSiteView; also the homepage banner
|
|
12
|
+
"navConfig", // NavbarWithSettings, DashboardLayoutClient
|
|
13
|
+
"actionConfig", // useAuthGate
|
|
14
|
+
"background", // DashboardLayoutClient (background.light / .dark)
|
|
15
|
+
"watermark", // MediaVideo, as the fallback behind effectiveWatermark
|
|
16
|
+
];
|
|
17
|
+
/** Everything else, with the reason it stays server-side. */
|
|
18
|
+
export const PRIVATE_SITE_SETTINGS_FIELDS = [
|
|
19
|
+
"id", // singleton bookkeeping
|
|
20
|
+
"createdAt", // bookkeeping
|
|
21
|
+
"updatedAt", // bookkeeping
|
|
22
|
+
"credentials", // encrypted provider secrets — never leaves the server in any form
|
|
23
|
+
"emailSettings", // internal sender config + staff digest recipient addresses
|
|
24
|
+
"legalPages", // served by their own pages; large HTML blobs with no client reader
|
|
25
|
+
"commissions", // internal economics; the buyer-facing subset ships via toBuyerFacingFees
|
|
26
|
+
"emi", // ditto — surchargeSellerSharePercent is a platform/seller revenue split
|
|
27
|
+
"gst", // GSTIN, registered legal name and address
|
|
28
|
+
"laborRate", // internal procurement cost basis
|
|
29
|
+
"adSettings", // providerCredentials + unpublished ad inventory; /api/ads serves the filtered public view
|
|
30
|
+
"integrations", // analytics/tag IDs are injected server-side into markup, not fetched by the client
|
|
31
|
+
"platformLimits", // server-enforced caps; no client reader
|
|
32
|
+
"auctionConfig", // read by AuctionDetailPageView, a Server Component doing its own settings read
|
|
33
|
+
"disabledRoutes", // read by app/[locale]/layout.tsx (RSC) to gate routing
|
|
34
|
+
"siteName", // delivered as an SSR prop / rendered server-side
|
|
35
|
+
"tagline", // admin chrome only
|
|
36
|
+
"motto", // rendered server-side
|
|
37
|
+
"logo", // delivered as an SSR prop by app/[locale]/layout.tsx
|
|
38
|
+
"favicon", // emitted by generateMetadata, server-side
|
|
39
|
+
"seo", // consumed by generateMetadata, server-side
|
|
40
|
+
"socialLinks", // rendered server-side in the footer
|
|
41
|
+
"features", // rendered server-side
|
|
42
|
+
"shipping", // rendered server-side
|
|
43
|
+
"returns", // rendered server-side
|
|
44
|
+
"faq", // faq.variables are interpolated server-side by /api/faqs
|
|
45
|
+
"aboutContent", // rendered by the /about RSC
|
|
46
|
+
"navbarConfig", // rendered server-side
|
|
47
|
+
"footerConfig", // rendered server-side
|
|
48
|
+
"theme", // delivered as an SSR prop to ThemeProvider by app/[locale]/layout.tsx
|
|
49
|
+
"featuredResults", // rendered server-side by search
|
|
50
|
+
];
|
|
51
|
+
export function toPublicSiteSettings(doc, opts) {
|
|
52
|
+
return {
|
|
53
|
+
// Explicit sub-literals, never spreads. These two are the ones that carry
|
|
54
|
+
// real weight: `contact` drops `address` + `upiVpa`, and `featureFlags`
|
|
55
|
+
// drops `adminCheckoutBypass` — the flag that turns off payment and OTP.
|
|
56
|
+
contact: {
|
|
57
|
+
email: doc.contact?.email ?? "",
|
|
58
|
+
phone: doc.contact?.phone ?? "",
|
|
59
|
+
whatsappNumber: doc.contact?.whatsappNumber ?? "",
|
|
60
|
+
},
|
|
61
|
+
payment: {
|
|
62
|
+
otpCheckoutThreshold: doc.payment?.otpCheckoutThreshold,
|
|
63
|
+
},
|
|
64
|
+
featureFlags: {
|
|
65
|
+
listingTypes: doc.featureFlags?.listingTypes,
|
|
66
|
+
},
|
|
67
|
+
notificationChannels: doc.notificationChannels,
|
|
68
|
+
announcementBar: doc.announcementBar,
|
|
69
|
+
navConfig: doc.navConfig,
|
|
70
|
+
actionConfig: doc.actionConfig,
|
|
71
|
+
background: doc.background,
|
|
72
|
+
watermark: doc.watermark,
|
|
73
|
+
effectiveWatermark: opts?.effectiveWatermark,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { toPublicSiteSettings, PUBLIC_SITE_SETTINGS_FIELDS, PRIVATE_SITE_SETTINGS_FIELDS, type ToPublicSiteSettingsOptions, } from "./adapters";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { toPublicSiteSettings, PUBLIC_SITE_SETTINGS_FIELDS, PRIVATE_SITE_SETTINGS_FIELDS, } from "./adapters";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store adapters — the single allow-list projection for every public store
|
|
3
|
+
* response, HTTP or SSR.
|
|
4
|
+
*
|
|
5
|
+
* Every field of `StoreDocument` is triaged into exactly one of
|
|
6
|
+
* `PUBLIC_STORE_FIELDS` (mapped into the returned literal) or
|
|
7
|
+
* `PRIVATE_STORE_FIELDS` (with a stated reason). A field added to the schema
|
|
8
|
+
* and to neither list fails `scripts/audit-public-projection-parity.mjs`, so
|
|
9
|
+
* new fields are private by default.
|
|
10
|
+
*
|
|
11
|
+
* Why this exists (2026-08-24): `src/app/[locale]/stores/[storeSlug]/about/
|
|
12
|
+
* page.tsx` passed a raw `StoreDocument` into a `"use client"` component via
|
|
13
|
+
* `as unknown as StoreDetail` — a cast with no runtime effect — so Next.js
|
|
14
|
+
* serialised the whole document into the RSC flight payload embedded in that
|
|
15
|
+
* page's PUBLIC HTML, including the Meta WhatsApp Business `accessToken`
|
|
16
|
+
* (decrypted on every read by `StoreRepository.mapDoc` at the time),
|
|
17
|
+
* `adminNotes`, `suspensionReason` and `customCommissionRate`. The public
|
|
18
|
+
* store *APIs* were clean only because each had hand-rolled its own literal;
|
|
19
|
+
* there were three such copies and the SSR page had none. This module is the
|
|
20
|
+
* one copy they all now share.
|
|
21
|
+
*/
|
|
22
|
+
import type { StoreDocument } from "../../../../features/stores/schemas/firestore";
|
|
23
|
+
import type { StoreDetail, StoreListItem } from "../../../../features/stores/types/index";
|
|
24
|
+
/**
|
|
25
|
+
* Document fields that may reach an unauthenticated client. Keep in sync with
|
|
26
|
+
* the object literals below — the audit asserts both directions.
|
|
27
|
+
*/
|
|
28
|
+
export declare const PUBLIC_STORE_FIELDS: readonly ["id", "storeSlug", "ownerId", "storeName", "storeDescription", "storeCategory", "storeLogoURL", "storeBannerURL", "status", "isPublic", "stats", "bio", "location", "website", "socialLinks", "returnPolicy", "shippingPolicy", "isVacationMode", "vacationMessage", "createdAt"];
|
|
29
|
+
/** Everything else, with the reason it stays server-side. */
|
|
30
|
+
export declare const PRIVATE_STORE_FIELDS: readonly ["updatedAt", "whatsappConfig", "adminNotes", "isVerified", "suspensionReason", "capabilities", "customCommissionRate", "googleReviews", "shippingConfig", "emiEnabled", "isFeatured", "vacationReturnDate", "isTestData", "testDataExpiresAt"];
|
|
31
|
+
/**
|
|
32
|
+
* Accepted input: a `StoreDocument` (the common case) or a loosely-typed
|
|
33
|
+
* Firestore row. The legacy top-level counters below predate `stats` and are
|
|
34
|
+
* still present on older documents, so they stay as declared fallbacks rather
|
|
35
|
+
* than an index signature — `Record<string, unknown>` would not accept a
|
|
36
|
+
* plain `StoreDocument`.
|
|
37
|
+
*/
|
|
38
|
+
export type StoreProjectionSource = Partial<StoreDocument> & {
|
|
39
|
+
totalProducts?: number;
|
|
40
|
+
itemsSold?: number;
|
|
41
|
+
totalReviews?: number;
|
|
42
|
+
averageRating?: number;
|
|
43
|
+
};
|
|
44
|
+
export interface ToStoreListItemOptions {
|
|
45
|
+
/** Reserved for future per-caller shaping (see CLAUDE.md § Encapsulation + Override Contract). */
|
|
46
|
+
readonly _reserved?: never;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Grid/list projection. Stats are flattened off `stats`, falling back to the
|
|
50
|
+
* legacy top-level counters some older documents still carry.
|
|
51
|
+
*/
|
|
52
|
+
export declare function toStoreListItem(doc: StoreProjectionSource, _opts?: ToStoreListItemOptions): StoreListItem;
|
|
53
|
+
export interface ToStoreDetailOptions extends ToStoreListItemOptions {
|
|
54
|
+
}
|
|
55
|
+
/** Full public store profile — everything `StoreAboutView` renders, nothing more. */
|
|
56
|
+
export declare function toStoreDetail(doc: StoreProjectionSource, opts?: ToStoreDetailOptions): StoreDetail;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document fields that may reach an unauthenticated client. Keep in sync with
|
|
3
|
+
* the object literals below — the audit asserts both directions.
|
|
4
|
+
*/
|
|
5
|
+
export const PUBLIC_STORE_FIELDS = [
|
|
6
|
+
"id",
|
|
7
|
+
"storeSlug",
|
|
8
|
+
"ownerId",
|
|
9
|
+
"storeName",
|
|
10
|
+
"storeDescription",
|
|
11
|
+
"storeCategory",
|
|
12
|
+
"storeLogoURL",
|
|
13
|
+
"storeBannerURL",
|
|
14
|
+
"status",
|
|
15
|
+
"isPublic",
|
|
16
|
+
"stats",
|
|
17
|
+
"bio",
|
|
18
|
+
"location",
|
|
19
|
+
"website",
|
|
20
|
+
"socialLinks",
|
|
21
|
+
"returnPolicy",
|
|
22
|
+
"shippingPolicy",
|
|
23
|
+
"isVacationMode",
|
|
24
|
+
"vacationMessage",
|
|
25
|
+
"createdAt",
|
|
26
|
+
];
|
|
27
|
+
/** Everything else, with the reason it stays server-side. */
|
|
28
|
+
export const PRIVATE_STORE_FIELDS = [
|
|
29
|
+
"updatedAt", // bookkeeping; no public reader
|
|
30
|
+
"whatsappConfig", // Meta access token + WABA/catalog ids — secret
|
|
31
|
+
"adminNotes", // schema comment: never shown to the owner or the public
|
|
32
|
+
"isVerified", // admin moderation state; surfaced via a separate badge feed if ever needed
|
|
33
|
+
"suspensionReason", // admin moderation note
|
|
34
|
+
"capabilities", // internal feature gating; server-enforced, never client-read
|
|
35
|
+
"customCommissionRate", // commercial terms for this seller
|
|
36
|
+
"googleReviews", // placeId is billed-API config; the RSC reads it server-side
|
|
37
|
+
"shippingConfig", // resolved server-side into per-item shipping at cart time
|
|
38
|
+
"emiEnabled", // read server-side by the checkout/product RSCs, never from a public payload
|
|
39
|
+
"isFeatured", // ranking input; not part of the public store profile
|
|
40
|
+
"vacationReturnDate", // not rendered by StoreAboutView; add to PUBLIC when a reader exists
|
|
41
|
+
"isTestData", // tester-sandbox tagging; filtered by filterTestDataForViewer
|
|
42
|
+
"testDataExpiresAt", // ditto
|
|
43
|
+
];
|
|
44
|
+
/** `createdAt` is `Date` on the document and `string` on the client types. */
|
|
45
|
+
function toIsoString(value) {
|
|
46
|
+
if (!value)
|
|
47
|
+
return undefined;
|
|
48
|
+
if (value instanceof Date)
|
|
49
|
+
return value.toISOString();
|
|
50
|
+
if (typeof value === "string")
|
|
51
|
+
return value;
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Grid/list projection. Stats are flattened off `stats`, falling back to the
|
|
56
|
+
* legacy top-level counters some older documents still carry.
|
|
57
|
+
*/
|
|
58
|
+
export function toStoreListItem(doc, _opts) {
|
|
59
|
+
const stats = doc.stats;
|
|
60
|
+
return {
|
|
61
|
+
id: doc.id,
|
|
62
|
+
storeSlug: doc.storeSlug,
|
|
63
|
+
ownerId: doc.ownerId,
|
|
64
|
+
storeName: doc.storeName,
|
|
65
|
+
storeDescription: doc.storeDescription,
|
|
66
|
+
storeCategory: doc.storeCategory,
|
|
67
|
+
storeLogoURL: doc.storeLogoURL,
|
|
68
|
+
storeBannerURL: doc.storeBannerURL,
|
|
69
|
+
status: doc.status,
|
|
70
|
+
isPublic: doc.isPublic,
|
|
71
|
+
totalProducts: stats?.totalProducts ?? doc.totalProducts,
|
|
72
|
+
itemsSold: stats?.itemsSold ?? doc.itemsSold,
|
|
73
|
+
totalReviews: stats?.totalReviews ?? doc.totalReviews,
|
|
74
|
+
averageRating: stats?.averageRating ?? doc.averageRating,
|
|
75
|
+
createdAt: toIsoString(doc.createdAt),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** Full public store profile — everything `StoreAboutView` renders, nothing more. */
|
|
79
|
+
export function toStoreDetail(doc, opts) {
|
|
80
|
+
return {
|
|
81
|
+
...toStoreListItem(doc, opts),
|
|
82
|
+
bio: doc.bio,
|
|
83
|
+
location: doc.location,
|
|
84
|
+
website: doc.website,
|
|
85
|
+
socialLinks: doc.socialLinks,
|
|
86
|
+
returnPolicy: doc.returnPolicy,
|
|
87
|
+
shippingPolicy: doc.shippingPolicy,
|
|
88
|
+
isVacationMode: doc.isVacationMode,
|
|
89
|
+
vacationMessage: doc.vacationMessage,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { getStoreForDetail, listStoreProductsInitial, listStoreAuctionsInitial, listStorePreOrdersInitial, listSitemapStores, } from "./data";
|
|
2
2
|
export { STORES_PAGE_SIZE, STORES_PRODUCTS_PAGE_SIZE, STORES_SITEMAP_LIMIT, STORES_FEATURED_LIMIT, } from "../../../shared/features/stores/config";
|
|
3
3
|
export { renderStoreOgImage, renderStoreOg, type StoreOgData } from "./og";
|
|
4
|
+
export { toStoreDetail, toStoreListItem, PUBLIC_STORE_FIELDS, PRIVATE_STORE_FIELDS, type ToStoreDetailOptions, type ToStoreListItemOptions, type StoreProjectionSource, } from "./adapters";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { getStoreForDetail, listStoreProductsInitial, listStoreAuctionsInitial, listStorePreOrdersInitial, listSitemapStores, } from "./data";
|
|
2
2
|
export { STORES_PAGE_SIZE, STORES_PRODUCTS_PAGE_SIZE, STORES_SITEMAP_LIMIT, STORES_FEATURED_LIMIT, } from "../../../shared/features/stores/config";
|
|
3
3
|
export { renderStoreOgImage, renderStoreOg } from "./og";
|
|
4
|
+
export { toStoreDetail, toStoreListItem, PUBLIC_STORE_FIELDS, PRIVATE_STORE_FIELDS, } from "./adapters";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { bidRepository, cartRepository, productRepository, storeRepository, } from "../../../../repositories";
|
|
2
2
|
import { sendNotification } from "../../../../features/admin/actions/notification-actions";
|
|
3
|
-
import {
|
|
3
|
+
import { normalizeError } from "../../../../errors/normalize";
|
|
4
|
+
import { AUCTION_MESSAGES, BID_MESSAGES } from "../handlers/messages";
|
|
4
5
|
import { ROUTES } from "../../../../next/routing/route-map";
|
|
5
6
|
import { CART_LANE, LANE_CHECKOUT_WINDOW_MS } from "../../../shared/checkout/lanes";
|
|
6
7
|
/**
|
|
@@ -13,8 +14,61 @@ import { CART_LANE, LANE_CHECKOUT_WINDOW_MS } from "../../../shared/checkout/lan
|
|
|
13
14
|
const AUCTION_CHECKOUT_WINDOW_MS = LANE_CHECKOUT_WINDOW_MS.auction;
|
|
14
15
|
/** Deep link the winner straight into the auction checkout lane. */
|
|
15
16
|
const AUCTION_CHECKOUT_URL = `${String(ROUTES.USER.CHECKOUT)}?lane=${CART_LANE.AUCTION}`;
|
|
17
|
+
/**
|
|
18
|
+
* Clear every unpaid Buy Now hold on an auction whose clock just ran out.
|
|
19
|
+
*
|
|
20
|
+
* This is the user's explicit rule: "if by the time the checkout is not
|
|
21
|
+
* finished but auction timeout ends the buyout should fail as well." The item
|
|
22
|
+
* goes to whoever actually won the bidding, and the claimants get their carts
|
|
23
|
+
* back — the line is `locked: true`, so they cannot clear it themselves.
|
|
24
|
+
*
|
|
25
|
+
* `cancelled`, never `forfeited`: forfeiture means "won, then defaulted" and
|
|
26
|
+
* carries a warning about account restrictions. Nobody won anything here, and
|
|
27
|
+
* losing a race against a clock is not misconduct.
|
|
28
|
+
*
|
|
29
|
+
* Best-effort per hold — one failure must not abort settling the auction.
|
|
30
|
+
*/
|
|
31
|
+
async function releasePendingBuyouts(ctx, product, holds) {
|
|
32
|
+
for (const hold of holds) {
|
|
33
|
+
try {
|
|
34
|
+
await cartRepository.removeItemsByBidId(hold.data.userId, hold.id);
|
|
35
|
+
await bidRepository.markCancelled(hold.id);
|
|
36
|
+
await sendNotification({
|
|
37
|
+
userId: hold.data.userId,
|
|
38
|
+
type: "auction_ended",
|
|
39
|
+
priority: "high",
|
|
40
|
+
title: BID_MESSAGES.BUY_NOW_AUCTION_ENDED_TITLE,
|
|
41
|
+
message: BID_MESSAGES.BUY_NOW_AUCTION_ENDED_MESSAGE(product.title),
|
|
42
|
+
relatedId: product.id,
|
|
43
|
+
relatedType: "product",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
void normalizeError(err);
|
|
48
|
+
ctx.logger.warn(`Failed to release buyout hold ${hold.id}`, {
|
|
49
|
+
error: err instanceof Error ? err.message : String(err),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
ctx.logger.info(`Released ${holds.length} unpaid buyout hold(s) on ${product.id}`);
|
|
54
|
+
}
|
|
16
55
|
async function settleAuction(ctx, product) {
|
|
17
|
-
const
|
|
56
|
+
const allActive = await bidRepository.getActiveByProduct(product.id);
|
|
57
|
+
/**
|
|
58
|
+
* A pending Buy Now claim is `active` and sits at the BIN price, so it would
|
|
59
|
+
* be `activeBids[0]` and win by default the instant the clock ran out —
|
|
60
|
+
* handing the item to somebody who explicitly did NOT complete their purchase
|
|
61
|
+
* in time. A buyout only counts once it has been paid for, at which point
|
|
62
|
+
* `claimAuctionForCheckout` has already marked it `won` and flipped the
|
|
63
|
+
* product to `isSold`, so this job never sees that auction at all.
|
|
64
|
+
*
|
|
65
|
+
* The stale holds are released below, after the real winner is decided.
|
|
66
|
+
*/
|
|
67
|
+
const pendingBuyouts = allActive.filter((e) => e.data.isBuyout === true);
|
|
68
|
+
const activeBids = allActive.filter((e) => e.data.isBuyout !== true);
|
|
69
|
+
if (pendingBuyouts.length > 0) {
|
|
70
|
+
await releasePendingBuyouts(ctx, product, pendingBuyouts);
|
|
71
|
+
}
|
|
18
72
|
if (activeBids.length === 0) {
|
|
19
73
|
const batch = ctx.db.batch();
|
|
20
74
|
productRepository.updateStatusInBatch(batch, product.id, "archived");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { normalizeError } from "../../../../errors/normalize";
|
|
2
2
|
import { bidRepository, cartRepository, offerRepository, storeRepository } from "../../../../repositories";
|
|
3
3
|
import { sendNotification } from "../../../../features/admin/actions/notification-actions";
|
|
4
|
+
import { BID_MESSAGES } from "../handlers/messages";
|
|
4
5
|
export async function runOfferExpiry(ctx) {
|
|
5
6
|
ctx.logger.info("Starting offer expiry sweep");
|
|
6
7
|
let expiredOffers;
|
|
@@ -116,16 +117,25 @@ async function expireAcceptedPastCheckoutDeadline(ctx) {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
/**
|
|
119
|
-
*
|
|
120
|
+
* An auction line the buyer never paid for. TWO different situations, and the
|
|
121
|
+
* difference matters to both parties.
|
|
120
122
|
*
|
|
121
|
-
* Folded into this job rather than given its own scheduled function:
|
|
122
|
-
* "a locked cart line whose claim has lapsed", they want the same
|
|
123
|
-
* Cloud Scheduler bills per registered job (see CLAUDE.md's
|
|
124
|
-
* table — 27 jobs is already an accepted, documented cost).
|
|
123
|
+
* Folded into this job rather than given its own scheduled function: all of
|
|
124
|
+
* these are "a locked cart line whose claim has lapsed", they want the same
|
|
125
|
+
* cadence, and Cloud Scheduler bills per registered job (see CLAUDE.md's
|
|
126
|
+
* Firebase budget table — 27 jobs is already an accepted, documented cost).
|
|
125
127
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
128
|
+
* **A settled win** (`!isBuyout`) is a finished auction the buyer committed to
|
|
129
|
+
* by bidding. The listing is NOT silently relisted — that is a seller decision
|
|
130
|
+
* (relist, approach the runner-up, or ban the non-payer) — so the line is
|
|
131
|
+
* cleared, the bid is `forfeited`, and both sides are told.
|
|
132
|
+
*
|
|
133
|
+
* **A buyout hold** (`isBuyout`) never won anything. `buyNowAuction` left the
|
|
134
|
+
* auction completely untouched, so there is no sale to reverse and nothing for
|
|
135
|
+
* the seller to decide: the line is cleared, the bid is `cancelled`, and only
|
|
136
|
+
* the buyer hears about it. This is what makes flipping nothing on the product
|
|
137
|
+
* at claim time pay off — the old buy-now marked the listing sold immediately,
|
|
138
|
+
* so an abandoned buyout permanently killed an auction that still had time left.
|
|
129
139
|
*/
|
|
130
140
|
async function lapseUnpaidAuctionWins(ctx) {
|
|
131
141
|
let stale;
|
|
@@ -137,7 +147,33 @@ async function lapseUnpaidAuctionWins(ctx) {
|
|
|
137
147
|
ctx.logger.error("Failed to scan for expired locked cart lines", err);
|
|
138
148
|
return;
|
|
139
149
|
}
|
|
140
|
-
const
|
|
150
|
+
const allAuctionLines = stale.filter(({ item }) => item.bidId && item.isAuctionWin);
|
|
151
|
+
const buyoutHolds = allAuctionLines.filter(({ item }) => item.isBuyout === true);
|
|
152
|
+
const auctionLines = allAuctionLines.filter(({ item }) => item.isBuyout !== true);
|
|
153
|
+
if (buyoutHolds.length > 0) {
|
|
154
|
+
ctx.logger.info(`Found ${buyoutHolds.length} expired Buy Now hold(s)`);
|
|
155
|
+
for (const { userId, item } of buyoutHolds) {
|
|
156
|
+
try {
|
|
157
|
+
await cartRepository.removeItemsByBidId(userId, item.bidId);
|
|
158
|
+
await bidRepository.markCancelled(item.bidId);
|
|
159
|
+
await sendNotification({
|
|
160
|
+
userId,
|
|
161
|
+
type: "auction_ended",
|
|
162
|
+
priority: "normal",
|
|
163
|
+
title: BID_MESSAGES.BUY_NOW_LAPSED_TITLE,
|
|
164
|
+
message: BID_MESSAGES.BUY_NOW_LAPSED_MESSAGE(item.productTitle),
|
|
165
|
+
relatedId: item.auctionId ?? item.productId,
|
|
166
|
+
relatedType: "product",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
void normalizeError(err);
|
|
171
|
+
ctx.logger.warn(`Failed to lapse buyout hold ${item.bidId}`, {
|
|
172
|
+
error: err instanceof Error ? err.message : String(err),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
141
177
|
if (auctionLines.length === 0)
|
|
142
178
|
return;
|
|
143
179
|
ctx.logger.info(`Found ${auctionLines.length} unpaid auction win(s) past deadline`);
|
|
@@ -6,5 +6,10 @@ import type { JobContext } from "../runtime/types";
|
|
|
6
6
|
* effect, plus `autoApproved`/`autoApprovedAt` so it's visibly distinguishable
|
|
7
7
|
* from a manually-reviewed order. Buyer/seller can raise a dispute afterward
|
|
8
8
|
* via `raiseOrderDisputeAction` — this sweep never reverses itself.
|
|
9
|
+
*
|
|
10
|
+
* Buyout orders are held back to 24 hours (see `BUYOUT_AUTO_APPROVE_HOURS`).
|
|
11
|
+
* The QUERY still runs at the 2-hour cutoff and the longer window is applied
|
|
12
|
+
* in-memory, so this keeps one query on one existing composite index rather
|
|
13
|
+
* than needing a second scan on a second cutoff.
|
|
9
14
|
*/
|
|
10
15
|
export declare function runPaymentReviewAutoApprove(ctx: JobContext): Promise<void>;
|
|
@@ -3,6 +3,18 @@ import { sendNotification } from "../../../../features/admin/actions/notificatio
|
|
|
3
3
|
import { normalizeError } from "../../../../errors/normalize";
|
|
4
4
|
import { ORDER_MESSAGES } from "../handlers/messages";
|
|
5
5
|
const AUTO_APPROVE_HOURS = 2;
|
|
6
|
+
/**
|
|
7
|
+
* Buyout orders get a full day of admin review instead of two hours.
|
|
8
|
+
*
|
|
9
|
+
* A Buy Now purchase is settled in a one-hour scramble against a still-live
|
|
10
|
+
* auction, and the amounts involved are whole-listing rather than incremental —
|
|
11
|
+
* so an admin should get a proper working day to look at the proof before the
|
|
12
|
+
* safety net fires, rather than a window that can elapse overnight.
|
|
13
|
+
*/
|
|
14
|
+
const BUYOUT_AUTO_APPROVE_HOURS = 24;
|
|
15
|
+
function autoApproveHoursFor(order) {
|
|
16
|
+
return order.isBuyout === true ? BUYOUT_AUTO_APPROVE_HOURS : AUTO_APPROVE_HOURS;
|
|
17
|
+
}
|
|
6
18
|
/**
|
|
7
19
|
* 2-hour auto-approve safety net — a manual-payment proof nobody has acted
|
|
8
20
|
* on (no admin approve / re-upload-request / fraud-reject) within 2 hours of
|
|
@@ -10,10 +22,30 @@ const AUTO_APPROVE_HOURS = 2;
|
|
|
10
22
|
* effect, plus `autoApproved`/`autoApprovedAt` so it's visibly distinguishable
|
|
11
23
|
* from a manually-reviewed order. Buyer/seller can raise a dispute afterward
|
|
12
24
|
* via `raiseOrderDisputeAction` — this sweep never reverses itself.
|
|
25
|
+
*
|
|
26
|
+
* Buyout orders are held back to 24 hours (see `BUYOUT_AUTO_APPROVE_HOURS`).
|
|
27
|
+
* The QUERY still runs at the 2-hour cutoff and the longer window is applied
|
|
28
|
+
* in-memory, so this keeps one query on one existing composite index rather
|
|
29
|
+
* than needing a second scan on a second cutoff.
|
|
13
30
|
*/
|
|
14
31
|
export async function runPaymentReviewAutoApprove(ctx) {
|
|
15
32
|
ctx.logger.info(`Scanning unreviewed payment proofs > ${AUTO_APPROVE_HOURS}h old`);
|
|
16
|
-
const
|
|
33
|
+
const scanned = await orderRepository.getUnreviewedProofPastDeadline(AUTO_APPROVE_HOURS);
|
|
34
|
+
const unreviewed = scanned.filter((entry) => {
|
|
35
|
+
const hours = autoApproveHoursFor(entry.data);
|
|
36
|
+
if (hours === AUTO_APPROVE_HOURS)
|
|
37
|
+
return true;
|
|
38
|
+
const uploadedAt = entry.data.paymentProofUploadedAt
|
|
39
|
+
? new Date(entry.data.paymentProofUploadedAt)
|
|
40
|
+
: null;
|
|
41
|
+
if (!uploadedAt)
|
|
42
|
+
return false;
|
|
43
|
+
return ctx.now.getTime() - uploadedAt.getTime() >= hours * 60 * 60 * 1000;
|
|
44
|
+
});
|
|
45
|
+
const held = scanned.length - unreviewed.length;
|
|
46
|
+
if (held > 0) {
|
|
47
|
+
ctx.logger.info(`Holding ${held} buyout order(s) for the ${BUYOUT_AUTO_APPROVE_HOURS}h admin window`);
|
|
48
|
+
}
|
|
17
49
|
if (unreviewed.length === 0) {
|
|
18
50
|
ctx.logger.info("No unreviewed payment proofs past the auto-approve window");
|
|
19
51
|
return;
|
|
@@ -19,6 +19,13 @@ export declare const AUCTION_MESSAGES: {
|
|
|
19
19
|
export declare const BID_MESSAGES: {
|
|
20
20
|
OUTBID_TITLE: string;
|
|
21
21
|
OUTBID_MESSAGE: (productTitle: string, currency: string, currentBid: number) => string;
|
|
22
|
+
BUY_NOW_HELD_TITLE: string;
|
|
23
|
+
BUY_NOW_HELD_MESSAGE: (productTitle: string, currency: string, amount: number, minutes: number) => string;
|
|
24
|
+
BUY_NOW_HELD_ACTION: string;
|
|
25
|
+
BUY_NOW_LAPSED_TITLE: string;
|
|
26
|
+
BUY_NOW_LAPSED_MESSAGE: (productTitle: string) => string;
|
|
27
|
+
BUY_NOW_AUCTION_ENDED_TITLE: string;
|
|
28
|
+
BUY_NOW_AUCTION_ENDED_MESSAGE: (productTitle: string) => string;
|
|
22
29
|
};
|
|
23
30
|
export declare const ORDER_MESSAGES: {
|
|
24
31
|
CANCELLED_TITLE: string;
|
|
@@ -19,6 +19,16 @@ export const AUCTION_MESSAGES = {
|
|
|
19
19
|
export const BID_MESSAGES = {
|
|
20
20
|
OUTBID_TITLE: "You've been outbid",
|
|
21
21
|
OUTBID_MESSAGE: (productTitle, currency, currentBid) => `Someone placed a higher bid on "${productTitle}". Current bid: ${currency} ${currentBid}.`,
|
|
22
|
+
// Buy Now places a HOLD, not a purchase — the auction stays live and other
|
|
23
|
+
// buyers can still bid or buy it out, so the copy has to convey urgency
|
|
24
|
+
// without claiming the item is already theirs.
|
|
25
|
+
BUY_NOW_HELD_TITLE: "Complete your Buy Now purchase",
|
|
26
|
+
BUY_NOW_HELD_MESSAGE: (productTitle, currency, amount, minutes) => `You've claimed "${productTitle}" at the Buy Now price of ${currency} ${amount}. The auction is still live, so it's yours only once you've paid — complete checkout within ${minutes} minutes.`,
|
|
27
|
+
BUY_NOW_HELD_ACTION: "Pay now",
|
|
28
|
+
BUY_NOW_LAPSED_TITLE: "Buy Now claim expired",
|
|
29
|
+
BUY_NOW_LAPSED_MESSAGE: (productTitle) => `Your Buy Now claim on "${productTitle}" expired before payment was completed, so it's been released. The auction is still running if you'd like to bid.`,
|
|
30
|
+
BUY_NOW_AUCTION_ENDED_TITLE: "Buy Now claim released",
|
|
31
|
+
BUY_NOW_AUCTION_ENDED_MESSAGE: (productTitle) => `The auction for "${productTitle}" ended before you completed your Buy Now purchase, so your claim was released and the item went to the winning bidder.`,
|
|
22
32
|
};
|
|
23
33
|
export const ORDER_MESSAGES = {
|
|
24
34
|
CANCELLED_TITLE: "Order cancelled",
|
|
@@ -134,7 +134,7 @@ export const ACTIONS = {
|
|
|
134
134
|
"buy-it-now": {
|
|
135
135
|
id: "auction.buy-it-now",
|
|
136
136
|
label: "Buy It Now",
|
|
137
|
-
description: "
|
|
137
|
+
description: "Claim the listing at the buy-now price (SB-UNI-H). Offered while the auction is unsold, before its end date, and while the buy-now price still beats the standing bid. Places a bid at that price plus a 1h locked cart line — the auction stays live until the buyer pays.",
|
|
138
138
|
kind: "secondary",
|
|
139
139
|
listingTypeScope: ["auction"],
|
|
140
140
|
},
|
|
@@ -1876,6 +1876,20 @@ export const ACTIONS = {
|
|
|
1876
1876
|
confirmKind: "danger",
|
|
1877
1877
|
},
|
|
1878
1878
|
},
|
|
1879
|
+
"cancel-offer": {
|
|
1880
|
+
id: "admin.cancel-offer",
|
|
1881
|
+
label: "Cancel Offer",
|
|
1882
|
+
ariaLabel: "Cancel this offer",
|
|
1883
|
+
description: "Expire the offer, clear it from the buyer's cart, and notify them. Admin cannot accept or counter on a seller's behalf.",
|
|
1884
|
+
kind: "danger",
|
|
1885
|
+
permissions: ["admin", "moderator"],
|
|
1886
|
+
confirmation: {
|
|
1887
|
+
title: "Cancel this offer?",
|
|
1888
|
+
body: "The offer will be expired and removed from the buyer's cart, and they'll be notified. This cannot be undone — the buyer would need to make a new offer.",
|
|
1889
|
+
confirmLabel: "Cancel offer",
|
|
1890
|
+
confirmKind: "danger",
|
|
1891
|
+
},
|
|
1892
|
+
},
|
|
1879
1893
|
// ── Address ban management ─────────────────────────────────────────────
|
|
1880
1894
|
"ban-address": {
|
|
1881
1895
|
id: "admin.ban-address",
|
|
@@ -31,6 +31,31 @@ export declare const CART_LANE_LABELS: Record<CartLane, string>;
|
|
|
31
31
|
export declare const LANE_CHECKOUT_WINDOW_MS: Record<"auction" | "offer", number>;
|
|
32
32
|
/** 2 days, in hours — for buyer-facing copy ("pay within 48 hours"). */
|
|
33
33
|
export declare const LANE_CHECKOUT_WINDOW_HOURS: Record<"auction" | "offer", number>;
|
|
34
|
+
/**
|
|
35
|
+
* A BUYOUT hold is a third window, deliberately much shorter than the two above,
|
|
36
|
+
* even though it lands in the same auction lane.
|
|
37
|
+
*
|
|
38
|
+
* A settled win (`LANE_CHECKOUT_WINDOW_MS.auction`) is a finished auction: the
|
|
39
|
+
* clock already ran, nobody else is waiting, and 48 hours costs no one anything.
|
|
40
|
+
* A buyout is the opposite — the auction is still live and still taking bids
|
|
41
|
+
* while the hold exists, so a long window would let one buyer sit on a listing
|
|
42
|
+
* they may never pay for. One hour, and the hold is additionally clamped to the
|
|
43
|
+
* auction's own end time by `buyNowAuction` (see `AUCTION_BUYOUT_WINDOW_MS`
|
|
44
|
+
* callers): a buyout that hasn't been paid for by the time the auction ends
|
|
45
|
+
* fails, because by then the auction has settled to whoever actually won it.
|
|
46
|
+
*/
|
|
47
|
+
export declare const AUCTION_BUYOUT_WINDOW_MS: number;
|
|
48
|
+
/** 1 hour, in minutes — for buyer-facing copy ("pay within 60 minutes"). */
|
|
49
|
+
export declare const AUCTION_BUYOUT_WINDOW_MINUTES: number;
|
|
50
|
+
/**
|
|
51
|
+
* Deep link that drops a buyer straight into the auction lane at checkout.
|
|
52
|
+
*
|
|
53
|
+
* Declared once here, with the lane it selects, because it was previously a
|
|
54
|
+
* const in `auctionSettlement.ts` AND an inline template literal in
|
|
55
|
+
* `bid-actions.ts` — the same two-copies problem `LANE_CHECKOUT_WINDOW_MS` was
|
|
56
|
+
* extracted to fix, one layer up.
|
|
57
|
+
*/
|
|
58
|
+
export declare const AUCTION_CHECKOUT_URL: string;
|
|
34
59
|
/** The two lanes whose price is fixed outside the listing. */
|
|
35
60
|
export declare function isLockedLane(lane: CartLane): boolean;
|
|
36
61
|
/** Minimal shape this module needs — works on cart documents and client rows alike. */
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* @tag consumers:CartRouteClient,CheckoutRouteClient,cart-actions,order-splitter,checkout/actions
|
|
25
25
|
* @tag sideEffects:none
|
|
26
26
|
*/
|
|
27
|
+
import { ROUTES } from "../../../next/routing/route-map";
|
|
27
28
|
export const CART_LANE = {
|
|
28
29
|
AUCTION: "auction",
|
|
29
30
|
OFFER: "offer",
|
|
@@ -72,6 +73,31 @@ export const LANE_CHECKOUT_WINDOW_HOURS = {
|
|
|
72
73
|
auction: LANE_CHECKOUT_WINDOW_MS.auction / (60 * 60 * 1000),
|
|
73
74
|
offer: LANE_CHECKOUT_WINDOW_MS.offer / (60 * 60 * 1000),
|
|
74
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* A BUYOUT hold is a third window, deliberately much shorter than the two above,
|
|
78
|
+
* even though it lands in the same auction lane.
|
|
79
|
+
*
|
|
80
|
+
* A settled win (`LANE_CHECKOUT_WINDOW_MS.auction`) is a finished auction: the
|
|
81
|
+
* clock already ran, nobody else is waiting, and 48 hours costs no one anything.
|
|
82
|
+
* A buyout is the opposite — the auction is still live and still taking bids
|
|
83
|
+
* while the hold exists, so a long window would let one buyer sit on a listing
|
|
84
|
+
* they may never pay for. One hour, and the hold is additionally clamped to the
|
|
85
|
+
* auction's own end time by `buyNowAuction` (see `AUCTION_BUYOUT_WINDOW_MS`
|
|
86
|
+
* callers): a buyout that hasn't been paid for by the time the auction ends
|
|
87
|
+
* fails, because by then the auction has settled to whoever actually won it.
|
|
88
|
+
*/
|
|
89
|
+
export const AUCTION_BUYOUT_WINDOW_MS = 60 * 60 * 1000;
|
|
90
|
+
/** 1 hour, in minutes — for buyer-facing copy ("pay within 60 minutes"). */
|
|
91
|
+
export const AUCTION_BUYOUT_WINDOW_MINUTES = AUCTION_BUYOUT_WINDOW_MS / (60 * 1000);
|
|
92
|
+
/**
|
|
93
|
+
* Deep link that drops a buyer straight into the auction lane at checkout.
|
|
94
|
+
*
|
|
95
|
+
* Declared once here, with the lane it selects, because it was previously a
|
|
96
|
+
* const in `auctionSettlement.ts` AND an inline template literal in
|
|
97
|
+
* `bid-actions.ts` — the same two-copies problem `LANE_CHECKOUT_WINDOW_MS` was
|
|
98
|
+
* extracted to fix, one layer up.
|
|
99
|
+
*/
|
|
100
|
+
export const AUCTION_CHECKOUT_URL = `${String(ROUTES.USER.CHECKOUT)}?lane=${CART_LANE.AUCTION}`;
|
|
75
101
|
/** The two lanes whose price is fixed outside the listing. */
|
|
76
102
|
export function isLockedLane(lane) {
|
|
77
103
|
return lane === CART_LANE.AUCTION || lane === CART_LANE.OFFER;
|
|
@@ -49,6 +49,33 @@ export interface ResolveMinBidOptions {
|
|
|
49
49
|
* together with `{ hasBids: false }`.
|
|
50
50
|
*/
|
|
51
51
|
export declare function resolveMinBid(currentBidAmount: number, tiers: BidIncrementTier[], override?: number | null, opts?: ResolveMinBidOptions): number;
|
|
52
|
+
export interface BuyNowAvailabilityInput {
|
|
53
|
+
buyNowPrice: number | null | undefined;
|
|
54
|
+
/** The auction's live visible price. Pass the SSE value client-side. */
|
|
55
|
+
currentBid: number | null | undefined;
|
|
56
|
+
isEnded: boolean;
|
|
57
|
+
isSold?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The single definition of "can this auction be bought outright right now".
|
|
61
|
+
*
|
|
62
|
+
* Written once for the same reason `resolveMinBid` is: the predicate had been
|
|
63
|
+
* hand-written in five places (the bid form, three spots on the auction detail
|
|
64
|
+
* page, the marketplace card) and they disagreed — the detail page advertised
|
|
65
|
+
* "Buy Now: ₹5,999" from a two-condition check while the button beneath it used
|
|
66
|
+
* a three-condition one, so most auctions showed a price with no button under
|
|
67
|
+
* it. That is the drift shape Root Cause #30 describes.
|
|
68
|
+
*
|
|
69
|
+
* The old third condition was `!bidsHaveStarted` (eBay's rule: BIN vanishes the
|
|
70
|
+
* moment anyone bids). It is deliberately gone. Buyout now stays available for
|
|
71
|
+
* as long as it is still a better deal than the standing bid, which is the
|
|
72
|
+
* condition that actually matters — a BIN price the bidding has already passed
|
|
73
|
+
* is meaningless, and one it hasn't is still a real offer.
|
|
74
|
+
*
|
|
75
|
+
* `isSold` is optional because client callers rendering a live auction page
|
|
76
|
+
* already know it isn't sold; server callers should always pass it.
|
|
77
|
+
*/
|
|
78
|
+
export declare function isBuyNowAvailable({ buyNowPrice, currentBid, isEnded, isSold, }: BuyNowAvailabilityInput): boolean;
|
|
52
79
|
/**
|
|
53
80
|
* Preset step multipliers offered by the bid form, as multiples of the
|
|
54
81
|
* effective minimum increment. With a ₹100 increment this renders
|