@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
|
@@ -115,13 +115,14 @@ export declare const categoryItemSchema: z.ZodObject<{
|
|
|
115
115
|
updatedAt: z.ZodOptional<z.ZodString>;
|
|
116
116
|
}, "strip", z.ZodTypeAny, {
|
|
117
117
|
id: string;
|
|
118
|
-
name: string;
|
|
119
118
|
slug: string;
|
|
119
|
+
name: string;
|
|
120
120
|
tier: number;
|
|
121
121
|
createdAt?: string | undefined;
|
|
122
122
|
updatedAt?: string | undefined;
|
|
123
123
|
path?: string | undefined;
|
|
124
124
|
type?: "category" | "brand" | "collection" | "concern" | undefined;
|
|
125
|
+
description?: string | undefined;
|
|
125
126
|
display?: {
|
|
126
127
|
icon?: string | undefined;
|
|
127
128
|
showInMenu?: boolean | undefined;
|
|
@@ -129,7 +130,6 @@ export declare const categoryItemSchema: z.ZodObject<{
|
|
|
129
130
|
} | undefined;
|
|
130
131
|
order?: number | undefined;
|
|
131
132
|
isFeatured?: boolean | undefined;
|
|
132
|
-
description?: string | undefined;
|
|
133
133
|
seo?: {
|
|
134
134
|
title?: string | undefined;
|
|
135
135
|
description?: string | undefined;
|
|
@@ -148,13 +148,14 @@ export declare const categoryItemSchema: z.ZodObject<{
|
|
|
148
148
|
} | undefined;
|
|
149
149
|
}, {
|
|
150
150
|
id: string;
|
|
151
|
-
name: string;
|
|
152
151
|
slug: string;
|
|
152
|
+
name: string;
|
|
153
153
|
tier: number;
|
|
154
154
|
createdAt?: string | undefined;
|
|
155
155
|
updatedAt?: string | undefined;
|
|
156
156
|
path?: string | undefined;
|
|
157
157
|
type?: "category" | "brand" | "collection" | "concern" | undefined;
|
|
158
|
+
description?: string | undefined;
|
|
158
159
|
display?: {
|
|
159
160
|
icon?: string | undefined;
|
|
160
161
|
showInMenu?: boolean | undefined;
|
|
@@ -162,7 +163,6 @@ export declare const categoryItemSchema: z.ZodObject<{
|
|
|
162
163
|
} | undefined;
|
|
163
164
|
order?: number | undefined;
|
|
164
165
|
isFeatured?: boolean | undefined;
|
|
165
|
-
description?: string | undefined;
|
|
166
166
|
seo?: {
|
|
167
167
|
title?: string | undefined;
|
|
168
168
|
description?: string | undefined;
|
|
@@ -192,16 +192,16 @@ export declare const categoryListParamsSchema: z.ZodObject<{
|
|
|
192
192
|
perPage?: number | undefined;
|
|
193
193
|
type?: "category" | "brand" | "collection" | "concern" | undefined;
|
|
194
194
|
q?: string | undefined;
|
|
195
|
-
page?: number | undefined;
|
|
196
195
|
featured?: boolean | undefined;
|
|
196
|
+
page?: number | undefined;
|
|
197
197
|
tier?: number | undefined;
|
|
198
198
|
parentId?: string | undefined;
|
|
199
199
|
}, {
|
|
200
200
|
perPage?: number | undefined;
|
|
201
201
|
type?: "category" | "brand" | "collection" | "concern" | undefined;
|
|
202
202
|
q?: string | undefined;
|
|
203
|
-
page?: number | undefined;
|
|
204
203
|
featured?: boolean | undefined;
|
|
204
|
+
page?: number | undefined;
|
|
205
205
|
tier?: number | undefined;
|
|
206
206
|
parentId?: string | undefined;
|
|
207
207
|
}>;
|
|
@@ -11,6 +11,7 @@ import { serverLogger } from "../../../monitoring";
|
|
|
11
11
|
import { sendEmail } from "../../contact/email";
|
|
12
12
|
import { CONSENT_OTP_EXPIRY_MS, CONSENT_OTP_MAX_ATTEMPTS, hashOtp, maskEmail, generateOtpCode, buildConsentOtpEmail, enforceConsentOtpRateLimit, saveConsentOtp, readConsentOtp, patchConsentOtp, } from "../../auth/consent-otp";
|
|
13
13
|
import { addressesRepository } from "../../addresses/repository/addresses.repository";
|
|
14
|
+
const ERR_ADDRESS_NOT_FOUND = "Address not found.";
|
|
14
15
|
async function findUserAddress(userId, addressId) {
|
|
15
16
|
const address = await addressesRepository.findById(addressId);
|
|
16
17
|
if (!address || address.ownerType !== "user" || address.ownerId !== userId) {
|
|
@@ -24,7 +25,7 @@ import { resolveDate } from "../../../utils";
|
|
|
24
25
|
export async function sendCheckoutConsentOtp(userId, userEmail, addressId) {
|
|
25
26
|
const address = await findUserAddress(userId, addressId);
|
|
26
27
|
if (!address)
|
|
27
|
-
throw new ValidationError(
|
|
28
|
+
throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
|
|
28
29
|
await enforceConsentOtpRateLimit(userId);
|
|
29
30
|
const code = generateOtpCode();
|
|
30
31
|
const codeHash = hashOtp(code);
|
|
@@ -78,7 +79,7 @@ export async function grantCheckoutConsentViaSms(userId, userPhone, addressId) {
|
|
|
78
79
|
throw new ValidationError("No phone number registered on your account.");
|
|
79
80
|
const address = await findUserAddress(userId, addressId);
|
|
80
81
|
if (!address)
|
|
81
|
-
throw new ValidationError(
|
|
82
|
+
throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
|
|
82
83
|
const normalizePhone = (s) => s.replace(/[^0-9]/g, "").slice(-10);
|
|
83
84
|
if (normalizePhone(address.phone) !== normalizePhone(userPhone)) {
|
|
84
85
|
throw new ValidationError("Phone number does not match the shipping address. Please use email verification for this address.");
|
|
@@ -107,7 +108,7 @@ export async function grantCheckoutConsentViaSms(userId, userPhone, addressId) {
|
|
|
107
108
|
export async function grantAdminCheckoutBypass(userId, addressId, bypassingAdminUid) {
|
|
108
109
|
const address = await findUserAddress(userId, addressId);
|
|
109
110
|
if (!address)
|
|
110
|
-
throw new ValidationError(
|
|
111
|
+
throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
|
|
111
112
|
await saveConsentOtp(userId, addressId, {
|
|
112
113
|
codeHash: "",
|
|
113
114
|
expiresAt: new Date(Date.now() + CONSENT_OTP_EXPIRY_MS),
|
|
@@ -3,6 +3,29 @@ import { useCallback, useRef, useState } from "react";
|
|
|
3
3
|
import { getClientPaymentGateway } from "../../../contracts/client-payment-gateway";
|
|
4
4
|
import { useCheckout as useCheckoutApi } from "../hooks/useCheckoutApi";
|
|
5
5
|
import { usePaymentEvent, } from "../../payments/hooks/usePaymentEvent";
|
|
6
|
+
// --- Helpers ---------------------------------------------------------------
|
|
7
|
+
async function openGatewayStep(adapter, orderResponse, params) {
|
|
8
|
+
try {
|
|
9
|
+
await adapter.loadScript();
|
|
10
|
+
const response = await adapter.openPayment({
|
|
11
|
+
gatewayOrderId: orderResponse.gatewayOrderId,
|
|
12
|
+
publicKey: orderResponse.keyId,
|
|
13
|
+
amount: orderResponse.amount,
|
|
14
|
+
currency: orderResponse.currency,
|
|
15
|
+
merchantName: params.merchantName,
|
|
16
|
+
logoUrl: params.logoUrl,
|
|
17
|
+
prefill: params.prefill,
|
|
18
|
+
theme: params.themeColor ? { color: params.themeColor } : undefined,
|
|
19
|
+
});
|
|
20
|
+
return { response, cancelled: false, error: null };
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
if (err instanceof Error && err.message.toLowerCase().includes("cancel")) {
|
|
24
|
+
return { response: null, cancelled: true, error: null };
|
|
25
|
+
}
|
|
26
|
+
return { response: null, cancelled: false, error: err instanceof Error ? err : new Error("Payment gateway error") };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
6
29
|
// --- Hook ------------------------------------------------------------------
|
|
7
30
|
const INITIAL_STATE = {
|
|
8
31
|
step: "idle",
|
|
@@ -101,31 +124,17 @@ export function usePaymentCheckout(options) {
|
|
|
101
124
|
return null;
|
|
102
125
|
// Step 2: Open gateway modal
|
|
103
126
|
setState((s) => ({ ...s, step: "awaiting_payment" }));
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
gatewayOrderId: orderResponse.gatewayOrderId,
|
|
110
|
-
publicKey: orderResponse.keyId,
|
|
111
|
-
amount: orderResponse.amount,
|
|
112
|
-
currency: orderResponse.currency,
|
|
113
|
-
merchantName,
|
|
114
|
-
logoUrl,
|
|
115
|
-
prefill: params.prefill,
|
|
116
|
-
theme: themeColor ? { color: themeColor } : undefined,
|
|
117
|
-
});
|
|
127
|
+
const gatewayResult = await openGatewayStep(getAdapter(), orderResponse, { prefill: params.prefill, merchantName, logoUrl, themeColor });
|
|
128
|
+
if (gatewayResult.cancelled) {
|
|
129
|
+
setState((s) => ({ ...s, step: "cancelled", error: null }));
|
|
130
|
+
onCancel?.();
|
|
131
|
+
return null;
|
|
118
132
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
err.message.toLowerCase().includes("cancel")) {
|
|
122
|
-
setState((s) => ({ ...s, step: "cancelled", error: null }));
|
|
123
|
-
onCancel?.();
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
setError(err instanceof Error ? err : new Error("Payment gateway error"));
|
|
133
|
+
if (gatewayResult.error || !gatewayResult.response) {
|
|
134
|
+
setError(gatewayResult.error ?? new Error("Payment gateway error"));
|
|
127
135
|
return null;
|
|
128
136
|
}
|
|
137
|
+
const gatewayResponse = gatewayResult.response;
|
|
129
138
|
if (abortRef.current)
|
|
130
139
|
return null;
|
|
131
140
|
// Step 3: Verify payment on server
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FacetOption } from "../../filters/FilterFacetSection";
|
|
2
|
+
import type { UrlTable } from "../../filters/FilterPanel";
|
|
3
|
+
import type { AsyncFacetSectionProps } from "../../filters/AsyncFacetSection";
|
|
4
|
+
type LoadOptionsFn = AsyncFacetSectionProps["loadOptions"];
|
|
5
|
+
export interface ClassifiedFiltersProps {
|
|
6
|
+
table: UrlTable;
|
|
7
|
+
categoryOptions?: FacetOption[];
|
|
8
|
+
loadCategoryOptions?: LoadOptionsFn;
|
|
9
|
+
currencyPrefix?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function ClassifiedFilters({ table, categoryOptions, loadCategoryOptions, currencyPrefix, }: ClassifiedFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { FilterFacetSection } from "../../filters/FilterFacetSection";
|
|
4
|
+
import { AsyncFacetSection } from "../../filters/AsyncFacetSection";
|
|
5
|
+
import { RangeFilter } from "../../filters/RangeFilter";
|
|
6
|
+
import { Div } from "../../../ui";
|
|
7
|
+
const NEGOTIABLE_OPTIONS = [
|
|
8
|
+
{ value: "true", label: "Negotiable only" },
|
|
9
|
+
];
|
|
10
|
+
const SHIPPING_OPTIONS = [
|
|
11
|
+
{ value: "true", label: "Accepts shipping" },
|
|
12
|
+
];
|
|
13
|
+
export function ClassifiedFilters({ table, categoryOptions = [], loadCategoryOptions, currencyPrefix = "₹", }) {
|
|
14
|
+
const selectedCategories = table.get("category")
|
|
15
|
+
? table.get("category").split("|").filter(Boolean)
|
|
16
|
+
: [];
|
|
17
|
+
const selectedNegotiable = table.get("negotiable") ? [table.get("negotiable")] : [];
|
|
18
|
+
const selectedShipping = table.get("acceptsShipping") ? [table.get("acceptsShipping")] : [];
|
|
19
|
+
return (_jsxs(Div, { children: [loadCategoryOptions ? (_jsx(AsyncFacetSection, { title: "Category", loadOptions: loadCategoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), defaultCollapsed: false })) : categoryOptions.length > 0 ? (_jsx(FilterFacetSection, { title: "Category", options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), searchable: true, defaultCollapsed: categoryOptions.length > 6 })) : null, _jsxs("div", { className: "border-t border-[var(--appkit-color-border)] pt-4 mt-4", children: [_jsx("label", { className: "block text-sm font-medium text-[var(--appkit-color-text)] mb-2", children: "City / Area" }), _jsx("input", { type: "text", value: table.get("city") || "", onChange: (e) => table.set("city", e.target.value), placeholder: "e.g. Mumbai", className: "w-full rounded-lg border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-3 py-2 text-sm text-[var(--appkit-color-text)] placeholder-[var(--appkit-color-text-faint)] focus:outline-none focus:ring-2 focus:ring-[var(--appkit-color-primary)]" })] }), _jsx(FilterFacetSection, { title: "Negotiable", options: NEGOTIABLE_OPTIONS, selected: selectedNegotiable, onChange: (vals) => table.set("negotiable", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(FilterFacetSection, { title: "Shipping", options: SHIPPING_OPTIONS, selected: selectedShipping, onChange: (vals) => table.set("acceptsShipping", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(RangeFilter, { title: "Asking Price", minValue: table.get("minPrice"), maxValue: table.get("maxPrice"), onMinChange: (v) => table.set("minPrice", v), onMaxChange: (v) => table.set("maxPrice", v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 100, minPlaceholder: "Min price", maxPlaceholder: "Max price", defaultCollapsed: false })] }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
|
+
import { useProducts } from "../../products/hooks/useProducts";
|
|
6
|
+
import { BulkActionBar, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Text } from "../../../ui";
|
|
7
|
+
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
8
|
+
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
9
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
10
|
+
import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
|
|
11
|
+
import { pushWishlistOp } from "../../cart/utils/pending-ops";
|
|
12
|
+
import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
|
|
13
|
+
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
14
|
+
import { sortBy } from "../../../constants/sort";
|
|
15
|
+
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
16
|
+
import { ROUTES } from "../../../next";
|
|
17
|
+
import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
|
|
18
|
+
import { ClassifiedFilters } from "./ClassifiedFilters";
|
|
19
|
+
import { ACTION_ID } from "../../products/constants/action-defs";
|
|
20
|
+
import { useToast } from "../../../ui";
|
|
21
|
+
const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
|
|
22
|
+
const SORT_OPTIONS = [
|
|
23
|
+
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
|
|
24
|
+
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
|
|
25
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Price: Low to High" },
|
|
26
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE), label: "Price: High to Low" },
|
|
27
|
+
];
|
|
28
|
+
const FILTER_KEYS = [
|
|
29
|
+
TABLE_KEYS.CATEGORY,
|
|
30
|
+
TABLE_KEYS.CITY,
|
|
31
|
+
TABLE_KEYS.MIN_PRICE,
|
|
32
|
+
TABLE_KEYS.MAX_PRICE,
|
|
33
|
+
TABLE_KEYS.NEGOTIABLE,
|
|
34
|
+
TABLE_KEYS.ACCEPTS_SHIPPING,
|
|
35
|
+
];
|
|
36
|
+
export function ClassifiedIndexListing({ initialData }) {
|
|
37
|
+
const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
|
|
38
|
+
const { showToast } = useToast();
|
|
39
|
+
const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
|
|
40
|
+
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
41
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
42
|
+
const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
|
|
43
|
+
const localWishlist = useGuestWishlist();
|
|
44
|
+
const { categories } = useCategoryTree();
|
|
45
|
+
const categoryOptions = categoriesToFacetOptions(categories);
|
|
46
|
+
const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "classified").map((i) => i.itemId));
|
|
47
|
+
const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onFilterReset } = usePendingTable(table, FILTER_KEYS);
|
|
48
|
+
const openFilters = useCallback(() => {
|
|
49
|
+
onFilterReset();
|
|
50
|
+
setFilterOpen(true);
|
|
51
|
+
}, [onFilterReset]);
|
|
52
|
+
const applyFilters = useCallback(() => {
|
|
53
|
+
onFilterApply();
|
|
54
|
+
setFilterOpen(false);
|
|
55
|
+
}, [onFilterApply]);
|
|
56
|
+
const resetAll = useCallback(() => {
|
|
57
|
+
table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
|
|
58
|
+
onFilterClear();
|
|
59
|
+
setSearchInput("");
|
|
60
|
+
}, [table, onFilterClear]);
|
|
61
|
+
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
62
|
+
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
63
|
+
filterActiveCount > 0;
|
|
64
|
+
const params = {
|
|
65
|
+
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
66
|
+
category: table.get(TABLE_KEYS.CATEGORY) || undefined,
|
|
67
|
+
minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
|
|
68
|
+
maxPrice: table.get(TABLE_KEYS.MAX_PRICE) ? Number(table.get(TABLE_KEYS.MAX_PRICE)) : undefined,
|
|
69
|
+
sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
|
|
70
|
+
page: table.getNumber(TABLE_KEYS.PAGE, 1),
|
|
71
|
+
perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, 24),
|
|
72
|
+
listingType: "classified",
|
|
73
|
+
};
|
|
74
|
+
const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
75
|
+
const commitSearch = useCallback(() => {
|
|
76
|
+
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
77
|
+
}, [searchInput, table]);
|
|
78
|
+
const selection = useBulkSelection({ items: products, keyExtractor: (p) => p.id });
|
|
79
|
+
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
|
|
80
|
+
const wishlistActions = {
|
|
81
|
+
addToWishlist: (productId) => {
|
|
82
|
+
requireAuth(ACTION_ID.ADD_TO_WISHLIST, () => {
|
|
83
|
+
localWishlist.add(productId, "classified");
|
|
84
|
+
pushWishlistOp({ op: "add", itemId: productId, type: "classified" });
|
|
85
|
+
showToast("Added to wishlist", "success");
|
|
86
|
+
});
|
|
87
|
+
return Promise.resolve();
|
|
88
|
+
},
|
|
89
|
+
removeFromWishlist: (productId) => {
|
|
90
|
+
requireAuth(ACTION_ID.REMOVE_FROM_WISHLIST, () => {
|
|
91
|
+
localWishlist.remove(productId, "classified");
|
|
92
|
+
pushWishlistOp({ op: "remove", itemId: productId, type: "classified" });
|
|
93
|
+
showToast("Removed from wishlist", "info");
|
|
94
|
+
});
|
|
95
|
+
return Promise.resolve();
|
|
96
|
+
},
|
|
97
|
+
isWishlisted: (productId) => wishlistedIds.has(productId),
|
|
98
|
+
};
|
|
99
|
+
function handleToggleWishlist(id) {
|
|
100
|
+
if (wishlistActions.isWishlisted(id)) {
|
|
101
|
+
wishlistActions.removeFromWishlist(id);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
wishlistActions.addToWishlist(id);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search classified listings\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: (e) => { if (e.key === "Enter")
|
|
108
|
+
commitSearch(); }, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: (v) => { if (v !== "table")
|
|
109
|
+
setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] }), 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-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" })] })] }, i))) })) : products.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No classified listings found." })) : (_jsx("div", { className: view === "list" ? "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800" : gridClass, children: products.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.CLASSIFIED_DETAIL(product.id)), variant: view === "list" ? "list" : "grid", isWishlisted: wishlistActions.isWishlisted(product.id), onToggleWishlist: handleToggleWishlist, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ClassifiedFilters, { table: pendingTable, categoryOptions: categoryOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
110
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type SearchParams = Record<string, string | string[]>;
|
|
2
|
+
export interface ClassifiedListViewProps {
|
|
3
|
+
searchParams?: SearchParams;
|
|
4
|
+
}
|
|
5
|
+
export declare function ClassifiedListView({ searchParams }: ClassifiedListViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { productRepository } from "../../../repositories";
|
|
3
|
+
import { Container, Heading, Main, Section } from "../../../ui";
|
|
4
|
+
import { AdSlot } from "../../homepage/components/AdSlot";
|
|
5
|
+
import { parseListingSearchParams } from "../../../utils/listing-params";
|
|
6
|
+
import { ClassifiedIndexListing } from "./ClassifiedIndexListing";
|
|
7
|
+
const DEFAULT_PAGE = 1;
|
|
8
|
+
const DEFAULT_PAGE_SIZE = 24;
|
|
9
|
+
const DEFAULT_SORT = "-createdAt";
|
|
10
|
+
function sp(params, key) {
|
|
11
|
+
const v = params[key];
|
|
12
|
+
return Array.isArray(v) ? v[0] ?? "" : v ?? "";
|
|
13
|
+
}
|
|
14
|
+
function buildClassifiedFilters(params) {
|
|
15
|
+
const parts = ["status==published", "listingType==classified"];
|
|
16
|
+
const minPrice = sp(params, "minPrice");
|
|
17
|
+
const maxPrice = sp(params, "maxPrice");
|
|
18
|
+
if (minPrice)
|
|
19
|
+
parts.push(`price>=${minPrice}`);
|
|
20
|
+
if (maxPrice)
|
|
21
|
+
parts.push(`price<=${maxPrice}`);
|
|
22
|
+
const city = sp(params, "city");
|
|
23
|
+
if (city)
|
|
24
|
+
parts.push(`classifiedCity==${city}`);
|
|
25
|
+
const negotiable = sp(params, "negotiable");
|
|
26
|
+
if (negotiable === "true")
|
|
27
|
+
parts.push(`classifiedNegotiable==true`);
|
|
28
|
+
const acceptsShipping = sp(params, "acceptsShipping");
|
|
29
|
+
if (acceptsShipping === "true")
|
|
30
|
+
parts.push(`classifiedAcceptsShipping==true`);
|
|
31
|
+
return parts.join(",");
|
|
32
|
+
}
|
|
33
|
+
export async function ClassifiedListView({ searchParams = {} }) {
|
|
34
|
+
const std = parseListingSearchParams(searchParams);
|
|
35
|
+
const sort = std.sorts ?? DEFAULT_SORT;
|
|
36
|
+
const page = std.page ?? DEFAULT_PAGE;
|
|
37
|
+
const pageSize = std.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
38
|
+
const filters = buildClassifiedFilters(searchParams);
|
|
39
|
+
const result = await productRepository
|
|
40
|
+
.list({ filters, sorts: sort, page, pageSize })
|
|
41
|
+
.catch(() => null);
|
|
42
|
+
return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8 text-3xl font-semibold text-zinc-900", children: "Classifieds" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(ClassifiedIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
|
|
43
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ClassifiedListView } from "./ClassifiedListView";
|
|
2
|
+
export type { ClassifiedListViewProps } from "./ClassifiedListView";
|
|
3
|
+
export { ClassifiedIndexListing } from "./ClassifiedIndexListing";
|
|
4
|
+
export type { ClassifiedIndexListingProps } from "./ClassifiedIndexListing";
|
|
5
|
+
export { ClassifiedFilters } from "./ClassifiedFilters";
|
|
6
|
+
export type { ClassifiedFiltersProps } from "./ClassifiedFilters";
|
|
@@ -10,22 +10,26 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { NextResponse } from "next/server.js";
|
|
12
12
|
import { getProviders } from "../../../../contracts";
|
|
13
|
+
const ERR_COLLECTION_SLUG_REQUIRED = "Collection slug is required";
|
|
14
|
+
const ERR_DB_NOT_CONFIGURED = "Database provider not configured";
|
|
15
|
+
const ERR_COLLECTION_NOT_FOUND = "Collection not found";
|
|
16
|
+
const ERR_INTERNAL_SERVER_ERROR = "Internal server error";
|
|
13
17
|
export async function GET(request, { params }) {
|
|
14
18
|
try {
|
|
15
19
|
const { slug } = await params;
|
|
16
20
|
if (!slug) {
|
|
17
|
-
return NextResponse.json({ success: false, error:
|
|
21
|
+
return NextResponse.json({ success: false, error: ERR_COLLECTION_SLUG_REQUIRED }, { status: 400 });
|
|
18
22
|
}
|
|
19
23
|
const { db } = getProviders();
|
|
20
24
|
if (!db)
|
|
21
|
-
return NextResponse.json({ success: false, error:
|
|
25
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_CONFIGURED }, { status: 503 });
|
|
22
26
|
const repo = db.getRepository("collections");
|
|
23
27
|
const result = await repo.findAll({
|
|
24
28
|
filters: `slug==${slug}`,
|
|
25
29
|
perPage: 1,
|
|
26
30
|
});
|
|
27
31
|
if (!result.data[0]) {
|
|
28
|
-
return NextResponse.json({ success: false, error:
|
|
32
|
+
return NextResponse.json({ success: false, error: ERR_COLLECTION_NOT_FOUND }, { status: 404 });
|
|
29
33
|
}
|
|
30
34
|
return NextResponse.json({
|
|
31
35
|
success: true,
|
|
@@ -35,7 +39,7 @@ export async function GET(request, { params }) {
|
|
|
35
39
|
catch (error) {
|
|
36
40
|
return NextResponse.json({
|
|
37
41
|
success: false,
|
|
38
|
-
error: error instanceof Error ? error.message :
|
|
42
|
+
error: error instanceof Error ? error.message : ERR_INTERNAL_SERVER_ERROR,
|
|
39
43
|
}, { status: 500 });
|
|
40
44
|
}
|
|
41
45
|
}
|
|
@@ -43,19 +47,19 @@ export async function PATCH(request, { params }) {
|
|
|
43
47
|
try {
|
|
44
48
|
const { slug } = await params;
|
|
45
49
|
if (!slug) {
|
|
46
|
-
return NextResponse.json({ success: false, error:
|
|
50
|
+
return NextResponse.json({ success: false, error: ERR_COLLECTION_SLUG_REQUIRED }, { status: 400 });
|
|
47
51
|
}
|
|
48
52
|
const body = await request.json();
|
|
49
53
|
const { db } = getProviders();
|
|
50
54
|
if (!db)
|
|
51
|
-
return NextResponse.json({ success: false, error:
|
|
55
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_CONFIGURED }, { status: 503 });
|
|
52
56
|
const repo = db.getRepository("collections");
|
|
53
57
|
const result = await repo.findAll({
|
|
54
58
|
filters: `slug==${slug}`,
|
|
55
59
|
perPage: 1,
|
|
56
60
|
});
|
|
57
61
|
if (!result.data[0]) {
|
|
58
|
-
return NextResponse.json({ success: false, error:
|
|
62
|
+
return NextResponse.json({ success: false, error: ERR_COLLECTION_NOT_FOUND }, { status: 404 });
|
|
59
63
|
}
|
|
60
64
|
const updated = await repo.update(result.data[0].id, body);
|
|
61
65
|
return NextResponse.json({
|
|
@@ -66,7 +70,7 @@ export async function PATCH(request, { params }) {
|
|
|
66
70
|
catch (error) {
|
|
67
71
|
return NextResponse.json({
|
|
68
72
|
success: false,
|
|
69
|
-
error: error instanceof Error ? error.message :
|
|
73
|
+
error: error instanceof Error ? error.message : ERR_INTERNAL_SERVER_ERROR,
|
|
70
74
|
}, { status: 500 });
|
|
71
75
|
}
|
|
72
76
|
}
|
|
@@ -74,18 +78,18 @@ export async function DELETE(request, { params }) {
|
|
|
74
78
|
try {
|
|
75
79
|
const { slug } = await params;
|
|
76
80
|
if (!slug) {
|
|
77
|
-
return NextResponse.json({ success: false, error:
|
|
81
|
+
return NextResponse.json({ success: false, error: ERR_COLLECTION_SLUG_REQUIRED }, { status: 400 });
|
|
78
82
|
}
|
|
79
83
|
const { db } = getProviders();
|
|
80
84
|
if (!db)
|
|
81
|
-
return NextResponse.json({ success: false, error:
|
|
85
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_CONFIGURED }, { status: 503 });
|
|
82
86
|
const repo = db.getRepository("collections");
|
|
83
87
|
const result = await repo.findAll({
|
|
84
88
|
filters: `slug==${slug}`,
|
|
85
89
|
perPage: 1,
|
|
86
90
|
});
|
|
87
91
|
if (!result.data[0]) {
|
|
88
|
-
return NextResponse.json({ success: false, error:
|
|
92
|
+
return NextResponse.json({ success: false, error: ERR_COLLECTION_NOT_FOUND }, { status: 404 });
|
|
89
93
|
}
|
|
90
94
|
await repo.delete(result.data[0].id);
|
|
91
95
|
return NextResponse.json({
|
|
@@ -96,7 +100,7 @@ export async function DELETE(request, { params }) {
|
|
|
96
100
|
catch (error) {
|
|
97
101
|
return NextResponse.json({
|
|
98
102
|
success: false,
|
|
99
|
-
error: error instanceof Error ? error.message :
|
|
103
|
+
error: error instanceof Error ? error.message : ERR_INTERNAL_SERVER_ERROR,
|
|
100
104
|
}, { status: 500 });
|
|
101
105
|
}
|
|
102
106
|
}
|
|
@@ -26,13 +26,13 @@ export declare const collectionItemSchema: z.ZodObject<{
|
|
|
26
26
|
}, "strip", z.ZodTypeAny, {
|
|
27
27
|
sortOrder: number;
|
|
28
28
|
title: string;
|
|
29
|
-
active: boolean;
|
|
30
29
|
slug: string;
|
|
30
|
+
active: boolean;
|
|
31
31
|
createdAt?: string | undefined;
|
|
32
32
|
image?: string | undefined;
|
|
33
|
-
subtitle?: string | undefined;
|
|
34
33
|
description?: string | undefined;
|
|
35
34
|
brandSlug?: string | undefined;
|
|
35
|
+
subtitle?: string | undefined;
|
|
36
36
|
productCount?: number | undefined;
|
|
37
37
|
bannerImage?: string | undefined;
|
|
38
38
|
franchiseSlug?: string | undefined;
|
|
@@ -43,10 +43,10 @@ export declare const collectionItemSchema: z.ZodObject<{
|
|
|
43
43
|
createdAt?: string | undefined;
|
|
44
44
|
sortOrder?: number | undefined;
|
|
45
45
|
image?: string | undefined;
|
|
46
|
-
active?: boolean | undefined;
|
|
47
|
-
subtitle?: string | undefined;
|
|
48
46
|
description?: string | undefined;
|
|
49
47
|
brandSlug?: string | undefined;
|
|
48
|
+
active?: boolean | undefined;
|
|
49
|
+
subtitle?: string | undefined;
|
|
50
50
|
productCount?: number | undefined;
|
|
51
51
|
bannerImage?: string | undefined;
|
|
52
52
|
franchiseSlug?: string | undefined;
|
|
@@ -61,8 +61,8 @@ export declare const collectionListItemSchema: z.ZodObject<{
|
|
|
61
61
|
active: z.ZodDefault<z.ZodBoolean>;
|
|
62
62
|
}, "strip", z.ZodTypeAny, {
|
|
63
63
|
title: string;
|
|
64
|
-
active: boolean;
|
|
65
64
|
slug: string;
|
|
65
|
+
active: boolean;
|
|
66
66
|
image?: string | undefined;
|
|
67
67
|
subtitle?: string | undefined;
|
|
68
68
|
productCount?: number | undefined;
|
|
@@ -10,15 +10,18 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { NextResponse } from "next/server.js";
|
|
12
12
|
import { getProviders } from "../../../../contracts";
|
|
13
|
+
const ERR_CONSULTATION_ID_REQUIRED = "Consultation ID is required";
|
|
14
|
+
const ERR_DB_NOT_CONFIGURED = "Database provider not configured";
|
|
15
|
+
const ERR_INTERNAL_SERVER_ERROR = "Internal server error";
|
|
13
16
|
export async function GET(request, { params }) {
|
|
14
17
|
try {
|
|
15
18
|
const { id } = await params;
|
|
16
19
|
if (!id) {
|
|
17
|
-
return NextResponse.json({ success: false, error:
|
|
20
|
+
return NextResponse.json({ success: false, error: ERR_CONSULTATION_ID_REQUIRED }, { status: 400 });
|
|
18
21
|
}
|
|
19
22
|
const { db } = getProviders();
|
|
20
23
|
if (!db)
|
|
21
|
-
return NextResponse.json({ success: false, error:
|
|
24
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_CONFIGURED }, { status: 503 });
|
|
22
25
|
const repo = db.getRepository("consultations");
|
|
23
26
|
const consultation = await repo.findById(id);
|
|
24
27
|
if (!consultation) {
|
|
@@ -32,7 +35,7 @@ export async function GET(request, { params }) {
|
|
|
32
35
|
catch (error) {
|
|
33
36
|
return NextResponse.json({
|
|
34
37
|
success: false,
|
|
35
|
-
error: error instanceof Error ? error.message :
|
|
38
|
+
error: error instanceof Error ? error.message : ERR_INTERNAL_SERVER_ERROR,
|
|
36
39
|
}, { status: 500 });
|
|
37
40
|
}
|
|
38
41
|
}
|
|
@@ -40,12 +43,12 @@ export async function PATCH(request, { params }) {
|
|
|
40
43
|
try {
|
|
41
44
|
const { id } = await params;
|
|
42
45
|
if (!id) {
|
|
43
|
-
return NextResponse.json({ success: false, error:
|
|
46
|
+
return NextResponse.json({ success: false, error: ERR_CONSULTATION_ID_REQUIRED }, { status: 400 });
|
|
44
47
|
}
|
|
45
48
|
const body = await request.json();
|
|
46
49
|
const { db } = getProviders();
|
|
47
50
|
if (!db)
|
|
48
|
-
return NextResponse.json({ success: false, error:
|
|
51
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_CONFIGURED }, { status: 503 });
|
|
49
52
|
const repo = db.getRepository("consultations");
|
|
50
53
|
const updated = await repo.update(id, body);
|
|
51
54
|
if (!updated) {
|
|
@@ -59,7 +62,7 @@ export async function PATCH(request, { params }) {
|
|
|
59
62
|
catch (error) {
|
|
60
63
|
return NextResponse.json({
|
|
61
64
|
success: false,
|
|
62
|
-
error: error instanceof Error ? error.message :
|
|
65
|
+
error: error instanceof Error ? error.message : ERR_INTERNAL_SERVER_ERROR,
|
|
63
66
|
}, { status: 500 });
|
|
64
67
|
}
|
|
65
68
|
}
|
|
@@ -67,11 +70,11 @@ export async function DELETE(request, { params }) {
|
|
|
67
70
|
try {
|
|
68
71
|
const { id } = await params;
|
|
69
72
|
if (!id) {
|
|
70
|
-
return NextResponse.json({ success: false, error:
|
|
73
|
+
return NextResponse.json({ success: false, error: ERR_CONSULTATION_ID_REQUIRED }, { status: 400 });
|
|
71
74
|
}
|
|
72
75
|
const { db } = getProviders();
|
|
73
76
|
if (!db)
|
|
74
|
-
return NextResponse.json({ success: false, error:
|
|
77
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_CONFIGURED }, { status: 503 });
|
|
75
78
|
const repo = db.getRepository("consultations");
|
|
76
79
|
await repo.delete(id);
|
|
77
80
|
return NextResponse.json({
|
|
@@ -82,7 +85,7 @@ export async function DELETE(request, { params }) {
|
|
|
82
85
|
catch (error) {
|
|
83
86
|
return NextResponse.json({
|
|
84
87
|
success: false,
|
|
85
|
-
error: error instanceof Error ? error.message :
|
|
88
|
+
error: error instanceof Error ? error.message : ERR_INTERNAL_SERVER_ERROR,
|
|
86
89
|
}, { status: 500 });
|
|
87
90
|
}
|
|
88
91
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { Button, Div, Input, Row, Select, Textarea } from "../../../ui";
|
|
5
|
+
const CLS_INPUT = "w-full rounded-md border border-neutral-300 px-3 py-2 text-sm";
|
|
5
6
|
const TIME_SLOTS = [
|
|
6
7
|
"9:00 AM",
|
|
7
8
|
"10:00 AM",
|
|
@@ -42,10 +43,10 @@ export function ConsultationForm({ onSubmit, isPending, concerns = [], }) {
|
|
|
42
43
|
e.preventDefault();
|
|
43
44
|
await onSubmit(form);
|
|
44
45
|
}
|
|
45
|
-
return (_jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [_jsx(Input, { name: "name", type: "text", placeholder: "Full name", value: form.name, onChange: handleChange, required: true, className:
|
|
46
|
+
return (_jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [_jsx(Input, { name: "name", type: "text", placeholder: "Full name", value: form.name, onChange: handleChange, required: true, className: CLS_INPUT }), _jsx(Input, { name: "email", type: "email", placeholder: "Email address", value: form.email, onChange: handleChange, required: true, className: CLS_INPUT }), _jsx(Input, { name: "phone", type: "tel", placeholder: "Phone number", value: form.phone, onChange: handleChange, required: true, className: CLS_INPUT }), concerns.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", children: concerns.map((c) => (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => toggleConcern(c), className: `rounded-full border px-3 py-1 text-xs transition ${form.concern.includes(c)
|
|
46
47
|
? "border-primary bg-primary text-white"
|
|
47
48
|
: "border-neutral-300 text-neutral-700 hover:border-primary"}`, children: c }, c))) })), _jsxs(Div, { className: "flex gap-3", children: [_jsx(Input, { name: "preferredDate", type: "date", value: form.preferredDate, onChange: handleChange, required: true, className: "flex-1 rounded-md border border-neutral-300 px-3 py-2 text-sm" }), _jsx(Select, { value: form.preferredTime, onValueChange: (value) => handleValueChange("preferredTime", value), options: TIME_SLOTS.map((t) => ({ value: t, label: t })), className: "flex-1 rounded-md border border-neutral-300 px-3 py-2 text-sm" })] }), _jsx(Select, { value: form.mode, onValueChange: (value) => handleValueChange("mode", value), options: [
|
|
48
49
|
{ value: "remote", label: "Remote (Video Call)" },
|
|
49
50
|
{ value: "in-person", label: "In-Person" },
|
|
50
|
-
], className:
|
|
51
|
+
], className: CLS_INPUT }), _jsx(Textarea, { name: "message", placeholder: "Any additional notes (optional)", value: form.message, onChange: handleChange, rows: 3, className: CLS_INPUT }), _jsx(Button, { type: "submit", disabled: isPending, variant: "primary", className: "w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:bg-primary/90 disabled:opacity-60", children: isPending ? "Booking..." : "Book Consultation" })] }));
|
|
51
52
|
}
|