@mohasinac/appkit 2.7.26 → 2.7.30
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/classified/ClassifiedDetailView.js +2 -2
- package/dist/_internal/client/features/digital-code/CodeRevealPanel.js +2 -2
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
- package/dist/_internal/server/features/checkout/actions.js +42 -35
- package/dist/_internal/server/features/classified/adapters.d.ts +21 -0
- package/dist/_internal/server/features/classified/adapters.js +24 -0
- package/dist/_internal/server/features/classified/index.d.ts +3 -0
- package/dist/_internal/server/features/classified/index.js +3 -0
- package/dist/_internal/server/features/classified/metadata.d.ts +8 -0
- package/dist/_internal/server/features/classified/metadata.js +43 -0
- package/dist/_internal/server/features/classified/og.d.ts +30 -0
- package/dist/_internal/server/features/classified/og.js +76 -0
- package/dist/_internal/server/features/digital-code/adapters.d.ts +21 -0
- package/dist/_internal/server/features/digital-code/adapters.js +29 -0
- package/dist/_internal/server/features/digital-code/index.d.ts +3 -0
- package/dist/_internal/server/features/digital-code/index.js +3 -0
- package/dist/_internal/server/features/digital-code/metadata.d.ts +8 -0
- package/dist/_internal/server/features/digital-code/metadata.js +36 -0
- package/dist/_internal/server/features/digital-code/og.d.ts +27 -0
- package/dist/_internal/server/features/digital-code/og.js +77 -0
- package/dist/_internal/server/features/live/adapters.d.ts +21 -0
- package/dist/_internal/server/features/live/adapters.js +34 -0
- package/dist/_internal/server/features/live/index.d.ts +3 -0
- package/dist/_internal/server/features/live/index.js +3 -0
- package/dist/_internal/server/features/live/metadata.d.ts +8 -0
- package/dist/_internal/server/features/live/metadata.js +39 -0
- package/dist/_internal/server/features/live/og.d.ts +27 -0
- package/dist/_internal/server/features/live/og.js +72 -0
- package/dist/_internal/server/features/promotions/actions.js +4 -3
- package/dist/_internal/server/jobs/core/onOrderCreate.js +19 -16
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +25 -22
- package/dist/_internal/server/jobs/core/onProductWrite.js +48 -58
- package/dist/_internal/shared/actions/action-registry.js +310 -8
- package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
- package/dist/_internal/shared/features/brands/schema.d.ts +5 -5
- package/dist/_internal/shared/features/cart/schema.d.ts +18 -18
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +38 -38
- package/dist/_internal/shared/features/events/schema.d.ts +8 -8
- package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
- package/dist/_internal/shared/features/products/schema.d.ts +42 -42
- package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
- package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
- package/dist/_internal/shared/media/limits.js +4 -3
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/configs/next.js +4 -0
- package/dist/constants/table-keys.d.ts +6 -0
- package/dist/constants/table-keys.js +9 -0
- package/dist/core/hooks/useSyncManager.js +9 -8
- package/dist/features/about/components/FeesView.js +10 -7
- package/dist/features/about/components/HelpPageView.js +10 -1
- package/dist/features/about/components/HowAuctionsWorkView.js +10 -1
- package/dist/features/about/components/HowCheckoutWorksView.js +10 -1
- package/dist/features/about/components/HowOffersWorkView.js +10 -1
- package/dist/features/about/components/HowOrdersWorkView.js +10 -1
- package/dist/features/about/components/HowPayoutsWorkView.js +10 -1
- package/dist/features/about/components/HowPreOrdersWorkView.js +10 -1
- package/dist/features/about/components/HowReviewsWorkView.js +10 -1
- package/dist/features/about/components/PublicProfileView.js +44 -14
- package/dist/features/about/components/SecurityPrivacyView.js +7 -1
- package/dist/features/account/components/AddressesIndexListing.js +2 -2
- package/dist/features/account/components/NotificationBell.js +27 -16
- package/dist/features/account/components/NotificationPreferencesPanel.d.ts +9 -0
- package/dist/features/account/components/NotificationPreferencesPanel.js +91 -0
- package/dist/features/account/components/UserSidebar.js +4 -4
- package/dist/features/account/components/UserSupportView.js +16 -6
- package/dist/features/account/components/index.d.ts +2 -0
- package/dist/features/account/components/index.js +1 -0
- package/dist/features/account/schemas/index.d.ts +4 -4
- package/dist/features/account/types/index.d.ts +34 -0
- package/dist/features/admin/actions/admin-actions.js +4 -3
- package/dist/features/admin/actions/notification-actions.d.ts +24 -22
- package/dist/features/admin/actions/notification-actions.js +122 -22
- package/dist/features/admin/components/AdminAdEditorView.js +29 -15
- package/dist/features/admin/components/AdminAdsView.js +22 -28
- package/dist/features/admin/components/AdminAllEventEntriesView.js +6 -1
- package/dist/features/admin/components/AdminBidsView.js +10 -3
- package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
- package/dist/features/admin/components/AdminBlogView.js +8 -3
- package/dist/features/admin/components/AdminBrandsView.js +7 -2
- package/dist/features/admin/components/AdminBundleEditorView.js +5 -2
- package/dist/features/admin/components/AdminCarouselEditorView.js +6 -3
- package/dist/features/admin/components/AdminCarouselView.js +13 -7
- package/dist/features/admin/components/AdminCategoriesView.js +8 -3
- package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
- package/dist/features/admin/components/AdminContactView.js +6 -1
- package/dist/features/admin/components/AdminCouponEditorView.js +8 -2
- package/dist/features/admin/components/AdminCouponsView.js +6 -1
- package/dist/features/admin/components/AdminEmployeeEditorView.js +12 -9
- package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
- package/dist/features/admin/components/AdminFaqsView.js +7 -2
- package/dist/features/admin/components/AdminFeaturesView.js +4 -3
- package/dist/features/admin/components/AdminMediaView.js +9 -8
- package/dist/features/admin/components/AdminNavigationView.js +10 -17
- package/dist/features/admin/components/AdminNewsletterView.js +6 -1
- package/dist/features/admin/components/AdminNotificationsView.js +11 -3
- package/dist/features/admin/components/AdminOrdersView.js +6 -1
- package/dist/features/admin/components/AdminPayoutsView.js +10 -3
- package/dist/features/admin/components/AdminPrizeDrawsView.js +10 -3
- package/dist/features/admin/components/AdminProductsView.js +18 -12
- package/dist/features/admin/components/AdminReviewsView.js +33 -24
- package/dist/features/admin/components/AdminScammerEditorView.js +3 -2
- package/dist/features/admin/components/AdminScammersView.js +28 -26
- package/dist/features/admin/components/AdminSectionsView.js +112 -99
- package/dist/features/admin/components/AdminSessionsView.js +10 -3
- package/dist/features/admin/components/AdminSidebar.js +4 -4
- package/dist/features/admin/components/AdminSiteSettingsView.js +45 -4
- package/dist/features/admin/components/AdminStoresView.js +6 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +0 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +32 -34
- package/dist/features/admin/components/AdminTeamView.js +6 -1
- package/dist/features/admin/components/AdminUserEditorView.js +8 -15
- package/dist/features/admin/components/AdminUsersView.js +6 -1
- package/dist/features/admin/components/AdminViewCards.js +4 -3
- package/dist/features/admin/components/DashboardStats.js +2 -2
- package/dist/features/admin/components/DataTable.js +2 -2
- package/dist/features/admin/components/sections/adminSectionsTypes.js +4 -3
- package/dist/features/admin/repository/chat.repository.js +8 -8
- package/dist/features/admin/repository/site-settings.repository.d.ts +1 -0
- package/dist/features/admin/repository/site-settings.repository.js +11 -9
- package/dist/features/admin/schemas/firestore.d.ts +38 -0
- package/dist/features/admin/schemas/firestore.js +15 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +13 -19
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +18 -9
- package/dist/features/auctions/components/MarketplaceAuctionGrid.js +3 -2
- package/dist/features/auctions/components/index.d.ts +0 -1
- package/dist/features/auctions/components/index.js +0 -1
- package/dist/features/auctions/schemas/index.d.ts +14 -14
- package/dist/features/auth/actions/realtime-token-actions.js +19 -4
- package/dist/features/auth/components/RegisterForm.js +8 -4
- package/dist/features/auth/consent-otp.d.ts +2 -0
- package/dist/features/auth/consent-otp.js +8 -0
- package/dist/features/auth/schemas/firestore.d.ts +2 -0
- package/dist/features/auth/schemas/firestore.js +18 -25
- package/dist/features/auth/schemas/index.d.ts +2 -2
- package/dist/features/before-after/api/[id]/route.js +8 -6
- package/dist/features/before-after/schemas/index.d.ts +2 -2
- package/dist/features/blog/actions/blog-actions.d.ts +3 -3
- package/dist/features/blog/components/BlogIndexListing.js +21 -2
- package/dist/features/blog/components/BlogPostForm.js +11 -5
- package/dist/features/blog/components/BlogPostView.js +10 -2
- package/dist/features/blog/schemas/index.d.ts +18 -18
- package/dist/features/cart/components/CartDrawer.js +3 -2
- package/dist/features/cart/repository/cart.repository.js +4 -3
- package/dist/features/cart/utils/pending-ops.d.ts +1 -1
- package/dist/features/categories/api/[id]/route.js +4 -3
- package/dist/features/categories/api/route.js +4 -3
- package/dist/features/categories/components/BrandDetailPageView.js +2 -2
- package/dist/features/categories/components/BrandDetailTabs.js +2 -2
- package/dist/features/categories/components/BundleCollage.d.ts +8 -0
- package/dist/features/categories/components/BundleCollage.js +50 -0
- package/dist/features/categories/components/BundleDetailView.js +2 -10
- package/dist/features/categories/components/BundleItemsPicker.js +20 -13
- package/dist/features/categories/components/CategoriesIndexListing.js +28 -9
- package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
- package/dist/features/categories/components/CategoryDetailTabs.js +2 -2
- package/dist/features/categories/components/CategoryProductsListing.js +52 -13
- package/dist/features/categories/components/index.d.ts +2 -0
- package/dist/features/categories/components/index.js +1 -0
- package/dist/features/categories/schemas/index.d.ts +6 -6
- package/dist/features/checkout/actions/checkout-actions.js +4 -3
- package/dist/features/checkout/hooks/usePaymentCheckout.js +31 -22
- package/dist/features/classified/components/ClassifiedFilters.d.ts +8 -0
- package/dist/features/classified/components/ClassifiedFilters.js +19 -0
- package/dist/features/classified/components/ClassifiedIndexListing.d.ts +4 -0
- package/dist/features/classified/components/ClassifiedIndexListing.js +110 -0
- package/dist/features/classified/components/ClassifiedListView.d.ts +6 -0
- package/dist/features/classified/components/ClassifiedListView.js +43 -0
- package/dist/features/classified/components/index.d.ts +6 -0
- package/dist/features/classified/components/index.js +3 -0
- package/dist/features/collections/api/[slug]/route.js +16 -12
- package/dist/features/collections/schemas/index.d.ts +5 -5
- package/dist/features/consultation/api/[id]/route.js +12 -9
- package/dist/features/consultation/components/ConsultationForm.js +3 -2
- package/dist/features/consultation/schemas/index.d.ts +6 -6
- package/dist/features/contact/email.js +77 -116
- package/dist/features/copilot/components/AdminCopilotView.js +12 -2
- package/dist/features/corporate/api/[id]/route.js +12 -9
- package/dist/features/corporate/components/CorporateInquiryForm.js +2 -1
- package/dist/features/digital-codes/components/DigitalCodeFilters.d.ts +8 -0
- package/dist/features/digital-codes/components/DigitalCodeFilters.js +16 -0
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +4 -0
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +107 -0
- package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +6 -0
- package/dist/features/digital-codes/components/DigitalCodesListView.js +37 -0
- package/dist/features/digital-codes/components/index.d.ts +6 -0
- package/dist/features/digital-codes/components/index.js +3 -0
- package/dist/features/events/actions/event-actions.js +4 -3
- package/dist/features/events/components/AdminEventEditorView.js +3 -2
- package/dist/features/events/components/AdminEventsView.js +4 -2
- package/dist/features/events/components/EventPollWidget.js +5 -5
- package/dist/features/events/components/EventsIndexListing.js +2 -2
- package/dist/features/events/schemas/index.d.ts +20 -20
- package/dist/features/faq/actions/faq-actions.d.ts +8 -8
- package/dist/features/faq/components/ContactCTA.js +3 -1
- package/dist/features/faq/components/FAQAccordion.js +1 -1
- package/dist/features/faq/schemas/index.d.ts +4 -4
- package/dist/features/filters/AsyncFacetSection.d.ts +18 -0
- package/dist/features/filters/AsyncFacetSection.js +65 -0
- package/dist/features/forms/Checkbox.js +2 -2
- package/dist/features/forms/Input.js +2 -2
- package/dist/features/forms/Radio.js +5 -5
- package/dist/features/forms/Select.js +2 -2
- package/dist/features/forms/Slider.js +2 -2
- package/dist/features/forms/Textarea.js +2 -2
- package/dist/features/homepage/api/[id]/route.js +4 -3
- package/dist/features/homepage/api/sections/[id]/route.js +12 -9
- package/dist/features/homepage/components/AdSlot.js +2 -1
- package/dist/features/homepage/components/AdvertisementBanner.js +5 -3
- package/dist/features/homepage/components/BrandsSection.js +2 -2
- package/dist/features/homepage/components/CTABannerSection.js +2 -2
- package/dist/features/homepage/components/CharacterHotspot.d.ts +2 -2
- package/dist/features/homepage/components/CharacterHotspot.js +119 -157
- package/dist/features/homepage/components/CharacterHotspotForm.js +25 -20
- package/dist/features/homepage/components/CustomCardsSection.js +8 -6
- package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
- package/dist/features/homepage/components/GoogleReviewsSection.js +3 -3
- package/dist/features/homepage/components/HeroBanner.js +5 -4
- package/dist/features/homepage/components/HeroCarousel.js +13 -26
- package/dist/features/homepage/components/HowItWorksSection.js +4 -2
- package/dist/features/homepage/components/SocialFeedSection.js +2 -2
- package/dist/features/homepage/components/SocialPostCard.js +3 -2
- package/dist/features/homepage/lib/social-feed-fetcher.js +6 -3
- package/dist/features/layout/AppLayoutShell.d.ts +8 -2
- package/dist/features/layout/AppLayoutShell.js +55 -84
- package/dist/features/layout/BottomActions.js +43 -48
- package/dist/features/layout/NavbarWithSettings.d.ts +6 -0
- package/dist/features/layout/NavbarWithSettings.js +11 -0
- package/dist/features/live/components/LiveItemFilters.d.ts +8 -0
- package/dist/features/live/components/LiveItemFilters.js +23 -0
- package/dist/features/live/components/LiveItemsIndexListing.d.ts +4 -0
- package/dist/features/live/components/LiveItemsIndexListing.js +108 -0
- package/dist/features/live/components/LiveItemsListView.d.ts +6 -0
- package/dist/features/live/components/LiveItemsListView.js +43 -0
- package/dist/features/live/components/index.d.ts +6 -0
- package/dist/features/live/components/index.js +3 -0
- package/dist/features/media/MediaLightbox.js +8 -14
- package/dist/features/media/types/index.d.ts +2 -2
- package/dist/features/media/upload/CameraCapture.js +2 -2
- package/dist/features/media/upload/ImageUpload.js +4 -2
- package/dist/features/media/upload/MediaUploadField.js +24 -8
- package/dist/features/media/upload/MediaUploadList.js +1 -1
- package/dist/features/orders/actions/order-actions.js +4 -3
- package/dist/features/orders/components/MarketplaceOrderCard.js +2 -1
- package/dist/features/orders/components/OrderFilters.js +12 -9
- package/dist/features/orders/schemas/index.d.ts +6 -6
- package/dist/features/pre-orders/api/[id]/route.js +12 -9
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +5 -4
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +27 -20
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +17 -52
- package/dist/features/pre-orders/components/PreorderCard.d.ts +0 -9
- package/dist/features/pre-orders/components/PreorderCard.js +2 -13
- package/dist/features/pre-orders/components/index.d.ts +1 -1
- package/dist/features/pre-orders/components/index.js +1 -1
- package/dist/features/pre-orders/schemas/index.d.ts +10 -10
- package/dist/features/products/api/[id]/route.js +9 -7
- package/dist/features/products/api/route.js +1 -1
- package/dist/features/products/columns/productTableColumns.js +2 -2
- package/dist/features/products/components/AuctionsIndexListing.js +15 -51
- package/dist/features/products/components/BidHistory.js +2 -2
- package/dist/features/products/components/CompareOverlay.js +3 -2
- package/dist/features/products/components/MakeOfferButton.js +20 -14
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -1
- package/dist/features/products/components/PrizeDrawCollage.d.ts +1 -15
- package/dist/features/products/components/PrizeDrawCollage.js +54 -14
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +9 -7
- package/dist/features/products/components/PrizeDrawsIndexListing.js +18 -51
- package/dist/features/products/components/PrizeRevealModal.js +24 -14
- package/dist/features/products/components/ProductDetailPageView.js +2 -1
- package/dist/features/products/components/ProductFilters.js +8 -6
- package/dist/features/products/components/ProductForm.js +12 -10
- package/dist/features/products/components/ProductGalleryClient.js +2 -1
- package/dist/features/products/components/ProductGrid.js +11 -14
- package/dist/features/products/components/ProductsIndexListing.js +16 -52
- package/dist/features/products/components/ProductsIndexPageView.js +2 -2
- package/dist/features/products/schemas/index.d.ts +56 -56
- package/dist/features/products/schemas/product-features.validators.d.ts +14 -14
- package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
- package/dist/features/promotions/schemas/index.d.ts +6 -6
- package/dist/features/reviews/actions/review-actions.js +6 -5
- package/dist/features/reviews/api/[id]/route.js +4 -3
- package/dist/features/reviews/components/ReviewDetailPageView.js +2 -2
- package/dist/features/reviews/components/ReviewDetailShell.js +6 -3
- package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
- package/dist/features/reviews/schemas/index.d.ts +6 -6
- package/dist/features/scams/components/ScamProfileView.js +19 -10
- package/dist/features/scams/components/ScamRegistryView.js +2 -1
- package/dist/features/search/components/Search.d.ts +1 -1
- package/dist/features/search/components/Search.js +56 -49
- package/dist/features/search/schemas/index.d.ts +5 -5
- package/dist/features/seller/actions/offer-actions.js +11 -9
- package/dist/features/seller/components/SellerAddressesView.js +6 -4
- package/dist/features/seller/components/SellerBidsView.js +2 -2
- package/dist/features/seller/components/SellerCouponsView.js +1 -1
- package/dist/features/seller/components/SellerOffersPanel.js +6 -1
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +3 -3
- package/dist/features/seller/components/SellerPreOrdersView.js +5 -4
- package/dist/features/seller/components/SellerPrizeDrawsView.js +5 -4
- package/dist/features/seller/components/SellerProductShell.d.ts +19 -1
- package/dist/features/seller/components/SellerProductShell.js +75 -9
- package/dist/features/seller/components/SellerProductsView.js +10 -11
- package/dist/features/seller/components/SellerShippingView.js +3 -3
- package/dist/features/seller/components/SellerSidebar.js +10 -7
- package/dist/features/seller/components/SellerStorefrontView.js +3 -3
- package/dist/features/seller/schemas/index.d.ts +4 -4
- package/dist/features/shell/FormShell.js +2 -1
- package/dist/features/shell/QuickFormDrawer.js +4 -3
- package/dist/features/shell/StepForm.js +4 -3
- package/dist/features/stores/actions/store-query-actions.js +4 -3
- package/dist/features/stores/components/StoreAboutView.js +2 -1
- package/dist/features/stores/components/StoreAuctionsListing.js +43 -64
- package/dist/features/stores/components/StoreDetailLayoutView.js +2 -2
- package/dist/features/stores/components/StorePreOrdersListing.js +33 -57
- package/dist/features/stores/components/StoreProductsListing.js +16 -49
- package/dist/features/stores/components/StoresIndexListing.js +2 -2
- package/dist/features/stores/schemas/index.d.ts +2 -2
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +8 -7
- package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +5 -5
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +6 -6
- package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -5
- package/dist/index.d.ts +21 -3
- package/dist/index.js +1737 -1725
- package/dist/monitoring/server-logger.js +5 -9
- package/dist/next/api/apiHandler.js +56 -37
- package/dist/next/components/GlobalError.js +2 -1
- package/dist/next/routing/route-map.d.ts +27 -0
- package/dist/next/routing/route-map.js +10 -0
- package/dist/react/ErrorBoundary.js +2 -1
- package/dist/react/contexts/SessionContext.js +38 -28
- package/dist/react/hooks/useCamera.d.ts +1 -1
- package/dist/react/hooks/useCamera.js +8 -7
- package/dist/seed/site-settings-seed-data.js +6 -0
- package/dist/server-entry.d.ts +3 -0
- package/dist/server-entry.js +3 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.js +15 -0
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/DataTable.js +28 -7
- package/dist/ui/components/Button.d.ts +8 -1
- package/dist/ui/components/Button.js +47 -8
- package/dist/ui/components/CollageLightbox.d.ts +18 -0
- package/dist/ui/components/CollageLightbox.js +41 -0
- package/dist/ui/components/Dropdown.js +7 -6
- package/dist/ui/components/ImageLightbox.d.ts +7 -1
- package/dist/ui/components/ImageLightbox.js +15 -5
- package/dist/ui/components/PageLoader.js +4 -3
- package/dist/ui/components/PaginatedMultiSelect.d.ts +28 -0
- package/dist/ui/components/PaginatedMultiSelect.js +141 -0
- package/dist/ui/components/PaginatedMultiSelect.style.css +272 -0
- package/dist/ui/components/Semantic.js +1 -1
- package/dist/ui/components/SideDrawer.js +17 -15
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/utils/number.formatter.js +5 -13
- package/dist/utils/string.formatter.js +21 -18
- package/dist/validation/schemas.d.ts +2 -2
- package/package.json +2 -2
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
3
|
-
import { useState, useCallback
|
|
4
|
-
import { Columns, Heart, ShoppingCart
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
import { Columns, Heart, ShoppingCart } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
7
|
-
import {
|
|
7
|
+
import { BulkActionBar, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Text, useToast } from "../../../ui";
|
|
8
|
+
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
8
9
|
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
9
10
|
import { ACTION_ID, ACTION_META, COMPARE_MAX_ITEMS } from "../../products/constants/action-defs";
|
|
10
11
|
import { CompareOverlay } from "../../products/components/CompareOverlay";
|
|
@@ -45,60 +46,24 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
45
46
|
const categoryOptions = categoriesToFacetOptions(categories);
|
|
46
47
|
const { brandOptions } = useBrands();
|
|
47
48
|
const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "preorder").map((i) => i.itemId));
|
|
48
|
-
|
|
49
|
-
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
50
|
-
const pendingTable = useMemo(() => ({
|
|
51
|
-
get: (key) => pendingFilters[key] ?? "",
|
|
52
|
-
getNumber: (key, fallback = 0) => {
|
|
53
|
-
const v = pendingFilters[key];
|
|
54
|
-
if (!v)
|
|
55
|
-
return fallback;
|
|
56
|
-
const n = Number(v);
|
|
57
|
-
return isNaN(n) ? fallback : n;
|
|
58
|
-
},
|
|
59
|
-
set: (key, value) => setPendingFilters((p) => ({ ...p, [key]: value })),
|
|
60
|
-
setMany: (updates) => setPendingFilters((p) => ({ ...p, ...updates })),
|
|
61
|
-
clear: (keys) => {
|
|
62
|
-
const ks = keys ?? FILTER_KEYS;
|
|
63
|
-
setPendingFilters((p) => ({
|
|
64
|
-
...p,
|
|
65
|
-
...Object.fromEntries(ks.map((k) => [k, ""])),
|
|
66
|
-
}));
|
|
67
|
-
},
|
|
68
|
-
setPage: (_) => { },
|
|
69
|
-
setPageSize: (_) => { },
|
|
70
|
-
setSort: (_) => { },
|
|
71
|
-
buildSieveParams: () => "",
|
|
72
|
-
buildSearchParams: () => "",
|
|
73
|
-
params: new URLSearchParams(),
|
|
74
|
-
}), [pendingFilters]);
|
|
49
|
+
const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onFilterReset } = usePendingTable(table, FILTER_KEYS);
|
|
75
50
|
const openFilters = useCallback(() => {
|
|
76
|
-
|
|
51
|
+
onFilterReset();
|
|
77
52
|
setFilterOpen(true);
|
|
78
|
-
}, [
|
|
53
|
+
}, [onFilterReset]);
|
|
79
54
|
const applyFilters = useCallback(() => {
|
|
80
|
-
|
|
81
|
-
for (const k of FILTER_KEYS)
|
|
82
|
-
updates[k] = pendingFilters[k] ?? "";
|
|
83
|
-
table.setMany(updates);
|
|
55
|
+
onFilterApply();
|
|
84
56
|
setFilterOpen(false);
|
|
85
|
-
}, [
|
|
86
|
-
const clearFilters = useCallback(() => {
|
|
87
|
-
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
88
|
-
}, []);
|
|
57
|
+
}, [onFilterApply]);
|
|
89
58
|
const resetAll = useCallback(() => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
updates[k] = "";
|
|
93
|
-
table.setMany(updates);
|
|
59
|
+
table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.SHOW_CLOSED]: "" });
|
|
60
|
+
onFilterClear();
|
|
94
61
|
setSearchInput("");
|
|
95
|
-
}, [table]);
|
|
96
|
-
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
97
|
-
const pendingFilterCount = FILTER_KEYS.filter((k) => !!pendingFilters[k]).length;
|
|
62
|
+
}, [table, onFilterClear]);
|
|
98
63
|
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
99
64
|
table.get(TABLE_KEYS.SHOW_CLOSED) === "true" ||
|
|
100
65
|
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
101
|
-
|
|
66
|
+
filterActiveCount > 0;
|
|
102
67
|
const params = {
|
|
103
68
|
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
104
69
|
category: table.get(TABLE_KEYS.CATEGORY) || undefined,
|
|
@@ -162,7 +127,7 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
162
127
|
}, [localCart, showToast]);
|
|
163
128
|
const selection = useBulkSelection({ items: preOrders, keyExtractor: (p) => p.id });
|
|
164
129
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
|
|
165
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount:
|
|
130
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: preOrders.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show closed" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showClosed, onClick: () => table.set(TABLE_KEYS.SHOW_CLOSED, showClosed ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showClosed ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showClosed ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
166
131
|
{
|
|
167
132
|
id: ACTION_ID.ADD_TO_CART,
|
|
168
133
|
label: ACTION_META[ACTION_ID.ADD_TO_CART].label,
|
|
@@ -206,8 +171,8 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
206
171
|
setCompareIds(ids);
|
|
207
172
|
},
|
|
208
173
|
},
|
|
209
|
-
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx(
|
|
174
|
+
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No pre-orders found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) : (_jsx("div", { className: gridClass, children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "preorder", onClose: () => {
|
|
210
175
|
setCompareIds([]);
|
|
211
176
|
selection.clearSelection();
|
|
212
|
-
}, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }),
|
|
177
|
+
}, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(PreOrderFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
213
178
|
}
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
import type { PreorderItem } from "../types";
|
|
2
1
|
interface PreorderBadgeProps {
|
|
3
2
|
shipDate?: string;
|
|
4
3
|
className?: string;
|
|
5
4
|
}
|
|
6
5
|
export declare function PreorderBadge({ shipDate, className }: PreorderBadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
-
interface PreorderCardProps {
|
|
8
|
-
item: PreorderItem;
|
|
9
|
-
href: string;
|
|
10
|
-
selectable?: boolean;
|
|
11
|
-
isSelected?: boolean;
|
|
12
|
-
onSelect?: (id: string, selected: boolean) => void;
|
|
13
|
-
}
|
|
14
|
-
export declare function PreorderCard({ item, href, selectable, isSelected, onSelect }: PreorderCardProps): import("react/jsx-runtime").JSX.Element;
|
|
15
6
|
export {};
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import
|
|
3
|
-
import { BaseListingCard, Div, Heading, Span, Text } from "../../../ui";
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Span } from "../../../ui";
|
|
4
3
|
import { getPreorderStatus } from "../types";
|
|
5
|
-
import { formatCurrency } from "../../../utils/number.formatter";
|
|
6
|
-
import { getDefaultLocale } from "../../../core/baseline-resolver";
|
|
7
|
-
import { useLongPress } from "../../../react/hooks/useLongPress";
|
|
8
4
|
const STATUS_LABELS = {
|
|
9
5
|
available: "Pre-order now",
|
|
10
6
|
shipping_soon: "Shipping soon",
|
|
@@ -19,10 +15,3 @@ export function PreorderBadge({ shipDate, className }) {
|
|
|
19
15
|
const status = getPreorderStatus(shipDate);
|
|
20
16
|
return (_jsx(Span, { className: `inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${STATUS_COLORS[status]} ${className ?? ""}`, children: STATUS_LABELS[status] }));
|
|
21
17
|
}
|
|
22
|
-
export function PreorderCard({ item, href, selectable = false, isSelected = false, onSelect }) {
|
|
23
|
-
const longPress = useLongPress(() => onSelect?.(item.id, !isSelected));
|
|
24
|
-
return (_jsxs(Div, { className: `group relative block overflow-hidden rounded-xl border bg-white dark:bg-slate-900 transition-shadow hover:shadow-md ${isSelected ? "border-primary outline outline-2 outline-primary" : "border-gray-200 dark:border-slate-700"}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(item.id, !isSelected); }, label: isSelected ? "Deselect pre-order" : "Select pre-order", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsxs(Link, { href: href, className: "block", children: [item.images[0] ? (_jsx(Div, { role: "img", "aria-label": item.name, className: "aspect-square w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${item.images[0]})` } })) : (_jsx(Div, { className: "aspect-square w-full bg-gray-100" })), _jsxs(Div, { className: "p-4", children: [_jsx(PreorderBadge, { shipDate: item.preorderShipDate }), _jsx(Heading, { level: 3, className: "mt-2 font-semibold text-gray-900 dark:text-zinc-100 text-base", children: item.name }), _jsx(Text, { className: "mt-1 text-sm text-gray-500 dark:text-zinc-400", children: item.brand }), _jsxs(Div, { className: "mt-3 flex items-baseline gap-2", children: [_jsx(Span, { className: "text-lg font-bold text-gray-900 dark:text-zinc-100", children: formatCurrency(item.salePrice) }), item.regularPrice > item.salePrice && (_jsx(Span, { className: "text-sm text-gray-400 dark:text-zinc-500 line-through", children: formatCurrency(item.regularPrice) }))] }), item.preorderShipDate && (_jsxs(Text, { className: "mt-1 text-xs text-gray-400 dark:text-zinc-500", children: ["Ships:", " ", new Date(item.preorderShipDate).toLocaleDateString(getDefaultLocale(), {
|
|
25
|
-
month: "short",
|
|
26
|
-
year: "numeric",
|
|
27
|
-
})] }))] })] })] }));
|
|
28
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { PreorderBadge
|
|
1
|
+
export { PreorderBadge } from "./PreorderCard";
|
|
2
2
|
export { PreOrderFilters } from "./PreOrderFilters";
|
|
3
3
|
export type { PreOrderFiltersProps } from "./PreOrderFilters";
|
|
4
4
|
export { MarketplacePreorderCard, type MarketplacePreorderCardData, type MarketplacePreorderCardLabels, type MarketplacePreorderCardProps, } from "./MarketplacePreorderCard";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { PreorderBadge
|
|
1
|
+
export { PreorderBadge } from "./PreorderCard";
|
|
2
2
|
export { PreOrderFilters } from "./PreOrderFilters";
|
|
3
3
|
export { MarketplacePreorderCard, } from "./MarketplacePreorderCard";
|
|
4
4
|
export { PreOrdersListView } from "./PreOrdersListView";
|
|
@@ -24,14 +24,14 @@ export declare const preorderItemSchema: z.ZodObject<{
|
|
|
24
24
|
}, "strip", z.ZodTypeAny, {
|
|
25
25
|
type: "video" | "image" | "file";
|
|
26
26
|
url: string;
|
|
27
|
-
source?: "
|
|
27
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
28
28
|
alt?: string | undefined;
|
|
29
29
|
thumbnailUrl?: string | undefined;
|
|
30
30
|
youtubeId?: string | undefined;
|
|
31
31
|
}, {
|
|
32
32
|
type: "video" | "image" | "file";
|
|
33
33
|
url: string;
|
|
34
|
-
source?: "
|
|
34
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
35
35
|
alt?: string | undefined;
|
|
36
36
|
thumbnailUrl?: string | undefined;
|
|
37
37
|
youtubeId?: string | undefined;
|
|
@@ -46,44 +46,44 @@ export declare const preorderItemSchema: z.ZodObject<{
|
|
|
46
46
|
createdAt: z.ZodOptional<z.ZodString>;
|
|
47
47
|
}, "strip", z.ZodTypeAny, {
|
|
48
48
|
id: string;
|
|
49
|
+
slug: string;
|
|
49
50
|
name: string;
|
|
50
51
|
active: boolean;
|
|
51
52
|
isFeatured: boolean;
|
|
52
|
-
slug: string;
|
|
53
53
|
salePrice: number;
|
|
54
54
|
regularPrice: number;
|
|
55
55
|
createdAt?: string | undefined;
|
|
56
|
+
brand?: string | undefined;
|
|
57
|
+
images?: string[] | undefined;
|
|
56
58
|
media?: {
|
|
57
59
|
type: "video" | "image" | "file";
|
|
58
60
|
url: string;
|
|
59
|
-
source?: "
|
|
61
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
60
62
|
alt?: string | undefined;
|
|
61
63
|
thumbnailUrl?: string | undefined;
|
|
62
64
|
youtubeId?: string | undefined;
|
|
63
65
|
}[] | undefined;
|
|
64
|
-
brand?: string | undefined;
|
|
65
|
-
images?: string[] | undefined;
|
|
66
66
|
franchise?: string | undefined;
|
|
67
67
|
preorderShipDate?: string | undefined;
|
|
68
68
|
}, {
|
|
69
69
|
id: string;
|
|
70
|
-
name: string;
|
|
71
70
|
slug: string;
|
|
71
|
+
name: string;
|
|
72
72
|
salePrice: number;
|
|
73
73
|
regularPrice: number;
|
|
74
74
|
createdAt?: string | undefined;
|
|
75
|
+
brand?: string | undefined;
|
|
76
|
+
images?: string[] | undefined;
|
|
75
77
|
media?: {
|
|
76
78
|
type: "video" | "image" | "file";
|
|
77
79
|
url: string;
|
|
78
|
-
source?: "
|
|
80
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
79
81
|
alt?: string | undefined;
|
|
80
82
|
thumbnailUrl?: string | undefined;
|
|
81
83
|
youtubeId?: string | undefined;
|
|
82
84
|
}[] | undefined;
|
|
83
85
|
active?: boolean | undefined;
|
|
84
|
-
brand?: string | undefined;
|
|
85
86
|
isFeatured?: boolean | undefined;
|
|
86
|
-
images?: string[] | undefined;
|
|
87
87
|
franchise?: string | undefined;
|
|
88
88
|
preorderShipDate?: string | undefined;
|
|
89
89
|
}>;
|
|
@@ -18,6 +18,8 @@ import { storeRepository } from "../../../stores/repository/store.repository";
|
|
|
18
18
|
// SB-UNI-V — bundlesRepository deleted; bundle stock-sync moves to the
|
|
19
19
|
// `onProductStockChange` Firebase Function (see functions/src/bundle-stock-sync.ts).
|
|
20
20
|
import { sanitizeProductForPublic } from "../../utils/sanitize";
|
|
21
|
+
const ERR_DB_NOT_REGISTERED = "Database provider not registered";
|
|
22
|
+
const ERR_PRODUCT_NOT_FOUND = "Product not found";
|
|
21
23
|
const UNAVAILABLE_PRODUCT_STATUSES = new Set([
|
|
22
24
|
"sold",
|
|
23
25
|
"out_of_stock",
|
|
@@ -53,7 +55,7 @@ const productUpdateSchema = z
|
|
|
53
55
|
featured: z.boolean().optional(),
|
|
54
56
|
isPromoted: z.boolean().optional(),
|
|
55
57
|
listingType: z
|
|
56
|
-
.enum(["standard", "auction", "pre-order", "prize-draw", "bundle"])
|
|
58
|
+
.enum(["standard", "auction", "pre-order", "prize-draw", "bundle", "classified", "digital-code", "live"])
|
|
57
59
|
.optional(),
|
|
58
60
|
media: z.array(mediaFieldSchema).optional(),
|
|
59
61
|
})
|
|
@@ -64,7 +66,7 @@ export async function GET(_request, context) {
|
|
|
64
66
|
const { id } = await context.params;
|
|
65
67
|
const repo = getRepo();
|
|
66
68
|
if (!repo) {
|
|
67
|
-
return NextResponse.json({ success: false, error:
|
|
69
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_REGISTERED }, { status: 503 });
|
|
68
70
|
}
|
|
69
71
|
// Support both Firestore document ID and URL slug
|
|
70
72
|
let item = await repo.findById(id);
|
|
@@ -77,7 +79,7 @@ export async function GET(_request, context) {
|
|
|
77
79
|
item = slugResult.data[0] ?? null;
|
|
78
80
|
}
|
|
79
81
|
if (!item) {
|
|
80
|
-
return NextResponse.json({ success: false, error:
|
|
82
|
+
return NextResponse.json({ success: false, error: ERR_PRODUCT_NOT_FOUND }, { status: 404 });
|
|
81
83
|
}
|
|
82
84
|
const sanitized = sanitizeProductForPublic(item);
|
|
83
85
|
return NextResponse.json({ success: true, data: sanitized });
|
|
@@ -96,11 +98,11 @@ export const PATCH = createRouteHandler({
|
|
|
96
98
|
const { id } = params;
|
|
97
99
|
const repo = getRepo();
|
|
98
100
|
if (!repo) {
|
|
99
|
-
return NextResponse.json({ success: false, error:
|
|
101
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_REGISTERED }, { status: 503 });
|
|
100
102
|
}
|
|
101
103
|
const product = await repo.findById(id);
|
|
102
104
|
if (!product) {
|
|
103
|
-
return NextResponse.json({ success: false, error:
|
|
105
|
+
return NextResponse.json({ success: false, error: ERR_PRODUCT_NOT_FOUND }, { status: 404 });
|
|
104
106
|
}
|
|
105
107
|
const ownerStore = product.storeId ? await storeRepository.findByOwnerId(user?.uid ?? "") : null;
|
|
106
108
|
const isOwner = !!ownerStore && ownerStore.id === product.storeId;
|
|
@@ -150,11 +152,11 @@ export const DELETE = createRouteHandler({
|
|
|
150
152
|
const { id } = params;
|
|
151
153
|
const repo = getRepo();
|
|
152
154
|
if (!repo) {
|
|
153
|
-
return NextResponse.json({ success: false, error:
|
|
155
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_REGISTERED }, { status: 503 });
|
|
154
156
|
}
|
|
155
157
|
const product = await repo.findById(id);
|
|
156
158
|
if (!product) {
|
|
157
|
-
return NextResponse.json({ success: false, error:
|
|
159
|
+
return NextResponse.json({ success: false, error: ERR_PRODUCT_NOT_FOUND }, { status: 404 });
|
|
158
160
|
}
|
|
159
161
|
const ownerStore = product.storeId ? await storeRepository.findByOwnerId(user?.uid ?? "") : null;
|
|
160
162
|
const isOwner = !!ownerStore && ownerStore.id === product.storeId;
|
|
@@ -48,7 +48,7 @@ const productMutateSchema = z
|
|
|
48
48
|
sellerEmail: z.string().email().optional(),
|
|
49
49
|
slug: z.string().optional(),
|
|
50
50
|
listingType: z
|
|
51
|
-
.enum(["standard", "auction", "pre-order", "prize-draw", "bundle"])
|
|
51
|
+
.enum(["standard", "auction", "pre-order", "prize-draw", "bundle", "classified", "digital-code", "live"])
|
|
52
52
|
.optional(),
|
|
53
53
|
media: z.array(mediaFieldSchema).optional(),
|
|
54
54
|
})
|
|
@@ -76,10 +76,10 @@ export function getProductTableColumns({ labels, onEdit, onDelete, currencyCode,
|
|
|
76
76
|
},
|
|
77
77
|
},
|
|
78
78
|
];
|
|
79
|
-
const actions = (product) => (_jsxs(Div, { className: "flex gap-2", children: [_jsx(Button, { type: "button", onClick: (e) => {
|
|
79
|
+
const actions = (product) => (_jsxs(Div, { className: "flex gap-2", children: [_jsx(Button, { variant: "ghost", type: "button", onClick: (e) => {
|
|
80
80
|
e.stopPropagation();
|
|
81
81
|
onEdit(product);
|
|
82
|
-
}, className: "text-primary hover:text-primary/80", children: labels.edit }), _jsx(Button, { type: "button", onClick: (e) => {
|
|
82
|
+
}, className: "text-primary hover:text-primary/80", children: labels.edit }), _jsx(Button, { variant: "ghost", type: "button", onClick: (e) => {
|
|
83
83
|
e.stopPropagation();
|
|
84
84
|
onDelete(product);
|
|
85
85
|
}, className: "text-red-600 hover:text-red-800 dark:text-red-400", children: labels.delete })] }));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
3
|
-
import { useState, useCallback
|
|
4
|
-
import { SlidersHorizontal, X } from "lucide-react";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
5
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
5
|
import { useProducts } from "../hooks/useProducts";
|
|
7
|
-
import { Pagination, useToast, ListingToolbar, BulkActionBar, LoginRequiredModal } from "../../../ui";
|
|
6
|
+
import { Pagination, useToast, ListingToolbar, BulkActionBar, LoginRequiredModal, FilterDrawer } from "../../../ui";
|
|
7
|
+
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
8
8
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
9
9
|
import { MarketplaceAuctionGrid } from "../../auctions/components/MarketplaceAuctionGrid";
|
|
10
10
|
import { AuctionFilters } from "../../auctions/components/AuctionFilters";
|
|
@@ -40,60 +40,24 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
|
40
40
|
const { categories } = useCategoryTree();
|
|
41
41
|
const categoryOptions = categoriesToFacetOptions(categories);
|
|
42
42
|
const { brandOptions } = useBrands();
|
|
43
|
-
|
|
44
|
-
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
45
|
-
const pendingTable = useMemo(() => ({
|
|
46
|
-
get: (key) => pendingFilters[key] ?? "",
|
|
47
|
-
getNumber: (key, fallback = 0) => {
|
|
48
|
-
const v = pendingFilters[key];
|
|
49
|
-
if (!v)
|
|
50
|
-
return fallback;
|
|
51
|
-
const n = Number(v);
|
|
52
|
-
return isNaN(n) ? fallback : n;
|
|
53
|
-
},
|
|
54
|
-
set: (key, value) => setPendingFilters((p) => ({ ...p, [key]: value })),
|
|
55
|
-
setMany: (updates) => setPendingFilters((p) => ({ ...p, ...updates })),
|
|
56
|
-
clear: (keys) => {
|
|
57
|
-
const ks = keys ?? FILTER_KEYS;
|
|
58
|
-
setPendingFilters((p) => ({
|
|
59
|
-
...p,
|
|
60
|
-
...Object.fromEntries(ks.map((k) => [k, ""])),
|
|
61
|
-
}));
|
|
62
|
-
},
|
|
63
|
-
setPage: (_) => { },
|
|
64
|
-
setPageSize: (_) => { },
|
|
65
|
-
setSort: (_) => { },
|
|
66
|
-
buildSieveParams: () => "",
|
|
67
|
-
buildSearchParams: () => "",
|
|
68
|
-
params: new URLSearchParams(),
|
|
69
|
-
}), [pendingFilters]);
|
|
43
|
+
const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onFilterReset } = usePendingTable(table, FILTER_KEYS);
|
|
70
44
|
const openFilters = useCallback(() => {
|
|
71
|
-
|
|
45
|
+
onFilterReset();
|
|
72
46
|
setFilterOpen(true);
|
|
73
|
-
}, [
|
|
47
|
+
}, [onFilterReset]);
|
|
74
48
|
const applyFilters = useCallback(() => {
|
|
75
|
-
|
|
76
|
-
for (const k of FILTER_KEYS)
|
|
77
|
-
updates[k] = pendingFilters[k] ?? "";
|
|
78
|
-
table.setMany(updates);
|
|
49
|
+
onFilterApply();
|
|
79
50
|
setFilterOpen(false);
|
|
80
|
-
}, [
|
|
81
|
-
const clearFilters = useCallback(() => {
|
|
82
|
-
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
83
|
-
}, []);
|
|
51
|
+
}, [onFilterApply]);
|
|
84
52
|
const resetAll = useCallback(() => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
updates[k] = "";
|
|
88
|
-
table.setMany(updates);
|
|
53
|
+
table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.SHOW_ENDED]: "" });
|
|
54
|
+
onFilterClear();
|
|
89
55
|
setSearchInput("");
|
|
90
|
-
}, [table]);
|
|
91
|
-
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
92
|
-
const pendingFilterCount = FILTER_KEYS.filter((k) => !!pendingFilters[k]).length;
|
|
56
|
+
}, [table, onFilterClear]);
|
|
93
57
|
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
94
58
|
table.get(TABLE_KEYS.SHOW_ENDED) === "true" ||
|
|
95
59
|
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
96
|
-
|
|
60
|
+
filterActiveCount > 0;
|
|
97
61
|
const params = {
|
|
98
62
|
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
99
63
|
category: table.get(TABLE_KEYS.CATEGORY) || undefined,
|
|
@@ -148,7 +112,7 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
|
148
112
|
isWishlisted: (productId) => wishlistedIds.has(productId),
|
|
149
113
|
};
|
|
150
114
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-3 gap-4";
|
|
151
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount:
|
|
115
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: auctions.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show ended" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showEnded, onClick: () => table.set(TABLE_KEYS.SHOW_ENDED, showEnded ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showEnded ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showEnded ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
152
116
|
{
|
|
153
117
|
id: ACTION_ID.WATCH_AUCTION,
|
|
154
118
|
label: "Add to Watchlist",
|
|
@@ -169,5 +133,5 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
|
169
133
|
selection.clearSelection();
|
|
170
134
|
},
|
|
171
135
|
},
|
|
172
|
-
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-4" : gridClass, wishlistActions: wishlistActions })) }),
|
|
136
|
+
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-4" : gridClass, wishlistActions: wishlistActions })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
173
137
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import { Span, Text } from "../../../ui";
|
|
3
|
+
import { Heading, Span, Text } from "../../../ui";
|
|
4
4
|
import { formatCurrency } from "../../../utils/number.formatter";
|
|
5
5
|
function formatBidDate(raw) {
|
|
6
6
|
if (!raw)
|
|
@@ -30,5 +30,5 @@ export function BidHistory({ isLoading = false, isEmpty = false, renderList, ren
|
|
|
30
30
|
}
|
|
31
31
|
if (renderList)
|
|
32
32
|
return _jsx("div", { className: className, "data-section": "bidhistory-div-420", children: renderList() });
|
|
33
|
-
return (_jsxs("div", { className: className, "data-section": "bidhistory-div-421", children: [labels.title && (_jsx(
|
|
33
|
+
return (_jsxs("div", { className: className, "data-section": "bidhistory-div-421", children: [labels.title && (_jsx(Heading, { level: 3, className: "mb-3 text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: labels.title })), _jsx("div", { className: "space-y-2", children: bids.map((bid, i) => renderBid ? (_jsx(React.Fragment, { children: renderBid(bid, i) }, bid.id)) : (_jsxs("div", { className: "flex items-center justify-between rounded-lg border border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-3 text-sm", "data-section": "bidhistory-div-422", children: [_jsx(Span, { className: "font-bold text-primary-600 dark:text-primary-400", children: currency ? formatCurrency(bid.amount, currency) : bid.amount.toLocaleString() }), _jsx(Span, { className: "text-xs text-zinc-400 dark:text-zinc-500", children: formatBidDate(bid.placedAt) })] }, bid.id))) })] }));
|
|
34
34
|
}
|
|
@@ -17,6 +17,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
17
17
|
* `GET /api/products?ids=<csv>` (BK3 listByIds endpoint).
|
|
18
18
|
*/
|
|
19
19
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
20
|
+
const REL_NOOPENER = "noopener noreferrer";
|
|
20
21
|
import Link from "next/link";
|
|
21
22
|
import { X } from "lucide-react";
|
|
22
23
|
import { Button, Div, Heading, Row, Stack, Text } from "../../../ui";
|
|
@@ -67,9 +68,9 @@ function FieldRow({ label, children }) {
|
|
|
67
68
|
function CompareColumn({ item, productType, labels, onRemove, onClose }) {
|
|
68
69
|
const href = detailHref(item, productType);
|
|
69
70
|
const img = item.mainImage ?? item.images?.[0];
|
|
70
|
-
return (_jsxs(Stack, { gap: "md", className: COLUMN_CARD_CLASS, children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", children: [_jsx(Link, { href: href, target: "_blank", rel:
|
|
71
|
+
return (_jsxs(Stack, { gap: "md", className: COLUMN_CARD_CLASS, children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", children: [_jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 hover:underline line-clamp-2", children: item.title ?? item.id }), onRemove && (_jsx("button", { type: "button", onClick: () => onRemove(item.id), "aria-label": `${labels.remove} ${item.title ?? item.id}`, className: "shrink-0 rounded-full p-1 text-zinc-400 hover:bg-zinc-100 hover:text-rose-500 dark:hover:bg-zinc-800", children: _jsx(X, { className: "h-4 w-4" }) }))] }), _jsx(FieldRow, { label: labels.field.image, children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, children: _jsx(Div, { className: "relative aspect-square overflow-hidden rounded-lg bg-zinc-100 dark:bg-zinc-800", children: img ? (
|
|
71
72
|
// eslint-disable-next-line @next/next/no-img-element
|
|
72
|
-
_jsx("img", { src: img, alt: item.title ?? item.id, className: "h-full w-full object-cover" })) : null }) }) }), _jsx(FieldRow, { label: labels.field.price, children: _jsx(Text, { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: priceLabel(item) }) }), _jsx(FieldRow, { label: labels.field.condition, children: _jsx(Text, { children: item.condition ? _jsx("span", { className: CHIP_CLASS, children: item.condition }) : "—" }) }), _jsx(FieldRow, { label: labels.field.brand, children: _jsx(Text, { children: item.brand ? _jsx("span", { className: CHIP_CLASS, children: item.brand }) : "—" }) }), _jsx(FieldRow, { label: labels.field.category, children: _jsx(Text, { children: item.categoryName || item.category ? (_jsx("span", { className: CHIP_CLASS, children: item.categoryName ?? item.category })) : ("—") }) }), _jsx(FieldRow, { label: labels.field.store, children: _jsx(Text, { className: "text-sm text-zinc-700 dark:text-zinc-300", children: item.storeName ?? "—" }) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", className: "mt-auto", children: _jsx(Link, { href: href, target: "_blank", rel:
|
|
73
|
+
_jsx("img", { src: img, alt: item.title ?? item.id, className: "h-full w-full object-cover" })) : null }) }) }), _jsx(FieldRow, { label: labels.field.price, children: _jsx(Text, { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: priceLabel(item) }) }), _jsx(FieldRow, { label: labels.field.condition, children: _jsx(Text, { children: item.condition ? _jsx("span", { className: CHIP_CLASS, children: item.condition }) : "—" }) }), _jsx(FieldRow, { label: labels.field.brand, children: _jsx(Text, { children: item.brand ? _jsx("span", { className: CHIP_CLASS, children: item.brand }) : "—" }) }), _jsx(FieldRow, { label: labels.field.category, children: _jsx(Text, { children: item.categoryName || item.category ? (_jsx("span", { className: CHIP_CLASS, children: item.categoryName ?? item.category })) : ("—") }) }), _jsx(FieldRow, { label: labels.field.store, children: _jsx(Text, { className: "text-sm text-zinc-700 dark:text-zinc-300", children: item.storeName ?? "—" }) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", className: "mt-auto", children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, onClick: onClose, children: labels.view }) })] }));
|
|
73
74
|
}
|
|
74
75
|
function useFetchedProducts(productIds, passedItems, isOpen) {
|
|
75
76
|
const [fetched, setFetched] = useState([]);
|
|
@@ -4,6 +4,9 @@ import { useState, useTransition } from "react";
|
|
|
4
4
|
import { Button, Div, Input, LoginRequiredModal, Span, Text } from "../../../ui";
|
|
5
5
|
import { isAuthError } from "../../../utils/auth-error";
|
|
6
6
|
import { formatCurrency } from "../../../utils/number.formatter";
|
|
7
|
+
function isActiveOfferError(msg) {
|
|
8
|
+
return msg.includes("active offer") || msg.includes("ACTIVE_OFFER");
|
|
9
|
+
}
|
|
7
10
|
export function MakeOfferButton({ productId, listedPrice, currency, minOfferPercent = 70, onMakeOffer, className = "", }) {
|
|
8
11
|
const [state, setState] = useState("idle");
|
|
9
12
|
const [errorMsg, setErrorMsg] = useState("");
|
|
@@ -25,6 +28,22 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
|
|
|
25
28
|
if (!isNaN(parsed))
|
|
26
29
|
setOfferAmount(parsed);
|
|
27
30
|
}
|
|
31
|
+
function handleOfferError(err) {
|
|
32
|
+
if (isAuthError(err)) {
|
|
33
|
+
setState("idle");
|
|
34
|
+
setShowLoginModal(true);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
38
|
+
if (isActiveOfferError(msg)) {
|
|
39
|
+
setState("pending");
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
setErrorMsg(msg || "Could not send offer. Please try again.");
|
|
43
|
+
setState("error");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
28
47
|
function handleSubmit() {
|
|
29
48
|
if (offerAmount < minOffer) {
|
|
30
49
|
setErrorMsg(`Minimum offer is ${fmt(minOffer)}`);
|
|
@@ -43,20 +62,7 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
|
|
|
43
62
|
setState("success");
|
|
44
63
|
}
|
|
45
64
|
catch (err) {
|
|
46
|
-
|
|
47
|
-
setState("idle");
|
|
48
|
-
setShowLoginModal(true);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
52
|
-
if (msg.includes("active offer") || msg.includes("ACTIVE_OFFER")) {
|
|
53
|
-
setState("pending");
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
setErrorMsg(msg || "Could not send offer. Please try again.");
|
|
57
|
-
setState("error");
|
|
58
|
-
}
|
|
59
|
-
}
|
|
65
|
+
handleOfferError(err);
|
|
60
66
|
}
|
|
61
67
|
});
|
|
62
68
|
}
|
|
@@ -4,6 +4,7 @@ import { useCallback } from "react";
|
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
5
|
import { useLongPress } from "../../../react/hooks/useLongPress";
|
|
6
6
|
import { ROUTES } from "../../../next";
|
|
7
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
7
8
|
import { formatCurrency } from "../../../utils";
|
|
8
9
|
import { getDefaultCurrency } from "../../../core/baseline-resolver";
|
|
9
10
|
import { BaseListingCard, Button, Div, Row, Span, Text, TextLink, } from "../../../ui";
|
|
@@ -15,7 +16,7 @@ const DEFAULT_LABELS = {
|
|
|
15
16
|
closedBadge: "Draw closed",
|
|
16
17
|
entriesRemainingLabel: (remaining, max) => `${remaining}/${max} entries left`,
|
|
17
18
|
pricePerEntryLabel: "per entry",
|
|
18
|
-
enterDraw: "
|
|
19
|
+
enterDraw: ACTIONS.PRIZE_DRAW["enter-draw"].label,
|
|
19
20
|
};
|
|
20
21
|
function statusVariant(status) {
|
|
21
22
|
switch (status) {
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PrizeDrawCollage (SB4-B)
|
|
3
|
-
*
|
|
4
|
-
* Read-only display of a prize-draw's full prize pool. Each cell shows the
|
|
5
|
-
* primary image, item-number badge, and title. Items with `isWon === true`
|
|
6
|
-
* are dimmed under a diagonal overlay with a "Won" label so the public
|
|
7
|
-
* collage truthfully reflects what's still up for grabs.
|
|
8
|
-
*
|
|
9
|
-
* Optional `highlightItemNumber` (passed by the reveal modal during animation)
|
|
10
|
-
* adds an emphasis ring to the cell that just won.
|
|
11
|
-
*/
|
|
12
1
|
import type { PrizeDrawItem } from "../schemas/firestore";
|
|
13
2
|
export interface PrizeDrawCollageProps {
|
|
14
3
|
items: PrizeDrawItem[];
|
|
15
4
|
highlightItemNumber?: number;
|
|
16
|
-
/** Custom click handler —
|
|
5
|
+
/** Custom click handler — overrides the built-in lightbox. */
|
|
17
6
|
onItemClick?: (item: PrizeDrawItem) => void;
|
|
18
7
|
/** Defaults to "Won". Use for localisation. */
|
|
19
8
|
wonLabel?: string;
|
|
@@ -22,9 +11,6 @@ export interface PrizeDrawCollageProps {
|
|
|
22
11
|
* renders — otherwise potential buyers would see their favorite prize is
|
|
23
12
|
* already gone and drop out. The seller / admin / winner views leave this
|
|
24
13
|
* `false` (default) to show real pool state.
|
|
25
|
-
*
|
|
26
|
-
* The product adapter for public reads should ALSO strip `isWon` before
|
|
27
|
-
* sending the items array client-side; this prop is the visual fallback.
|
|
28
14
|
*/
|
|
29
15
|
hideWonState?: boolean;
|
|
30
16
|
}
|