@mohasinac/appkit 2.7.27 → 2.7.31
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/filters/filter-load-options.d.ts +29 -0
- package/dist/_internal/client/features/filters/filter-load-options.js +98 -0
- 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 +504 -9
- 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 +4 -0
- package/dist/client.js +2 -0
- package/dist/configs/next.js +4 -0
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/constants/field-names.d.ts +1 -0
- package/dist/constants/field-names.js +1 -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 +46 -16
- 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/AdminAddressEditorView.d.ts +7 -0
- package/dist/features/admin/components/AdminAddressEditorView.js +117 -0
- 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 +12 -9
- 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/AdminFeatureFlagsView.js +58 -13
- 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/AdminProductEditorView.d.ts +3 -1
- package/dist/features/admin/components/AdminProductEditorView.js +12 -9
- package/dist/features/admin/components/AdminProductsView.js +18 -12
- package/dist/features/admin/components/AdminReturnRequestsView.js +5 -4
- package/dist/features/admin/components/AdminReviewsView.js +36 -26
- 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/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- 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/AuctionBidsTable.d.ts +13 -0
- package/dist/features/auctions/components/AuctionBidsTable.js +62 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +15 -21
- package/dist/features/auctions/components/AuctionFilters.d.ts +7 -1
- package/dist/features/auctions/components/AuctionFilters.js +3 -2
- 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 +2 -1
- package/dist/features/auctions/components/index.js +1 -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/components/CartView.js +2 -1
- package/dist/features/cart/components/CheckoutView.js +2 -1
- 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.d.ts +5 -1
- package/dist/features/categories/components/BrandDetailTabs.js +30 -16
- 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.d.ts +5 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +31 -16
- 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 +12 -0
- package/dist/features/classified/components/ClassifiedFilters.js +20 -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 +12 -0
- package/dist/features/digital-codes/components/DigitalCodeFilters.js +17 -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/repository/event-entry.repository.d.ts +1 -0
- package/dist/features/events/repository/event-entry.repository.js +5 -0
- 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/filters/index.d.ts +2 -0
- package/dist/features/filters/index.js +1 -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 +54 -78
- package/dist/features/layout/BottomActions.js +43 -48
- package/dist/features/live/components/LiveItemFilters.d.ts +12 -0
- package/dist/features/live/components/LiveItemFilters.js +24 -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 +29 -22
- package/dist/features/pre-orders/components/PreOrderFilters.d.ts +7 -1
- package/dist/features/pre-orders/components/PreOrderFilters.js +3 -2
- 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 +3 -3
- package/dist/features/products/components/AuctionsIndexListing.js +15 -51
- package/dist/features/products/components/BidHistory.js +2 -2
- package/dist/features/products/components/CompareOverlay.d.ts +2 -0
- 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.d.ts +7 -1
- package/dist/features/products/components/ProductFilters.js +11 -8
- package/dist/features/products/components/ProductForm.js +18 -16
- package/dist/features/products/components/ProductGalleryClient.js +2 -1
- package/dist/features/products/components/ProductGrid.js +12 -15
- package/dist/features/products/components/ProductsIndexListing.js +16 -52
- package/dist/features/products/components/ProductsIndexPageView.js +2 -2
- package/dist/features/products/repository/products.repository.d.ts +4 -0
- package/dist/features/products/repository/products.repository.js +22 -3
- package/dist/features/products/schemas/firestore.d.ts +7 -1
- 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/products/types/index.d.ts +4 -0
- 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 +18 -4
- 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 +27 -3
- package/dist/index.js +1743 -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 +41 -0
- package/dist/next/routing/route-map.js +17 -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/products-auctions-seed-data.js +40 -40
- package/dist/seed/products-preorders-seed-data.js +16 -16
- package/dist/seed/products-prize-draws-seed-data.js +4 -4
- package/dist/seed/products-standard-seed-data.js +202 -202
- 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 +1 -1
|
@@ -71,6 +71,14 @@ export function PrizeDrawItemsEditor({ items, onChange, onUploadImage, onUploadV
|
|
|
71
71
|
nextImgs[slot] = url;
|
|
72
72
|
update(index, { images: nextImgs.filter(Boolean) });
|
|
73
73
|
};
|
|
74
|
+
const handleVideoFileChange = (e, index, itemNumber) => {
|
|
75
|
+
const file = e.target.files?.[0];
|
|
76
|
+
if (!file || !onUploadVideo)
|
|
77
|
+
return;
|
|
78
|
+
void onUploadVideo(file, itemNumber).then((url) => {
|
|
79
|
+
update(index, { video: { url } });
|
|
80
|
+
});
|
|
81
|
+
};
|
|
74
82
|
const removeImage = (index, slot) => {
|
|
75
83
|
const cur = items[index];
|
|
76
84
|
const nextImgs = (cur.images ?? []).filter((_, i) => i !== slot);
|
|
@@ -85,13 +93,7 @@ export function PrizeDrawItemsEditor({ items, onChange, onUploadImage, onUploadV
|
|
|
85
93
|
}), disabled: locked, placeholder: "2999" }) })] }), _jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { className: "text-sm font-medium", children: ["Images (", (it.images ?? []).length, "/", MAX_IMAGES_PER_ITEM, ")"] }), _jsx(Row, { gap: "sm", className: "flex-wrap", children: Array.from({ length: MAX_IMAGES_PER_ITEM }).map((_, slot) => {
|
|
86
94
|
const existing = (it.images ?? [])[slot];
|
|
87
95
|
return (_jsxs(Div, { className: "w-40", children: [_jsx(ImageUpload, { currentImage: existing, label: existing ? "Replace" : `Image ${slot + 1}`, onUpload: (file) => onUploadImage(file, it.itemNumber), onChange: (url) => setImage(index, slot, url) }), existing && !locked ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeImage(index, slot), children: "Remove" })) : null] }, slot));
|
|
88
|
-
}) })] }), onUploadVideo ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium", children: "Video (optional)" }), _jsx("input", { type: "file", accept: "video/mp4,video/webm", disabled: locked, onChange:
|
|
89
|
-
const file = e.target.files?.[0];
|
|
90
|
-
if (!file)
|
|
91
|
-
return;
|
|
92
|
-
const url = await onUploadVideo(file, it.itemNumber);
|
|
93
|
-
update(index, { video: { url } });
|
|
94
|
-
} }), it.video?.url ? (_jsxs(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] truncate", children: ["Current: ", it.video.url] })) : null] })) : null] })] }, `prize-item-${it.itemNumber}-${index}`));
|
|
96
|
+
}) })] }), onUploadVideo ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium", children: "Video (optional)" }), _jsx("input", { type: "file", accept: "video/mp4,video/webm", disabled: locked, onChange: (e) => handleVideoFileChange(e, index, it.itemNumber) }), it.video?.url ? (_jsxs(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] truncate", children: ["Current: ", it.video.url] })) : null] })) : null] })] }, `prize-item-${it.itemNumber}-${index}`));
|
|
95
97
|
}) }), items.length < MIN_ITEMS ? (_jsxs(Div, { className: "rounded border border-red-400/40 bg-red-50 px-3 py-2 text-sm text-red-900 dark:bg-red-900/30 dark:text-red-100", children: ["At least ", MIN_ITEMS, " prizes are required."] })) : null] }));
|
|
96
98
|
}
|
|
97
99
|
export default PrizeDrawItemsEditor;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useCallback
|
|
4
|
-
import { SlidersHorizontal, X } from "lucide-react";
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
5
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
|
+
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
6
6
|
import { useProducts } from "../hooks/useProducts";
|
|
7
|
-
import {
|
|
7
|
+
import { FilterDrawer, ListingToolbar, Pagination, Text } from "../../../ui";
|
|
8
8
|
import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
|
|
9
9
|
import { useBrands } from "../hooks/useBrands";
|
|
10
10
|
import { MarketplacePrizeDrawCard } from "./MarketplacePrizeDrawCard";
|
|
@@ -38,58 +38,28 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
|
|
|
38
38
|
const { categories } = useCategoryTree();
|
|
39
39
|
const categoryOptions = categoriesToFacetOptions(categories);
|
|
40
40
|
const { brandOptions } = useBrands();
|
|
41
|
-
const
|
|
42
|
-
const pendingTable = useMemo(() => ({
|
|
43
|
-
get: (key) => pendingFilters[key] ?? "",
|
|
44
|
-
getNumber: (key, fallback = 0) => {
|
|
45
|
-
const v = pendingFilters[key];
|
|
46
|
-
if (!v)
|
|
47
|
-
return fallback;
|
|
48
|
-
const n = Number(v);
|
|
49
|
-
return isNaN(n) ? fallback : n;
|
|
50
|
-
},
|
|
51
|
-
set: (key, value) => setPendingFilters((p) => ({ ...p, [key]: value })),
|
|
52
|
-
setMany: (updates) => setPendingFilters((p) => ({ ...p, ...updates })),
|
|
53
|
-
clear: (keys) => {
|
|
54
|
-
const ks = keys ?? FILTER_KEYS;
|
|
55
|
-
setPendingFilters((p) => ({
|
|
56
|
-
...p,
|
|
57
|
-
...Object.fromEntries(ks.map((k) => [k, ""])),
|
|
58
|
-
}));
|
|
59
|
-
},
|
|
60
|
-
setPage: (_) => { },
|
|
61
|
-
setPageSize: (_) => { },
|
|
62
|
-
setSort: (_) => { },
|
|
63
|
-
buildSieveParams: () => "",
|
|
64
|
-
buildSearchParams: () => "",
|
|
65
|
-
params: new URLSearchParams(),
|
|
66
|
-
}), [pendingFilters]);
|
|
41
|
+
const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onFilterReset } = usePendingTable(table, FILTER_KEYS);
|
|
67
42
|
const openFilters = useCallback(() => {
|
|
68
|
-
|
|
43
|
+
onFilterReset();
|
|
69
44
|
setFilterOpen(true);
|
|
70
|
-
}, [
|
|
45
|
+
}, [onFilterReset]);
|
|
71
46
|
const applyFilters = useCallback(() => {
|
|
72
|
-
|
|
73
|
-
for (const k of FILTER_KEYS)
|
|
74
|
-
updates[k] = pendingFilters[k] ?? "";
|
|
75
|
-
table.setMany(updates);
|
|
47
|
+
onFilterApply();
|
|
76
48
|
setFilterOpen(false);
|
|
77
|
-
}, [
|
|
78
|
-
const clearFilters = useCallback(() => {
|
|
79
|
-
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
80
|
-
}, []);
|
|
49
|
+
}, [onFilterApply]);
|
|
81
50
|
const resetAll = useCallback(() => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
51
|
+
table.setMany({
|
|
52
|
+
[TABLE_KEYS.QUERY]: "",
|
|
53
|
+
[TABLE_KEYS.SORT]: "",
|
|
54
|
+
[TABLE_KEYS.SHOW_CLOSED]: "",
|
|
55
|
+
});
|
|
56
|
+
onFilterClear();
|
|
86
57
|
setSearchInput("");
|
|
87
|
-
}, [table]);
|
|
88
|
-
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
58
|
+
}, [table, onFilterClear]);
|
|
89
59
|
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
90
60
|
showClosed ||
|
|
91
61
|
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
92
|
-
|
|
62
|
+
filterActiveCount > 0;
|
|
93
63
|
const revealFilter = (table.get(TABLE_KEYS.PRIZE_REVEAL_STATUS) || undefined);
|
|
94
64
|
const params = {
|
|
95
65
|
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
@@ -119,15 +89,12 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
|
|
|
119
89
|
commitSearch();
|
|
120
90
|
};
|
|
121
91
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
|
|
122
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount:
|
|
92
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search prize draws...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PRIZE_DRAW_SORT_OPTIONS, onSortChange: (v) => {
|
|
123
93
|
table.set(TABLE_KEYS.SORT, v);
|
|
124
94
|
}, view: view, onViewChange: (v) => {
|
|
125
95
|
if (v === VIEW_MODE.TABLE)
|
|
126
96
|
return;
|
|
127
97
|
setView(v);
|
|
128
98
|
table.set(TABLE_KEYS.VIEW, v);
|
|
129
|
-
}, onResetAll: resetAll, hasActiveState: hasActiveState, 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("showClosed", 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]"}` }) })] }) }), 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-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : filteredDraws.length === 0 ? (_jsx(
|
|
130
|
-
...p,
|
|
131
|
-
prizeRevealStatus: e.target.value,
|
|
132
|
-
})), className: "w-full rounded border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-2 py-1.5 text-sm", children: [_jsx("option", { value: "", children: "Any" }), _jsx("option", { value: "pending", children: "Reveal pending" }), _jsx("option", { value: "open", children: "Reveal open" }), _jsx("option", { value: "closed", children: "Closed" })] })] }), _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions })] }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
99
|
+
}, onResetAll: resetAll, hasActiveState: hasActiveState, 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("showClosed", 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]"}` }) })] }) }), 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-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : filteredDraws.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No prize draws found." })) : (_jsx("div", { className: gridClass, children: filteredDraws.map((product) => (_jsx(MarketplacePrizeDrawCard, { product: product, variant: view }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsxs("div", { className: "space-y-4", children: [_jsxs(_Fragment, { children: [_jsx("label", { htmlFor: "prizeRevealStatusFilter", className: "block text-xs font-semibold text-zinc-700 dark:text-zinc-300 mb-1.5", children: "Reveal status" }), _jsxs("select", { id: "prizeRevealStatusFilter", value: pendingTable.get(TABLE_KEYS.PRIZE_REVEAL_STATUS) ?? "", onChange: (e) => pendingTable.set(TABLE_KEYS.PRIZE_REVEAL_STATUS, e.target.value), className: "w-full rounded border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-2 py-1.5 text-sm", children: [_jsx("option", { value: "", children: "Any" }), _jsx("option", { value: "pending", children: "Reveal pending" }), _jsx("option", { value: "open", children: "Reveal open" }), _jsx("option", { value: "closed", children: "Closed" })] })] }), _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions })] }) })] }));
|
|
133
100
|
}
|
|
@@ -21,6 +21,24 @@ import { PrizeDrawCollage } from "./PrizeDrawCollage";
|
|
|
21
21
|
const REVEAL_DURATION_MS = 3200; // total animation length
|
|
22
22
|
const CYCLE_INTERVAL_FAST = 80;
|
|
23
23
|
const CYCLE_INTERVAL_SLOW = 360;
|
|
24
|
+
function isUnauthenticatedResponse(status) {
|
|
25
|
+
return status === 401 || status === 403;
|
|
26
|
+
}
|
|
27
|
+
async function fetchPrizeReveal(productId, orderId) {
|
|
28
|
+
const res = await fetch(`/api/prize-draws/${productId}/reveal`, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: { "Content-Type": "application/json" },
|
|
31
|
+
body: JSON.stringify({ orderId }),
|
|
32
|
+
});
|
|
33
|
+
const json = (await res.json());
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
if (isUnauthenticatedResponse(res.status)) {
|
|
36
|
+
return { response: null, unauthenticated: true };
|
|
37
|
+
}
|
|
38
|
+
throw new Error("Reveal request failed");
|
|
39
|
+
}
|
|
40
|
+
return { response: json.data ?? {}, unauthenticated: false };
|
|
41
|
+
}
|
|
24
42
|
export function PrizeRevealModal({ open, onClose, items, orderId, productId, onReveal, initialPrizeWon, rngSourceUrl, }) {
|
|
25
43
|
const [phase, setPhase] = useState(initialPrizeWon ? "won" : "idle");
|
|
26
44
|
const [highlight, setHighlight] = useState(undefined);
|
|
@@ -79,21 +97,13 @@ export function PrizeRevealModal({ open, onClose, items, orderId, productId, onR
|
|
|
79
97
|
response = await onReveal({ orderId, productId });
|
|
80
98
|
}
|
|
81
99
|
else {
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const json = (await res.json());
|
|
88
|
-
if (!res.ok) {
|
|
89
|
-
if (res.status === 401 || res.status === 403) {
|
|
90
|
-
setPhase("idle");
|
|
91
|
-
setShowLoginModal(true);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
throw new Error("Reveal request failed");
|
|
100
|
+
const result = await fetchPrizeReveal(productId, orderId);
|
|
101
|
+
if (result.unauthenticated) {
|
|
102
|
+
setPhase("idle");
|
|
103
|
+
setShowLoginModal(true);
|
|
104
|
+
return;
|
|
95
105
|
}
|
|
96
|
-
response =
|
|
106
|
+
response = result.response ?? {};
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
109
|
catch (err) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import { productRepository, reviewRepository } from "../../../repositories";
|
|
4
|
+
const CLS_BREADCRUMB_LINK = "hover:text-primary-600 transition-colors";
|
|
4
5
|
import { ROUTES } from "../../../next";
|
|
5
6
|
import { getDefaultCurrency } from "../../../core/baseline-resolver";
|
|
6
7
|
import { Button, Container, Div, Heading, Main, RichText, Row, Section, Span, Stack, Text, } from "../../../ui";
|
|
@@ -216,7 +217,7 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
|
|
|
216
217
|
price: product.price,
|
|
217
218
|
storeId: product.storeId,
|
|
218
219
|
storeName: product.storeName,
|
|
219
|
-
} }), _jsxs(Container, { size: "xl", className: "px-4 py-6", children: [_jsx(ProductDetailView, { renderBreadcrumb: () => (_jsxs("div", { className: "mb-4 flex items-center justify-between flex-wrap gap-2", children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center gap-1.5 text-xs text-zinc-500 dark:text-zinc-400 flex-wrap", children: [_jsx(Link, { href: String(ROUTES.HOME), className:
|
|
220
|
+
} }), _jsxs(Container, { size: "xl", className: "px-4 py-6", children: [_jsx(ProductDetailView, { renderBreadcrumb: () => (_jsxs("div", { className: "mb-4 flex items-center justify-between flex-wrap gap-2", children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center gap-1.5 text-xs text-zinc-500 dark:text-zinc-400 flex-wrap", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRODUCTS), className: CLS_BREADCRUMB_LINK, children: "Products" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), subcategory && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "capitalize", children: subcategory })] }))] }), _jsx(ShareButton, { title: title || undefined })] })), renderGallery: () => (_jsx(ProductGalleryClient, { images: images, productName: title || undefined })), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [condition && (_jsx(Span, { className: "mb-2 inline-block rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-0.5 text-xs font-medium capitalize text-zinc-600 dark:text-zinc-300", children: condition === "new"
|
|
220
221
|
? "Brand New"
|
|
221
222
|
: condition === "like_new"
|
|
222
223
|
? "Like New"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { FacetOption } from "../../filters/FilterFacetSection";
|
|
2
2
|
import type { UrlTable } from "../../filters/FilterPanel";
|
|
3
|
+
import type { AsyncFacetSectionProps } from "../../filters/AsyncFacetSection";
|
|
4
|
+
type LoadOptionsFn = AsyncFacetSectionProps["loadOptions"];
|
|
3
5
|
export type { FacetOption, UrlTable };
|
|
4
6
|
export type ProductFilterVariant = "admin" | "seller" | "public";
|
|
5
7
|
export declare const PRODUCT_FILTER_KEYS: {
|
|
@@ -81,6 +83,10 @@ export interface ProductFiltersProps {
|
|
|
81
83
|
/** @deprecated use storeOptions */
|
|
82
84
|
sellerOptions?: FacetOption[];
|
|
83
85
|
tagOptions?: FacetOption[];
|
|
86
|
+
/** Async load functions — when provided, use AsyncFacetSection instead of static FilterFacetSection */
|
|
87
|
+
loadCategoryOptions?: LoadOptionsFn;
|
|
88
|
+
loadBrandOptions?: LoadOptionsFn;
|
|
89
|
+
loadStoreOptions?: LoadOptionsFn;
|
|
84
90
|
/** Sublisting category options — separate from main category filter */
|
|
85
91
|
sublistingCategoryOptions?: FacetOption[];
|
|
86
92
|
/** Feature badge options for filtering by product features */
|
|
@@ -94,4 +100,4 @@ export interface ProductFiltersProps {
|
|
|
94
100
|
/** Show "part of a bundle" switch filter (default true) */
|
|
95
101
|
showBundleFilter?: boolean;
|
|
96
102
|
}
|
|
97
|
-
export declare function ProductFilters({ table, categoryOptions, brandOptions, storeOptions, sellerOptions, tagOptions, sublistingCategoryOptions, featureOptions, showStatus, variant, statusOptions, currencyPrefix, showShipping, showBundleFilter, }: ProductFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
103
|
+
export declare function ProductFilters({ table, categoryOptions, brandOptions, storeOptions, sellerOptions, tagOptions, sublistingCategoryOptions, featureOptions, showStatus, variant, statusOptions, currencyPrefix, showShipping, showBundleFilter, loadCategoryOptions, loadBrandOptions, loadStoreOptions, }: ProductFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useTranslations } from "next-intl";
|
|
4
4
|
import { FilterFacetSection } from "../../filters/FilterFacetSection";
|
|
5
|
+
import { AsyncFacetSection } from "../../filters/AsyncFacetSection";
|
|
5
6
|
import { RangeFilter } from "../../filters/RangeFilter";
|
|
6
7
|
import { SwitchFilter } from "../../filters/SwitchFilter";
|
|
7
8
|
import { Div } from "../../../ui";
|
|
@@ -47,11 +48,13 @@ export const PRODUCT_FILTER_KEYS = {
|
|
|
47
48
|
TABLE_KEYS.FEATURES,
|
|
48
49
|
],
|
|
49
50
|
};
|
|
51
|
+
const LABEL_PRICE_HIGH_TO_LOW = "Price: High to Low";
|
|
52
|
+
const LABEL_PRICE_LOW_TO_HIGH = "Price: Low to High";
|
|
50
53
|
export const PRODUCT_ADMIN_SORT_OPTIONS = [
|
|
51
54
|
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
|
|
52
55
|
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
|
|
53
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE), label:
|
|
54
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label:
|
|
56
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE), label: LABEL_PRICE_HIGH_TO_LOW },
|
|
57
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: LABEL_PRICE_LOW_TO_HIGH },
|
|
55
58
|
{ value: sortBy(PRODUCT_FIELDS.TITLE, "ASC"), label: "Title A-Z" },
|
|
56
59
|
{ value: sortBy(PRODUCT_FIELDS.TITLE), label: "Title Z-A" },
|
|
57
60
|
{ value: sortBy(PRODUCT_FIELDS.VIEW_COUNT), label: "Most Viewed" },
|
|
@@ -59,15 +62,15 @@ export const PRODUCT_ADMIN_SORT_OPTIONS = [
|
|
|
59
62
|
export const PRODUCT_SELLER_SORT_OPTIONS = [
|
|
60
63
|
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
|
|
61
64
|
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
|
|
62
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE), label:
|
|
63
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label:
|
|
65
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE), label: LABEL_PRICE_HIGH_TO_LOW },
|
|
66
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: LABEL_PRICE_LOW_TO_HIGH },
|
|
64
67
|
{ value: sortBy(PRODUCT_FIELDS.TITLE, "ASC"), label: "Title A-Z" },
|
|
65
68
|
];
|
|
66
69
|
export const PRODUCT_PUBLIC_SORT_OPTIONS = [
|
|
67
70
|
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
|
|
68
71
|
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
|
|
69
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE), label:
|
|
70
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label:
|
|
72
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE), label: LABEL_PRICE_HIGH_TO_LOW },
|
|
73
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: LABEL_PRICE_LOW_TO_HIGH },
|
|
71
74
|
{ value: sortBy(PRODUCT_FIELDS.TITLE, "ASC"), label: "Title A–Z" },
|
|
72
75
|
{ value: sortBy(PRODUCT_FIELDS.TITLE), label: "Title Z–A" },
|
|
73
76
|
{ value: sortBy(PRODUCT_FIELDS.VIEW_COUNT), label: "Most Viewed" },
|
|
@@ -87,7 +90,7 @@ export function getProductSortOptions(variant) {
|
|
|
87
90
|
return PRODUCT_PUBLIC_SORT_OPTIONS;
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
|
-
export function ProductFilters({ table, categoryOptions = [], brandOptions = [], storeOptions = [], sellerOptions, tagOptions = [], sublistingCategoryOptions = [], featureOptions = [], showStatus = false, variant, statusOptions, currencyPrefix = "", showShipping = true, showBundleFilter = true, }) {
|
|
93
|
+
export function ProductFilters({ table, categoryOptions = [], brandOptions = [], storeOptions = [], sellerOptions, tagOptions = [], sublistingCategoryOptions = [], featureOptions = [], showStatus = false, variant, statusOptions, currencyPrefix = "", showShipping = true, showBundleFilter = true, loadCategoryOptions, loadBrandOptions, loadStoreOptions, }) {
|
|
91
94
|
const resolvedStoreOptions = storeOptions.length > 0 ? storeOptions : (sellerOptions ?? []);
|
|
92
95
|
const t = useTranslations("filters");
|
|
93
96
|
const conditionOptions = [
|
|
@@ -112,5 +115,5 @@ export function ProductFilters({ table, categoryOptions = [], brandOptions = [],
|
|
|
112
115
|
const selectedFeatures = splitPipe(TABLE_KEYS.FEATURES);
|
|
113
116
|
const resolvedVariant = variant ?? (showStatus ? "admin" : "public");
|
|
114
117
|
const shouldShowStatus = resolvedVariant !== "public" || showStatus;
|
|
115
|
-
return (_jsxs(Div, { children: [categoryOptions.length > 0
|
|
118
|
+
return (_jsxs(Div, { children: [loadCategoryOptions ? (_jsx(AsyncFacetSection, { title: t("category"), loadOptions: loadCategoryOptions, selected: selectedCategories, onChange: (vals) => table.set(TABLE_KEYS.CATEGORY, vals.join("|")), defaultCollapsed: false })) : categoryOptions.length > 0 ? (_jsx(FilterFacetSection, { title: t("category"), options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set(TABLE_KEYS.CATEGORY, vals.join("|")), searchable: true, defaultCollapsed: categoryOptions.length > 6 })) : null, _jsx(FilterFacetSection, { title: t("condition"), options: conditionOptions, selected: selectedConditions, onChange: (vals) => table.set(TABLE_KEYS.CONDITION, vals.join("|")), searchable: false, defaultCollapsed: false }), _jsx(RangeFilter, { title: t("priceRange"), minValue: table.get(TABLE_KEYS.MIN_PRICE), maxValue: table.get(TABLE_KEYS.MAX_PRICE), onMinChange: (v) => table.set(TABLE_KEYS.MIN_PRICE, v), onMaxChange: (v) => table.set(TABLE_KEYS.MAX_PRICE, v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 500, minPlaceholder: t("minPrice"), maxPlaceholder: t("maxPrice"), defaultCollapsed: false }), loadBrandOptions ? (_jsx(AsyncFacetSection, { title: t("brand"), loadOptions: loadBrandOptions, selected: selectedBrands, onChange: (vals) => table.set(TABLE_KEYS.BRAND, vals.join("|")), defaultCollapsed: false })) : brandOptions.length > 0 ? (_jsx(FilterFacetSection, { title: t("brand"), options: brandOptions, selected: selectedBrands, onChange: (vals) => table.set(TABLE_KEYS.BRAND, vals.join("|")), searchable: brandOptions.length > 4, defaultCollapsed: brandOptions.length > 6 })) : null, loadStoreOptions ? (_jsx(AsyncFacetSection, { title: t("store"), loadOptions: loadStoreOptions, selected: selectedSellers, onChange: (vals) => table.set(TABLE_KEYS.STORE_ID, vals.join("|")), defaultCollapsed: false })) : resolvedStoreOptions.length > 0 ? (_jsx(FilterFacetSection, { title: t("store"), options: resolvedStoreOptions, selected: selectedSellers, onChange: (vals) => table.set(TABLE_KEYS.STORE_ID, vals.join("|")), searchable: resolvedStoreOptions.length > 4, defaultCollapsed: resolvedStoreOptions.length > 6 })) : null, showShipping && (_jsx(SwitchFilter, { title: t("shipping"), label: t("freeShippingOnly"), checked: table.get(TABLE_KEYS.FREE_SHIPPING) === "true", onChange: (v) => table.set(TABLE_KEYS.FREE_SHIPPING, v ? "true" : ""), defaultCollapsed: false })), tagOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("tags"), options: tagOptions, selected: selectedTags, onChange: (vals) => table.set(TABLE_KEYS.TAGS, vals.join("|")), searchable: tagOptions.length > 4, defaultCollapsed: tagOptions.length > 6 })), shouldShowStatus && (_jsx(FilterFacetSection, { title: t("status"), options: statusOptions ?? defaultStatusOptions, selected: selectedStatuses, onChange: (vals) => table.set(TABLE_KEYS.STATUS, vals.join("|")), searchable: false, defaultCollapsed: false })), sublistingCategoryOptions.length > 0 && (_jsx(FilterFacetSection, { title: "Sublisting Type", options: sublistingCategoryOptions, selected: selectedSublistings, onChange: (vals) => table.set(TABLE_KEYS.SUBLISTING_CATEGORY, vals.join("|")), searchable: sublistingCategoryOptions.length > 4, defaultCollapsed: false })), showBundleFilter && (_jsx(SwitchFilter, { title: "Bundles", label: "Part of a bundle only", checked: table.get(TABLE_KEYS.IS_PART_OF_BUNDLE) === "true", onChange: (v) => table.set(TABLE_KEYS.IS_PART_OF_BUNDLE, v ? "true" : ""), defaultCollapsed: false })), featureOptions.length > 0 && (_jsx(FilterFacetSection, { title: "Features", options: featureOptions, selected: selectedFeatures, onChange: (vals) => table.set(TABLE_KEYS.FEATURES, vals.join("|")), searchable: featureOptions.length > 4, defaultCollapsed: featureOptions.length > 4 }))] }));
|
|
116
119
|
}
|
|
@@ -19,6 +19,13 @@ export const PRODUCT_STATUS_OPTIONS = [
|
|
|
19
19
|
{ value: "discontinued", label: "Discontinued" },
|
|
20
20
|
{ value: "sold", label: "Sold" },
|
|
21
21
|
];
|
|
22
|
+
function readFileAsDataUrl(file) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const reader = new FileReader();
|
|
25
|
+
reader.onload = () => resolve(String(reader.result ?? ""));
|
|
26
|
+
reader.readAsDataURL(file);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
22
29
|
export function ProductForm({ product, onChange, isReadonly = false, renderDescriptionEditor, renderCategorySelector, renderBrandSelector, renderStoreAddressSelector, onMediaAbort, currencyPrefix = "", renderGroupSettings, }) {
|
|
23
30
|
const t = useTranslations("adminProducts");
|
|
24
31
|
const { upload } = useMediaUpload();
|
|
@@ -36,7 +43,7 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
|
|
|
36
43
|
type: "product-image",
|
|
37
44
|
index: galleryIndexRef.current,
|
|
38
45
|
name: product.title || "product",
|
|
39
|
-
category: product.category || "uncategorized",
|
|
46
|
+
category: (product.categorySlugs?.[0] ?? product.category) || "uncategorized",
|
|
40
47
|
store: product.storeName || "store",
|
|
41
48
|
});
|
|
42
49
|
};
|
|
@@ -45,7 +52,7 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
|
|
|
45
52
|
type: "product-video",
|
|
46
53
|
index: 1,
|
|
47
54
|
name: product.title || "product",
|
|
48
|
-
category: product.category || "uncategorized",
|
|
55
|
+
category: (product.categorySlugs?.[0] ?? product.category) || "uncategorized",
|
|
49
56
|
store: product.storeName || "store",
|
|
50
57
|
});
|
|
51
58
|
};
|
|
@@ -56,10 +63,10 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
|
|
|
56
63
|
editable: !isReadonly,
|
|
57
64
|
}) })) : (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: t("formDescription") }), _jsx(RichTextEditor, { value: normalizeRichTextHtml(product.description || ""), onChange: (value) => update({ description: value }), disabled: isReadonly, placeholder: "Enter product description" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Rich text is supported for product descriptions." })] })), _jsxs(FormGroup, { columns: 2, children: [renderCategorySelector ? (_jsx(_Fragment, { children: renderCategorySelector({
|
|
58
65
|
label: t("formCategory"),
|
|
59
|
-
value: product.category || "",
|
|
60
|
-
onChange: (value) => update({
|
|
66
|
+
value: (product.categorySlugs?.[0] ?? product.category) || "",
|
|
67
|
+
onChange: (value) => update({ categorySlugs: value ? [value] : [] }),
|
|
61
68
|
disabled: isReadonly,
|
|
62
|
-
}) })) : (_jsx(FormField, { name: "category", label: t("formCategory"), type: "text", value: product.category || "", onChange: (value) => update({
|
|
69
|
+
}) })) : (_jsx(FormField, { name: "category", label: t("formCategory"), type: "text", value: (product.categorySlugs?.[0] ?? product.category) || "", onChange: (value) => update({ categorySlugs: value ? [value] : [] }), disabled: isReadonly })), _jsx(FormField, { name: "subcategory", label: t("formSubcategory"), type: "text", value: product.subcategory || "", onChange: (value) => update({ subcategory: value }), disabled: isReadonly, placeholder: "e.g. Smartphones" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: t("formBrand") }), _jsx("div", { className: "flex gap-3 flex-wrap", children: ["single", "unbranded", "mixed"].map((mode) => (_jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx("input", { type: "radio", name: "brandMode", value: mode, checked: (product.brandMode ?? "single") === mode, onChange: () => {
|
|
63
70
|
if (mode === "unbranded") {
|
|
64
71
|
update({ brandMode: "unbranded", brand: undefined, brands: [] });
|
|
65
72
|
}
|
|
@@ -81,7 +88,7 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
|
|
|
81
88
|
type: "product-image",
|
|
82
89
|
index: 1,
|
|
83
90
|
name: product.title || "product",
|
|
84
|
-
category: product.category || "uncategorized",
|
|
91
|
+
category: (product.categorySlugs?.[0] ?? product.category) || "uncategorized",
|
|
85
92
|
store: product.storeName || "store",
|
|
86
93
|
}), onChange: (url) => update({ mainImage: url }), label: t("formMainImage"), helperText: "Recommended: 800x800px (1:1)" })), isReadonly && product.mainImage && (_jsx(FormField, { name: "mainImage", label: t("formMainImage"), type: "text", value: product.mainImage, onChange: () => { }, disabled: true })), !isReadonly && (_jsx(MediaUploadList, { label: t("formGalleryImages"), value: galleryImages, onChange: (fields) => update({ images: fields.map((f) => f.url) }), onUpload: handleGalleryUpload, accept: "image/*", maxItems: 5, maxSizeMB: 10, helperText: t("formGalleryImagesHelper"), onAbort: onMediaAbort })), !isReadonly && (_jsx(MediaUploadField, { label: t("formVideo"), value: product.video?.url || "", onChange: (url) => update({
|
|
87
94
|
video: url
|
|
@@ -140,16 +147,11 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
|
|
|
140
147
|
return d.toISOString().slice(0, 16);
|
|
141
148
|
})(), onChange: (value) => update({ prizeRevealWindowEnd: value }), disabled: fieldDisabled })] }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "prizeRevealDeadlineDays", label: "Reveal deadline (days)", type: "number", value: String(product.prizeRevealDeadlineDays ?? 3), onChange: (value) => update({
|
|
142
149
|
prizeRevealDeadlineDays: Number(value) || 3,
|
|
143
|
-
}), disabled: fieldDisabled, placeholder: "3", helpText: "After window opens, buyers have this many days to claim." }), _jsx(FormField, { name: "maxPerUser", label: "Max entries per customer (blank = unlimited)", type: "number", value: String(product.maxPerUser ?? ""), onChange: (value) => update({ maxPerUser: Number(value) || undefined }), disabled: fieldDisabled, placeholder: "" })] }), product.prizeGithubFileUrl ? (_jsx(FormField, { name: "prizeGithubFileUrl", label: "RNG Source Code URL (read-only)", type: "text", value: product.prizeGithubFileUrl, onChange: () => { }, disabled: true, helpText: "Auto-set on first save. Public proof-of-fairness link." })) : null, _jsx(PrizeDrawItemsEditor, { items: (product.prizeDrawItems ?? []), onChange: (items) => update({ prizeDrawItems: items }), onUploadImage:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const reader = new FileReader();
|
|
149
|
-
reader.onload = () => resolve(String(reader.result ?? ""));
|
|
150
|
-
reader.readAsDataURL(file);
|
|
151
|
-
});
|
|
152
|
-
} })] }));
|
|
150
|
+
}), disabled: fieldDisabled, placeholder: "3", helpText: "After window opens, buyers have this many days to claim." }), _jsx(FormField, { name: "maxPerUser", label: "Max entries per customer (blank = unlimited)", type: "number", value: String(product.maxPerUser ?? ""), onChange: (value) => update({ maxPerUser: Number(value) || undefined }), disabled: fieldDisabled, placeholder: "" })] }), product.prizeGithubFileUrl ? (_jsx(FormField, { name: "prizeGithubFileUrl", label: "RNG Source Code URL (read-only)", type: "text", value: product.prizeGithubFileUrl, onChange: () => { }, disabled: true, helpText: "Auto-set on first save. Public proof-of-fairness link." })) : null, _jsx(PrizeDrawItemsEditor, { items: (product.prizeDrawItems ?? []), onChange: (items) => update({ prizeDrawItems: items }), onUploadImage: (file) =>
|
|
151
|
+
// Caller is expected to wire a real uploader by overriding
|
|
152
|
+
// renderPrizeDrawImageUpload — fall back to data URL preview
|
|
153
|
+
// so the editor still functions in unwired admin contexts.
|
|
154
|
+
readFileAsDataUrl(file) })] }));
|
|
153
155
|
})()), isPreOrderListing(product) && (_jsxs(_Fragment, { children: [_jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "preOrderDeliveryDate", label: t("formPreOrderDeliveryDate"), type: "datetime-local", value: (() => {
|
|
154
156
|
const d = resolveDate(product.preOrderDeliveryDate);
|
|
155
157
|
if (!d)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { ImageLightbox } from "../../../ui/components/ImageLightbox";
|
|
5
|
+
import { Text } from "../../../ui";
|
|
5
6
|
const PlaceholderSvg = () => (_jsxs("svg", { width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }), _jsx("path", { d: "M21 15l-5-5L5 21" })] }));
|
|
6
7
|
export function ProductGalleryClient({ images, productName }) {
|
|
7
8
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
@@ -19,5 +20,5 @@ export function ProductGalleryClient({ images, productName }) {
|
|
|
19
20
|
setLightboxIndex(activeIndex);
|
|
20
21
|
}, "aria-label": `View ${productName ?? "product"} image in full screen`, children: _jsx("img", { src: mainImage, alt: productName ? `${productName} — image ${activeIndex + 1}` : `Product image ${activeIndex + 1}`, className: "aspect-square w-full object-cover transition-transform duration-300 hover:scale-105" }) }), images.length > 1 && (_jsx("div", { className: "flex gap-2 overflow-x-auto pb-1", children: images.map((src, i) => (_jsx("button", { type: "button", onClick: () => setActiveIndex(i), className: `flex-shrink-0 h-16 w-16 overflow-hidden rounded-lg border-2 transition-all${i === activeIndex
|
|
21
22
|
? " border-primary-500"
|
|
22
|
-
: " border-transparent opacity-60 hover:opacity-100"}`, "aria-label": `View image ${i + 1}`, "aria-current": i === activeIndex ? "true" : undefined, children: _jsx("img", { src: src, alt: "", className: "h-full w-full object-cover" }) }, i))) })), images.length > 1 && (_jsxs(
|
|
23
|
+
: " border-transparent opacity-60 hover:opacity-100"}`, "aria-label": `View image ${i + 1}`, "aria-current": i === activeIndex ? "true" : undefined, children: _jsx("img", { src: src, alt: "", className: "h-full w-full object-cover" }) }, i))) })), images.length > 1 && (_jsxs(Text, { className: "text-center text-xs text-zinc-400 dark:text-zinc-500", children: [activeIndex + 1, " / ", images.length] })), _jsx(ImageLightbox, { images: lightboxImages, activeIndex: lightboxIndex, onClose: () => setLightboxIndex(null), onNavigate: (i) => setLightboxIndex(i) })] }));
|
|
23
24
|
}
|
|
@@ -59,7 +59,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
|
|
|
59
59
|
isWishlisted
|
|
60
60
|
? "bg-rose-500 text-white hover:bg-rose-600"
|
|
61
61
|
: "bg-white/90 dark:bg-slate-800/90 text-zinc-500 dark:text-zinc-400 hover:text-rose-500 hover:bg-white dark:hover:bg-slate-800",
|
|
62
|
-
].join(" "), children: _jsx("svg", { className: "h-4 w-4", fill: isWishlisted ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" }) }) }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3 pt-2.5", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-900 dark:text-white leading-snug`, children: product.title }), (product.categoryName || product.brand) && (_jsxs(Row, { className: "mt-1 gap-1 flex-wrap", children: [product.categoryName && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:text-zinc-400 truncate max-w-[100px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 7a2 2 0 012-2h14a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V7z" }) }), product.categoryName] })), product.brand && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:text-zinc-400 truncate max-w-[90px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" }) }), product.brand] }))] })), (() => {
|
|
62
|
+
].join(" "), children: _jsx("svg", { className: "h-4 w-4", fill: isWishlisted ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" }) }) }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3 pt-2.5", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-900 dark:text-white leading-snug`, children: product.title }), ((Array.isArray(product.categoryNames) ? product.categoryNames[0] : product.categoryName) || product.brand) && (_jsxs(Row, { className: "mt-1 gap-1 flex-wrap", children: [(Array.isArray(product.categoryNames) ? product.categoryNames[0] : product.categoryName) && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:text-zinc-400 truncate max-w-[100px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 7a2 2 0 012-2h14a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V7z" }) }), (Array.isArray(product.categoryNames) ? product.categoryNames[0] : product.categoryName)] })), product.brand && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:text-zinc-400 truncate max-w-[90px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" }) }), product.brand] }))] })), (() => {
|
|
63
63
|
const seller = safeDisplayName(product.storeName, "");
|
|
64
64
|
return seller ? (_jsxs(Text, { className: "mt-0.5 text-[11px] text-zinc-400 dark:text-zinc-500 truncate", children: ["by ", seller] })) : null;
|
|
65
65
|
})(), product.rating !== undefined && (_jsxs(Row, { className: "mt-1 gap-1 items-center", children: [_jsx(Span, { className: "text-[11px] text-amber-400", children: "\u2605" }), _jsxs(Span, { className: "text-[11px] text-zinc-500 dark:text-zinc-400", children: [product.rating.toFixed(1), product.reviewCount ? ` (${product.reviewCount})` : ""] })] })), _jsxs(Div, { className: "mt-auto pt-2", children: [_jsxs(Row, { className: "items-baseline gap-2", children: [_jsx(Span, { className: "text-base font-bold text-primary dark:text-primary-400", children: formatCurrency(product.price, getDefaultCurrency()) }), product.originalPrice && product.originalPrice > product.price && (_jsx(Span, { className: "text-xs text-zinc-400 line-through dark:text-zinc-500", children: formatCurrency(product.originalPrice, getDefaultCurrency()) }))] }), (() => {
|
|
@@ -146,19 +146,22 @@ export function ProductGrid({ products, renderCard, onProductClick, getProductHr
|
|
|
146
146
|
(slots?.renderEmptyState
|
|
147
147
|
? slots.renderEmptyState()
|
|
148
148
|
: null);
|
|
149
|
+
function buildProductCardContext(p) {
|
|
150
|
+
return {
|
|
151
|
+
onClick: onProductClick,
|
|
152
|
+
onWishlistToggle,
|
|
153
|
+
onAddToCart,
|
|
154
|
+
onBuyNow,
|
|
155
|
+
isWishlisted: wishlistedIds?.has(p.id) ?? false,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
149
158
|
const renderItems = () => {
|
|
150
159
|
if (view === "list") {
|
|
151
160
|
return (_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 ${className}`, children: products.map((p) => (_jsx(ProductListRow, { product: p, onClick: onProductClick, onAddToWishlist: onWishlistToggle, isWishlisted: wishlistedIds?.has(p.id) ?? false }, p.id))) }));
|
|
152
161
|
}
|
|
153
162
|
if (view === "fluid") {
|
|
154
163
|
return (_jsx(Grid, { cols: "productCardsCompact", className: className, children: products.map((p, i) => {
|
|
155
|
-
const ctx =
|
|
156
|
-
onClick: onProductClick,
|
|
157
|
-
onWishlistToggle,
|
|
158
|
-
onAddToCart,
|
|
159
|
-
onBuyNow,
|
|
160
|
-
isWishlisted: wishlistedIds?.has(p.id) ?? false,
|
|
161
|
-
};
|
|
164
|
+
const ctx = buildProductCardContext(p);
|
|
162
165
|
const cardRenderer = renderCard ?? slots?.renderCard;
|
|
163
166
|
return cardRenderer ? (_jsx(React.Fragment, { children: cardRenderer === renderCard
|
|
164
167
|
? renderCard(p, ctx)
|
|
@@ -167,13 +170,7 @@ export function ProductGrid({ products, renderCard, onProductClick, getProductHr
|
|
|
167
170
|
}
|
|
168
171
|
const gridClass = GRID_CLASSES.card;
|
|
169
172
|
return (_jsx(Div, { className: `${gridClass} ${className}`, children: products.map((p, i) => {
|
|
170
|
-
const ctx =
|
|
171
|
-
onClick: onProductClick,
|
|
172
|
-
onWishlistToggle,
|
|
173
|
-
onAddToCart,
|
|
174
|
-
onBuyNow,
|
|
175
|
-
isWishlisted: wishlistedIds?.has(p.id) ?? false,
|
|
176
|
-
};
|
|
173
|
+
const ctx = buildProductCardContext(p);
|
|
177
174
|
const cardRenderer = renderCard ?? slots?.renderCard;
|
|
178
175
|
return cardRenderer ? (_jsx(React.Fragment, { children: cardRenderer === renderCard
|
|
179
176
|
? renderCard(p, ctx)
|