@mohasinac/appkit 2.8.2 → 2.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/classified/ClassifiedDetailView.js +7 -1
- package/dist/_internal/client/features/digital-code/CodeRevealPanel.js +4 -1
- package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +7 -1
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +3 -2
- package/dist/_internal/client/features/live/LiveItemDetailView.js +11 -2
- package/dist/_internal/client/scaffolds/AppShell.js +2 -1
- package/dist/_internal/client/scaffolds/DashboardScaffold.js +2 -1
- package/dist/_internal/server/features/checkout/actions.js +40 -39
- package/dist/_internal/shared/actions/action-registry.js +6 -0
- package/dist/_internal/shared/config/index.d.ts +1 -1
- package/dist/_internal/shared/config/schema.d.ts +0 -8
- package/dist/client-entry.d.ts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/configs/next.js +12 -22
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/core/hooks/useSiteSettings.d.ts +1 -1
- package/dist/features/about/components/AboutView.js +5 -2
- package/dist/features/about/components/FAQPageView.js +3 -3
- package/dist/features/about/components/FeesView.js +13 -5
- package/dist/features/about/components/HelpPageView.js +9 -5
- package/dist/features/about/components/HowAuctionsWorkView.js +9 -5
- package/dist/features/about/components/HowCheckoutWorksView.js +9 -5
- package/dist/features/about/components/HowOffersWorkView.js +10 -5
- package/dist/features/about/components/HowOrdersWorkView.js +10 -5
- package/dist/features/about/components/HowPayoutsWorkView.js +9 -5
- package/dist/features/about/components/HowPreOrdersWorkView.js +9 -5
- package/dist/features/about/components/HowReviewsWorkView.js +9 -5
- package/dist/features/about/components/PolicyPageView.js +3 -3
- package/dist/features/about/components/PublicProfileView.js +11 -4
- package/dist/features/about/components/SecurityPrivacyView.js +8 -4
- package/dist/features/about/components/ShippingPolicyView.js +3 -2
- package/dist/features/about/components/TrackOrderView.js +2 -2
- package/dist/features/about/components/UnauthorizedView.js +3 -3
- package/dist/features/account/components/AddressBook.js +1 -1
- package/dist/features/account/components/AddressForm.js +2 -2
- package/dist/features/account/components/AddressInlineSelect.d.ts +24 -0
- package/dist/features/account/components/AddressInlineSelect.js +78 -0
- package/dist/features/account/components/AddressSelectorCreate.d.ts +2 -1
- package/dist/features/account/components/AddressSelectorCreate.js +5 -32
- package/dist/features/account/components/AddressesIndexListing.js +3 -3
- package/dist/features/account/components/ChatWindow.js +2 -1
- package/dist/features/account/components/NotificationBell.js +13 -7
- package/dist/features/account/components/NotificationPreferencesPanel.js +4 -1
- package/dist/features/account/components/UserOffersPanel.js +2 -1
- package/dist/features/account/components/UserSidebar.js +11 -7
- package/dist/features/account/components/UserSupportView.js +13 -4
- package/dist/features/account/components/index.d.ts +2 -0
- package/dist/features/account/components/index.js +1 -0
- package/dist/features/account/hooks/useAccount.d.ts +2 -2
- package/dist/features/account/hooks/useAddressSelector.d.ts +1 -1
- package/dist/features/account/hooks/useAddresses.d.ts +2 -2
- package/dist/features/account/hooks/useNotifications.d.ts +1 -1
- package/dist/features/account/hooks/useProfile.d.ts +1 -1
- package/dist/features/account/hooks/usePublicProfile.d.ts +2 -2
- package/dist/features/addresses/repository/addresses.repository.d.ts +1 -0
- package/dist/features/addresses/repository/addresses.repository.js +13 -0
- package/dist/features/admin/components/AdminAdEditorView.js +8 -5
- package/dist/features/admin/components/AdminAddressEditorView.js +5 -5
- package/dist/features/admin/components/AdminAdsView.js +10 -7
- package/dist/features/admin/components/AdminBlogEditorView.js +15 -9
- package/dist/features/admin/components/AdminBrandEditorView.js +10 -4
- package/dist/features/admin/components/AdminBrandsView.js +4 -4
- package/dist/features/admin/components/AdminBundleEditorView.js +4 -5
- package/dist/features/admin/components/AdminBundlesView.js +3 -3
- package/dist/features/admin/components/AdminCarouselEditorView.js +6 -3
- package/dist/features/admin/components/AdminCarouselView.js +6 -6
- package/dist/features/admin/components/AdminCatalogGuideView.js +2 -1
- package/dist/features/admin/components/AdminCategoriesView.js +5 -5
- package/dist/features/admin/components/AdminCategoryEditorView.js +11 -5
- package/dist/features/admin/components/AdminContactEditorView.js +8 -1
- package/dist/features/admin/components/AdminCouponEditorView.js +23 -5
- package/dist/features/admin/components/AdminEmployeeEditorView.js +7 -4
- package/dist/features/admin/components/AdminFaqEditorView.js +9 -3
- package/dist/features/admin/components/AdminFaqsView.js +4 -4
- package/dist/features/admin/components/AdminFeatureEditorView.js +7 -1
- package/dist/features/admin/components/AdminFeatureFlagsView.js +4 -1
- package/dist/features/admin/components/AdminFilterBar.js +4 -4
- package/dist/features/admin/components/AdminGroupedListingsView.js +65 -30
- package/dist/features/admin/components/AdminGuideHubView.js +4 -1
- package/dist/features/admin/components/AdminMediaView.js +4 -1
- package/dist/features/admin/components/AdminNavigationView.js +7 -4
- package/dist/features/admin/components/AdminNotificationsView.js +2 -2
- package/dist/features/admin/components/AdminOrderEditorView.js +2 -2
- package/dist/features/admin/components/AdminPageHeader.js +5 -2
- package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -5
- package/dist/features/admin/components/AdminProductEditorView.js +12 -5
- package/dist/features/admin/components/AdminProductsView.js +4 -4
- package/dist/features/admin/components/AdminReviewsView.js +2 -2
- package/dist/features/admin/components/AdminScammerEditorView.js +6 -2
- package/dist/features/admin/components/AdminScammersView.js +3 -3
- package/dist/features/admin/components/AdminSectionsView.js +20 -22
- package/dist/features/admin/components/AdminSessionsManager.js +2 -1
- package/dist/features/admin/components/AdminSessionsView.js +2 -2
- package/dist/features/admin/components/AdminSidebar.js +11 -7
- package/dist/features/admin/components/AdminSiteConfigGuideView.js +2 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +5 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +2 -2
- package/dist/features/admin/components/AdminStoreEditorView.js +5 -5
- package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +3 -3
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +10 -3
- package/dist/features/admin/components/AdminSupportTicketsView.js +3 -3
- package/dist/features/admin/components/AdminTrustGuideView.js +3 -3
- package/dist/features/admin/components/AdminUsersGuideView.js +3 -3
- package/dist/features/admin/components/AdminViewCards.js +12 -6
- package/dist/features/admin/components/BrandQuickCreateForm.js +2 -2
- package/dist/features/admin/components/CategoryQuickCreateForm.js +2 -2
- package/dist/features/admin/components/DashboardStats.js +9 -3
- package/dist/features/admin/components/DataListingView.js +3 -3
- package/dist/features/admin/components/DataTable.js +11 -7
- package/dist/features/admin/components/DrawerFormFooter.js +2 -2
- package/dist/features/admin/components/QuickEditMenu.js +2 -1
- package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +5 -1
- package/dist/features/admin/hooks/useAdmin.d.ts +1 -1
- package/dist/features/admin/hooks/useAdminSectionsListing.d.ts +1 -1
- package/dist/features/admin/hooks/useChat.d.ts +1 -1
- package/dist/features/auctions/components/AuctionBidsTable.js +4 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +14 -6
- package/dist/features/auctions/components/CollapsibleBidHistory.js +5 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +9 -3
- package/dist/features/auctions/components/MarketplaceAuctionGrid.js +8 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +4 -1
- package/dist/features/auctions/hooks/useAuctions.d.ts +8 -8
- package/dist/features/auth/components/RegisterForm.js +2 -2
- package/dist/features/auth/components/VerifyEmailView.js +5 -2
- package/dist/features/auth/hooks/useAuth.d.ts +2 -2
- package/dist/features/before-after/components/BeforeAfterSlider.js +1 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +8 -1
- package/dist/features/blog/components/BlogIndexListing.js +11 -5
- package/dist/features/blog/components/BlogListView.js +11 -3
- package/dist/features/blog/components/BlogPostView.js +10 -3
- package/dist/features/cart/components/CartDrawer.js +11 -3
- package/dist/features/cart/components/CartSummary.js +2 -2
- package/dist/features/cart/components/CheckoutOtpModal.js +2 -1
- package/dist/features/cart/hooks/useCart.d.ts +1 -1
- package/dist/features/cart/hooks/useCartQuery.d.ts +1 -1
- package/dist/features/cart/hooks/useCheckoutReadQueries.d.ts +2 -2
- package/dist/features/cart/hooks/useOrder.d.ts +2 -2
- package/dist/features/categories/components/BrandDetailPageView.js +5 -2
- package/dist/features/categories/components/BrandDetailTabs.js +5 -1
- package/dist/features/categories/components/BundleDetailView.js +6 -3
- package/dist/features/categories/components/BundleDynamicRuleEditor.js +3 -1
- package/dist/features/categories/components/BundleItemsPicker.js +10 -3
- package/dist/features/categories/components/CategoriesIndexListing.js +12 -6
- package/dist/features/categories/components/CategoryBundlesListing.js +5 -2
- package/dist/features/categories/components/CategoryDetailPageView.js +6 -2
- package/dist/features/categories/components/CategoryDetailTabs.js +5 -1
- package/dist/features/categories/components/CategoryForm.js +6 -3
- package/dist/features/categories/components/CategoryGrid.js +9 -2
- package/dist/features/categories/components/CategoryProductsListing.js +10 -4
- package/dist/features/categories/components/CategorySelectorCreate.js +3 -3
- package/dist/features/categories/components/CategorySortSelect.js +1 -1
- package/dist/features/categories/components/CategoryStoresListing.js +3 -3
- package/dist/features/categories/components/CategoryTableColumns.js +10 -6
- package/dist/features/categories/components/CategoryTree.js +1 -1
- package/dist/features/categories/hooks/useCategories.d.ts +3 -3
- package/dist/features/categories/hooks/useCategorySelector.d.ts +3 -3
- package/dist/features/categories/hooks/useCategoryTree.d.ts +1 -1
- package/dist/features/checkout/hooks/useCheckoutApi.d.ts +2 -2
- package/dist/features/classified/components/ClassifiedFilters.js +2 -2
- package/dist/features/classified/components/ClassifiedIndexListing.js +9 -3
- package/dist/features/collections/components/CollectionCard.js +2 -1
- package/dist/features/collections/hooks/useCollections.d.ts +2 -2
- package/dist/features/contact/components/ContactForm.js +4 -2
- package/dist/features/contact/components/ContactInfoSidebar.js +4 -1
- package/dist/features/copilot/components/AdminCopilotView.js +13 -5
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +9 -3
- package/dist/features/events/components/AdminEventEditorView.js +62 -40
- package/dist/features/events/components/AdminEventEntriesView.js +9 -6
- package/dist/features/events/components/EventCard.js +7 -1
- package/dist/features/events/components/EventFormDrawer.js +2 -1
- package/dist/features/events/components/EventLeaderboard.js +4 -4
- package/dist/features/events/components/EventOfferCard.js +4 -1
- package/dist/features/events/components/EventParticipateView.js +3 -2
- package/dist/features/events/components/EventPollWidget.js +6 -5
- package/dist/features/events/components/EventRaffleEntryForm.js +5 -2
- package/dist/features/events/components/EventRaffleWinnerView.js +7 -1
- package/dist/features/events/components/EventRafflesSection.js +2 -2
- package/dist/features/events/components/EventSaleBanner.js +5 -2
- package/dist/features/events/components/EventsIndexListing.js +8 -2
- package/dist/features/events/components/EventsListView.js +4 -1
- package/dist/features/events/components/SpinWheelView.js +7 -2
- package/dist/features/events/hooks/useEvent.d.ts +3 -3
- package/dist/features/events/hooks/useEvents.d.ts +1 -1
- package/dist/features/events/schemas/firestore.d.ts +1 -0
- package/dist/features/events/types/index.d.ts +1 -0
- package/dist/features/faq/components/ContactCTA.js +4 -1
- package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
- package/dist/features/faq/components/FAQHelpfulButtons.js +8 -5
- package/dist/features/faq/components/FAQPageContent.js +5 -5
- package/dist/features/faq/components/FAQSortDropdown.js +2 -2
- package/dist/features/faq/hooks/useFAQs.d.ts +1 -1
- package/dist/features/faq/hooks/useFaqList.d.ts +15 -15
- package/dist/features/filters/AsyncFacetSection.js +4 -2
- package/dist/features/filters/FilterFacetSection.js +14 -6
- package/dist/features/filters/RangeFilter.js +4 -2
- package/dist/features/filters/SwitchFilter.js +2 -1
- package/dist/features/forms/Checkbox.js +1 -1
- package/dist/features/forms/Radio.js +1 -1
- package/dist/features/forms/Slider.js +1 -1
- package/dist/features/homepage/components/AdSlot.js +4 -4
- package/dist/features/homepage/components/AdvertisementBanner.js +13 -4
- package/dist/features/homepage/components/AnnouncementBar.js +2 -1
- package/dist/features/homepage/components/BeforeAfterCard.js +8 -2
- package/dist/features/homepage/components/BrandsSection.js +6 -3
- package/dist/features/homepage/components/CTABannerSection.js +2 -2
- package/dist/features/homepage/components/CharacterHotspot.js +5 -2
- package/dist/features/homepage/components/CharacterHotspotForm.js +8 -4
- package/dist/features/homepage/components/CustomCardsSection.js +13 -7
- package/dist/features/homepage/components/CustomerReviewsSection.js +9 -3
- package/dist/features/homepage/components/FAQSection.js +7 -4
- package/dist/features/homepage/components/FeaturedBundlesSection.js +8 -2
- package/dist/features/homepage/components/FeaturedResultsSection.js +1 -1
- package/dist/features/homepage/components/GoogleReviewsSection.js +14 -8
- package/dist/features/homepage/components/HeroCarousel.js +1 -1
- package/dist/features/homepage/components/HomepageSkeleton.js +7 -1
- package/dist/features/homepage/components/HowItWorksInfoView.js +2 -2
- package/dist/features/homepage/components/HowItWorksSection.js +2 -2
- package/dist/features/homepage/components/NewsletterBanner.js +3 -2
- package/dist/features/homepage/components/NewsletterSection.js +5 -2
- package/dist/features/homepage/components/PromoGrid.js +4 -1
- package/dist/features/homepage/components/SectionCarousel.js +7 -4
- package/dist/features/homepage/components/SecurityHighlightsSection.js +3 -3
- package/dist/features/homepage/components/ShopByCategorySection.js +10 -4
- package/dist/features/homepage/components/SiteFeaturesSection.js +5 -2
- package/dist/features/homepage/components/SocialFeedSection.js +4 -4
- package/dist/features/homepage/components/SocialPostCard.js +6 -3
- package/dist/features/homepage/components/TrustFeaturesSection.js +5 -5
- package/dist/features/homepage/components/TrustIndicatorsSection.js +5 -2
- package/dist/features/homepage/components/WelcomeSection.js +1 -1
- package/dist/features/homepage/components/WhatsAppCommunitySection.js +3 -3
- package/dist/features/homepage/hooks/useBlogArticles.d.ts +1 -1
- package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +1 -1
- package/dist/features/homepage/hooks/useFeaturedPreOrders.d.ts +1 -1
- package/dist/features/homepage/hooks/useFeaturedProducts.d.ts +1 -1
- package/dist/features/homepage/hooks/useFeaturedStores.d.ts +1 -1
- package/dist/features/homepage/hooks/useHeroCarousel.d.ts +1 -1
- package/dist/features/homepage/hooks/useHomepageEvents.d.ts +1 -1
- package/dist/features/homepage/hooks/useHomepageReviews.d.ts +1 -1
- package/dist/features/homepage/hooks/useHomepageSections.d.ts +1 -1
- package/dist/features/homepage/hooks/useTopBrands.d.ts +1 -1
- package/dist/features/homepage/hooks/useTopCategories.d.ts +1 -1
- package/dist/features/layout/AppLayoutShell.js +2 -1
- package/dist/features/layout/BackToTop.js +1 -1
- package/dist/features/layout/BottomActions.js +4 -3
- package/dist/features/layout/BottomNavItem.js +1 -1
- package/dist/features/layout/BottomSheet.js +4 -1
- package/dist/features/layout/FooterLayout.js +1 -1
- package/dist/features/layout/NavbarLayout.js +4 -1
- package/dist/features/layout/SidebarLayout.js +5 -2
- package/dist/features/live/components/LiveItemFilters.js +2 -2
- package/dist/features/live/components/LiveItemsIndexListing.js +9 -3
- package/dist/features/loyalty/components/CoinsDisplay.js +4 -2
- package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +1 -1
- package/dist/features/media/AvatarUpload.js +2 -2
- package/dist/features/media/MediaLightbox.js +3 -2
- package/dist/features/media/modals/VideoThumbnailSelector.js +4 -1
- package/dist/features/media/modals/VideoTrimModal.js +1 -1
- package/dist/features/media/upload/CameraCapture.js +1 -1
- package/dist/features/media/upload/ImageUpload.js +4 -1
- package/dist/features/media/upload/MediaUploadField.js +11 -2
- package/dist/features/media/upload/MediaUploadList.js +5 -1
- package/dist/features/orders/components/MarketplaceOrderCard.js +2 -2
- package/dist/features/orders/components/OrdersList.js +2 -1
- package/dist/features/orders/components/RefundHistoryTable.js +4 -1
- package/dist/features/orders/components/RefundRequestView.js +2 -1
- package/dist/features/orders/hooks/useOrders.d.ts +2 -2
- package/dist/features/payments/hooks/usePayments.d.ts +1 -1
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +5 -1
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +3 -3
- package/dist/features/pre-orders/components/PreOrderBadge.js +2 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +16 -7
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +32 -24
- package/dist/features/pre-orders/hooks/usePreOrders.d.ts +2 -2
- package/dist/features/products/columns/productTableColumns.js +9 -4
- package/dist/features/products/components/AuctionsIndexListing.js +9 -3
- package/dist/features/products/components/BidHistory.js +4 -4
- package/dist/features/products/components/CompareOverlay.js +10 -2
- package/dist/features/products/components/CustomFieldsEditor.js +2 -1
- package/dist/features/products/components/CustomSectionTabContent.js +2 -2
- package/dist/features/products/components/CustomSectionsEditor.js +3 -2
- package/dist/features/products/components/GroupSettingsPanel.js +34 -26
- package/dist/features/products/components/MakeOfferButton.js +6 -2
- package/dist/features/products/components/MakeOfferForm.js +2 -1
- package/dist/features/products/components/MarketplaceBundleCard.js +6 -3
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +3 -2
- package/dist/features/products/components/PlaceBidForm.js +2 -1
- package/dist/features/products/components/PrizeDrawCollage.js +6 -2
- package/dist/features/products/components/PrizeDrawDetailPageView.js +6 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +4 -2
- package/dist/features/products/components/PrizeDrawsIndexListing.js +6 -3
- package/dist/features/products/components/PrizeDrawsSection.js +2 -2
- package/dist/features/products/components/PrizeRevealModal.js +5 -1
- package/dist/features/products/components/ProductDetailPageView.js +32 -16
- package/dist/features/products/components/ProductFeatureBadges.js +9 -9
- package/dist/features/products/components/ProductForm.d.ts +11 -1
- package/dist/features/products/components/ProductForm.js +10 -5
- package/dist/features/products/components/ProductGalleryClient.js +8 -4
- package/dist/features/products/components/ProductGradingTab.js +4 -1
- package/dist/features/products/components/ProductGrid.js +35 -14
- package/dist/features/products/components/ProductInfo.js +2 -1
- package/dist/features/products/components/ProductTabs.js +2 -1
- package/dist/features/products/components/ProductTabsShell.js +5 -1
- package/dist/features/products/components/ProductsIndexListing.js +32 -24
- package/dist/features/products/components/ProductsIndexPageView.js +2 -2
- package/dist/features/products/components/RelatedProducts.js +3 -3
- package/dist/features/products/components/ShowGroupSection.js +10 -6
- package/dist/features/products/components/SublistingCarouselSection.js +7 -3
- package/dist/features/products/components/SublistingCategorySelect.d.ts +3 -1
- package/dist/features/products/components/SublistingCategorySelect.js +4 -37
- package/dist/features/products/constants/action-defs.js +3 -3
- package/dist/features/products/hooks/useProductDetail.d.ts +1 -1
- package/dist/features/products/hooks/useRelatedProducts.d.ts +1 -1
- package/dist/features/promotions/components/CouponCard.js +5 -3
- package/dist/features/promotions/components/CouponsIndexListing.js +7 -3
- package/dist/features/promotions/hooks/usePromotions.d.ts +2 -2
- package/dist/features/promotions/repository/coupons.repository.d.ts +8 -0
- package/dist/features/promotions/repository/coupons.repository.js +2 -0
- package/dist/features/reviews/components/ReviewDetailPageView.js +3 -3
- package/dist/features/reviews/components/ReviewDetailShell.js +15 -3
- package/dist/features/reviews/components/ReviewSummary.js +4 -2
- package/dist/features/reviews/components/ReviewsIndexListing.js +8 -2
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/reviews/hooks/useReviews.d.ts +2 -2
- package/dist/features/scams/components/ScamAwarenessModal.js +2 -2
- package/dist/features/scams/components/ScamProfileView.js +3 -3
- package/dist/features/scams/components/ScamRegistryView.js +6 -4
- package/dist/features/search/components/Search.js +9 -6
- package/dist/features/search/components/SearchFiltersRow.js +1 -1
- package/dist/features/search/components/SearchResultsSection.js +1 -1
- package/dist/features/search/hooks/useSearch.d.ts +2 -2
- package/dist/features/seller/components/CouponInlineSelect.d.ts +22 -0
- package/dist/features/seller/components/CouponInlineSelect.js +103 -0
- package/dist/features/seller/components/GroupInlineSelect.d.ts +23 -0
- package/dist/features/seller/components/GroupInlineSelect.js +80 -0
- package/dist/features/seller/components/PhysicalLocationModal.js +2 -1
- package/dist/features/seller/components/PrintCenterView.js +2 -1
- package/dist/features/seller/components/ProductInlineSelect.d.ts +24 -0
- package/dist/features/seller/components/ProductInlineSelect.js +42 -0
- package/dist/features/seller/components/QuickProductForm.js +4 -1
- package/dist/features/seller/components/SellerAddressesView.js +8 -3
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +6 -3
- package/dist/features/seller/components/SellerAnalyticsView.js +2 -2
- package/dist/features/seller/components/SellerBidsView.js +7 -7
- package/dist/features/seller/components/SellerBundlesView.js +2 -1
- package/dist/features/seller/components/SellerCouponEditorView.d.ts +2 -0
- package/dist/features/seller/components/SellerCouponEditorView.js +15 -3
- package/dist/features/seller/components/SellerCouponsView.js +1 -1
- package/dist/features/seller/components/SellerDashboardView.js +2 -2
- package/dist/features/seller/components/SellerDigitalCodesView.js +2 -2
- package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -2
- package/dist/features/seller/components/SellerGroupedListingsView.js +5 -5
- package/dist/features/seller/components/SellerOrdersView.js +13 -10
- package/dist/features/seller/components/SellerPayoutMethodsView.js +5 -4
- package/dist/features/seller/components/SellerPayoutRequestView.js +5 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +5 -2
- package/dist/features/seller/components/SellerPreOrdersView.js +6 -6
- package/dist/features/seller/components/SellerPrizeDrawsView.js +6 -6
- package/dist/features/seller/components/SellerProductShell.js +1 -1
- package/dist/features/seller/components/SellerProductsCards.js +4 -1
- package/dist/features/seller/components/SellerProductsView.js +5 -5
- package/dist/features/seller/components/SellerReviewsView.js +7 -3
- package/dist/features/seller/components/SellerShippingConfigsView.js +4 -4
- package/dist/features/seller/components/SellerShippingView.js +1 -1
- package/dist/features/seller/components/SellerSidebar.js +15 -8
- package/dist/features/seller/components/SellerStoreCategoriesView.js +1 -1
- package/dist/features/seller/components/SellerStorefrontView.js +2 -2
- package/dist/features/seller/components/SellerTemplatesView.js +7 -4
- package/dist/features/seller/components/SublistingInlineSelect.d.ts +19 -0
- package/dist/features/seller/components/SublistingInlineSelect.js +83 -0
- package/dist/features/seller/components/analytics/SellerTopProducts.js +1 -1
- package/dist/features/seller/components/index.d.ts +8 -0
- package/dist/features/seller/components/index.js +4 -0
- package/dist/features/seller/hooks/useSellerPayouts.d.ts +3 -3
- package/dist/features/seller/hooks/useSellerStore.d.ts +6 -6
- package/dist/features/seller/hooks/useSellerStorefront.d.ts +2 -2
- package/dist/features/shell/FormShell.js +10 -2
- package/dist/features/shell/QuickFormDrawer.js +5 -5
- package/dist/features/shell/StepForm.js +6 -6
- package/dist/features/site-settings/components/ActionPermissionsManager.js +8 -4
- package/dist/features/site-settings/components/NavPermissionsManager.js +7 -4
- package/dist/features/stores/components/InteractiveStoreCard.js +5 -1
- package/dist/features/stores/components/StoreAboutView.js +4 -1
- package/dist/features/stores/components/StoreAddressSelectorCreate.d.ts +2 -1
- package/dist/features/stores/components/StoreAddressSelectorCreate.js +5 -31
- package/dist/features/stores/components/StoreAuctionsListing.js +6 -3
- package/dist/features/stores/components/StoreGuideHubView.js +5 -2
- package/dist/features/stores/components/StoreHeader.js +8 -1
- package/dist/features/stores/components/StorePreOrdersListing.js +6 -3
- package/dist/features/stores/components/StoreProductsListing.js +6 -3
- package/dist/features/stores/components/StoreReviewsListing.js +8 -2
- package/dist/features/stores/components/StoresIndexListing.js +12 -6
- package/dist/features/stores/hooks/useStoreAddressSelector.d.ts +1 -1
- package/dist/features/stores/hooks/useStores.d.ts +6 -6
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +7 -1
- package/dist/features/whatsapp-bot/components/WhatsAppChatButton.js +2 -1
- package/dist/features/wishlist/components/WishlistPage.js +2 -1
- package/dist/features/wishlist/hooks/useUserWishlist.d.ts +15 -15
- package/dist/features/wishlist/hooks/useWishlist.d.ts +1 -1
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +13 -1
- package/dist/next/components/GlobalError.js +2 -2
- package/dist/next/routing/route-map.d.ts +2 -0
- package/dist/next/routing/route-map.js +1 -0
- package/dist/react/ErrorBoundary.js +2 -2
- package/dist/styles.css +2 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/DataTable.js +17 -16
- package/dist/ui/components/DateInput.js +2 -2
- package/dist/ui/components/DetailPageGallery.js +4 -1
- package/dist/ui/components/DetailPageTabs.js +6 -2
- package/dist/ui/components/ImageLightbox.js +5 -3
- package/dist/ui/components/Input.js +6 -3
- package/dist/ui/components/Layout.d.ts +4 -0
- package/dist/ui/components/Layout.js +1 -1
- package/dist/ui/components/Layout.style.css +1 -0
- package/dist/ui/components/ListingFilterDrawer.js +4 -1
- package/dist/ui/components/ListingLayout.js +9 -8
- package/dist/ui/components/ListingToolbar.js +5 -1
- package/dist/ui/components/PaginatedSelect.d.ts +1 -2
- package/dist/ui/components/PaginatedSelect.js +1 -2
- package/dist/ui/components/Radio.js +1 -1
- package/dist/ui/components/Select.js +2 -1
- package/dist/ui/components/Slider.js +1 -1
- package/dist/ui/components/VacationBanner.js +2 -1
- package/dist/ui/components/surface-tokens.d.ts +1 -0
- package/dist/ui/components/surface-tokens.js +1 -0
- package/package.json +11 -2
|
@@ -6,6 +6,20 @@ import { Alert, Button, Div, Grid, Heading, Input, Label, RichTextEditor, Row, S
|
|
|
6
6
|
import { apiClient } from "../../../http";
|
|
7
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
8
|
import { StepForm } from "../../shell";
|
|
9
|
+
import { ProductInlineSelect } from "../../seller/components/ProductInlineSelect";
|
|
10
|
+
import { CouponInlineSelect } from "../../seller/components/CouponInlineSelect";
|
|
11
|
+
const __P = {
|
|
12
|
+
p4: "p-4",
|
|
13
|
+
};
|
|
14
|
+
const __O = {
|
|
15
|
+
yAuto: "overflow-y-auto",
|
|
16
|
+
};
|
|
17
|
+
const CLS_REMOVE_BTN = "text-zinc-400 hover:text-red-500 transition-colors text-lg leading-none px-1 p-0 min-h-0 h-auto rounded-none";
|
|
18
|
+
const CLS_REMOVE_BTN_PX2 = "text-zinc-400 hover:text-red-500 transition-colors px-2 py-1 text-lg leading-none p-0 min-h-0 h-auto rounded-none";
|
|
19
|
+
const CLS_REMOVE_BTN_LG = "text-zinc-400 hover:text-red-500 text-lg leading-none px-2 p-0 min-h-0 h-auto rounded-none";
|
|
20
|
+
const CLS_RAFFLE_PANEL = "border border-amber-200 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20";
|
|
21
|
+
const CLS_RAFFLE_HEADING = "text-amber-900 dark:text-amber-100";
|
|
22
|
+
const CLS_RAFFLE_BODY = "text-amber-800 dark:text-amber-200";
|
|
9
23
|
const EVENT_TYPE_OPTIONS = [
|
|
10
24
|
{ label: "Sale", value: "sale" },
|
|
11
25
|
{ label: "Offer / Coupon", value: "offer" },
|
|
@@ -50,7 +64,7 @@ function FormFieldBuilder({ fields, setFields }) {
|
|
|
50
64
|
const hasOptions = FIELD_TYPES_WITH_OPTIONS.includes(field.type);
|
|
51
65
|
const isNumeric = field.type === "number" || field.type === "rating";
|
|
52
66
|
const isText = field.type === "text" || field.type === "textarea" || field.type === "email";
|
|
53
|
-
return (_jsxs(Stack, { gap: "xs", surface: "default", rounded: "lg", border: "default", padding: "sm", children: [_jsxs(Row, { justify: "between", gap: "xs", children: [_jsxs(Text, { size: "xs", weight: "medium", color: "muted", children: ["Field ", idx + 1] }), _jsx("
|
|
67
|
+
return (_jsxs(Stack, { gap: "xs", surface: "default", rounded: "lg", border: "default", padding: "sm", children: [_jsxs(Row, { justify: "between", gap: "xs", children: [_jsxs(Text, { size: "xs", weight: "medium", color: "muted", children: ["Field ", idx + 1] }), _jsx(Button, { variant: "ghost", type: "button", onClick: () => removeField(field.id), className: CLS_REMOVE_BTN, "aria-label": "Remove field", children: "\u00D7" })] }), _jsxs(Grid, { cols: 2, gap: "xs", children: [_jsx(Select, { label: "Type", value: field.type, options: FORM_FIELD_TYPE_OPTIONS, onChange: (e) => updateField(field.id, { type: e.target.value, options: undefined }) }), _jsx(Input, { label: "Label", value: field.label, onChange: (e) => updateField(field.id, { label: e.target.value }), placeholder: "e.g. Describe your entry", required: true })] }), !hasOptions && field.type !== "date" && field.type !== "file" && field.type !== "rating" && (_jsx(Input, { label: "Placeholder (optional)", value: field.placeholder ?? "", onChange: (e) => updateField(field.id, { placeholder: e.target.value || undefined }), placeholder: "Hint shown inside the field" })), hasOptions && (_jsx(Textarea, { label: "Options \u2014 one per line", value: (field.options ?? []).join("\n"), onChange: (e) => updateField(field.id, { options: e.target.value.split("\n").map((s) => s.trim()).filter(Boolean) }), placeholder: "Option A\nOption B\nOption C", rows: 3 })), (isText || isNumeric) && (_jsxs(Stack, { gap: "xs", surface: "muted", rounded: "md", padding: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Validation (optional)" }), _jsxs(Grid, { gap: "xs", className: "grid-cols-2", children: [isText && (_jsxs(_Fragment, { children: [_jsx(Input, { label: "Min length", type: "number", value: String(field.validation?.minLength ?? ""), onChange: (e) => updateField(field.id, { validation: { ...field.validation, minLength: e.target.value ? Number(e.target.value) : undefined } }), placeholder: "0" }), _jsx(Input, { label: "Max length", type: "number", value: String(field.validation?.maxLength ?? ""), onChange: (e) => updateField(field.id, { validation: { ...field.validation, maxLength: e.target.value ? Number(e.target.value) : undefined } }), placeholder: "500" })] })), isNumeric && (_jsxs(_Fragment, { children: [_jsx(Input, { label: "Min value", type: "number", value: String(field.validation?.min ?? ""), onChange: (e) => updateField(field.id, { validation: { ...field.validation, min: e.target.value ? Number(e.target.value) : undefined } }), placeholder: "0" }), _jsx(Input, { label: "Max value", type: "number", value: String(field.validation?.max ?? ""), onChange: (e) => updateField(field.id, { validation: { ...field.validation, max: e.target.value ? Number(e.target.value) : undefined } }), placeholder: "100" })] }))] }), field.type === "text" && (_jsx(Input, { label: "Pattern (regex, optional)", value: field.validation?.pattern ?? "", onChange: (e) => updateField(field.id, { validation: { ...field.validation, pattern: e.target.value || undefined } }), placeholder: "^[A-Z].*" }))] })), _jsx(Toggle, { checked: field.required, onChange: (v) => updateField(field.id, { required: v }), label: "Required" })] }, field.id));
|
|
54
68
|
}), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addField, children: "+ Add field" })] }));
|
|
55
69
|
}
|
|
56
70
|
const POLL_VISIBILITY_OPTIONS = [
|
|
@@ -120,11 +134,52 @@ const DEFAULT_DRAFT = {
|
|
|
120
134
|
raffleTopN: "10",
|
|
121
135
|
rafflePrize: "",
|
|
122
136
|
rafflePrizeCouponId: "",
|
|
137
|
+
rafflePrizeProductIds: [],
|
|
123
138
|
spinPrizes: [],
|
|
124
139
|
spinMaxPerUser: "1",
|
|
125
140
|
spinWindowStart: "",
|
|
126
141
|
spinWindowEnd: "",
|
|
127
142
|
};
|
|
143
|
+
// --- Pure build helpers (module-level, no component closure) -----------------
|
|
144
|
+
function buildEventTypeConfig(draft) {
|
|
145
|
+
if (draft.type === "sale")
|
|
146
|
+
return { saleConfig: { discountPercent: Number(draft.discountPercent) || 10, bannerText: draft.saleBannerText || undefined } };
|
|
147
|
+
if (draft.type === "offer")
|
|
148
|
+
return { offerConfig: { couponId: draft.couponId, displayCode: draft.displayCode, bannerText: draft.offerBannerText || undefined } };
|
|
149
|
+
if (draft.type === "poll")
|
|
150
|
+
return { pollConfig: { options: draft.pollOptions.filter((o) => o.label.trim()), allowMultiSelect: draft.allowMultiSelect, allowComment: draft.allowComment, resultsVisibility: draft.resultsVisibility } };
|
|
151
|
+
if (draft.type === "survey")
|
|
152
|
+
return { surveyConfig: { requireLogin: draft.requireLogin, maxEntriesPerUser: Number(draft.maxEntriesPerUser) || 1, hasLeaderboard: draft.hasLeaderboard, hasPointSystem: draft.hasPointSystem, pointsLabel: draft.hasPointSystem ? (draft.pointsLabel.trim() || "Points") : undefined, entryReviewRequired: draft.entryReviewRequired, formFields: draft.surveyFields.map((f, i) => ({ ...f, order: i })) } };
|
|
153
|
+
if (draft.type === "feedback")
|
|
154
|
+
return { feedbackConfig: { anonymous: draft.anonymous, formFields: draft.feedbackFields.map((f, i) => ({ ...f, order: i })) } };
|
|
155
|
+
return {};
|
|
156
|
+
}
|
|
157
|
+
function buildEventRaffleFields(draft) {
|
|
158
|
+
const isRaffleType = draft.type === "raffle" || draft.type === "spin_wheel";
|
|
159
|
+
const fields = {};
|
|
160
|
+
if (!draft.hasRaffle && !isRaffleType)
|
|
161
|
+
return fields;
|
|
162
|
+
fields.hasRaffle = true;
|
|
163
|
+
fields.raffleType = isRaffleType && draft.type === "spin_wheel" ? "spin_wheel" : draft.raffleType;
|
|
164
|
+
if (Number(draft.raffleTopN) > 0)
|
|
165
|
+
fields.raffleTopN = Number(draft.raffleTopN);
|
|
166
|
+
if (draft.rafflePrize)
|
|
167
|
+
fields.rafflePrize = draft.rafflePrize;
|
|
168
|
+
if (draft.rafflePrizeCouponId)
|
|
169
|
+
fields.rafflePrizeCouponId = draft.rafflePrizeCouponId;
|
|
170
|
+
if (draft.rafflePrizeProductIds.length > 0)
|
|
171
|
+
fields.rafflePrizeProductIds = draft.rafflePrizeProductIds;
|
|
172
|
+
if (draft.raffleType === "spin_wheel" || draft.type === "spin_wheel") {
|
|
173
|
+
fields.spinPrizes = draft.spinPrizes;
|
|
174
|
+
if (Number(draft.spinMaxPerUser) > 0)
|
|
175
|
+
fields.spinMaxPerUser = Number(draft.spinMaxPerUser);
|
|
176
|
+
if (draft.spinWindowStart)
|
|
177
|
+
fields.spinWindowStart = toISOString(draft.spinWindowStart);
|
|
178
|
+
if (draft.spinWindowEnd)
|
|
179
|
+
fields.spinWindowEnd = toISOString(draft.spinWindowEnd);
|
|
180
|
+
}
|
|
181
|
+
return fields;
|
|
182
|
+
}
|
|
128
183
|
// --- Component ---------------------------------------------------------------
|
|
129
184
|
export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
130
185
|
const isEdit = Boolean(eventId);
|
|
@@ -187,6 +242,7 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
187
242
|
raffleTopN: String(event.raffleTopN ?? 10),
|
|
188
243
|
rafflePrize: event.rafflePrize || "",
|
|
189
244
|
rafflePrizeCouponId: event.rafflePrizeCouponId || "",
|
|
245
|
+
rafflePrizeProductIds: Array.isArray(event.rafflePrizeProductIds) ? event.rafflePrizeProductIds : [],
|
|
190
246
|
spinPrizes: Array.isArray(event.spinPrizes) ? event.spinPrizes : [],
|
|
191
247
|
spinMaxPerUser: String(event.spinMaxPerUser ?? 1),
|
|
192
248
|
spinWindowStart: toLocalDatetime(event.spinWindowStart),
|
|
@@ -200,40 +256,6 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
200
256
|
// --- save ---
|
|
201
257
|
const saveMutation = useMutation({
|
|
202
258
|
mutationFn: async () => {
|
|
203
|
-
const buildTypeConfig = () => {
|
|
204
|
-
if (draft.type === "sale")
|
|
205
|
-
return { saleConfig: { discountPercent: Number(draft.discountPercent) || 10, bannerText: draft.saleBannerText || undefined } };
|
|
206
|
-
if (draft.type === "offer")
|
|
207
|
-
return { offerConfig: { couponId: draft.couponId, displayCode: draft.displayCode, bannerText: draft.offerBannerText || undefined } };
|
|
208
|
-
if (draft.type === "poll")
|
|
209
|
-
return { pollConfig: { options: draft.pollOptions.filter((o) => o.label.trim()), allowMultiSelect: draft.allowMultiSelect, allowComment: draft.allowComment, resultsVisibility: draft.resultsVisibility } };
|
|
210
|
-
if (draft.type === "survey")
|
|
211
|
-
return { surveyConfig: { requireLogin: draft.requireLogin, maxEntriesPerUser: Number(draft.maxEntriesPerUser) || 1, hasLeaderboard: draft.hasLeaderboard, hasPointSystem: draft.hasPointSystem, pointsLabel: draft.hasPointSystem ? (draft.pointsLabel.trim() || "Points") : undefined, entryReviewRequired: draft.entryReviewRequired, formFields: draft.surveyFields.map((f, i) => ({ ...f, order: i })) } };
|
|
212
|
-
if (draft.type === "feedback")
|
|
213
|
-
return { feedbackConfig: { anonymous: draft.anonymous, formFields: draft.feedbackFields.map((f, i) => ({ ...f, order: i })) } };
|
|
214
|
-
return {};
|
|
215
|
-
};
|
|
216
|
-
const isRaffleType = draft.type === "raffle" || draft.type === "spin_wheel";
|
|
217
|
-
const raffleFields = {};
|
|
218
|
-
if (draft.hasRaffle || isRaffleType) {
|
|
219
|
-
raffleFields.hasRaffle = true;
|
|
220
|
-
raffleFields.raffleType = isRaffleType && draft.type === "spin_wheel" ? "spin_wheel" : draft.raffleType;
|
|
221
|
-
if (Number(draft.raffleTopN) > 0)
|
|
222
|
-
raffleFields.raffleTopN = Number(draft.raffleTopN);
|
|
223
|
-
if (draft.rafflePrize)
|
|
224
|
-
raffleFields.rafflePrize = draft.rafflePrize;
|
|
225
|
-
if (draft.rafflePrizeCouponId)
|
|
226
|
-
raffleFields.rafflePrizeCouponId = draft.rafflePrizeCouponId;
|
|
227
|
-
if (draft.raffleType === "spin_wheel" || draft.type === "spin_wheel") {
|
|
228
|
-
raffleFields.spinPrizes = draft.spinPrizes;
|
|
229
|
-
if (Number(draft.spinMaxPerUser) > 0)
|
|
230
|
-
raffleFields.spinMaxPerUser = Number(draft.spinMaxPerUser);
|
|
231
|
-
if (draft.spinWindowStart)
|
|
232
|
-
raffleFields.spinWindowStart = toISOString(draft.spinWindowStart);
|
|
233
|
-
if (draft.spinWindowEnd)
|
|
234
|
-
raffleFields.spinWindowEnd = toISOString(draft.spinWindowEnd);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
259
|
const payload = {
|
|
238
260
|
type: draft.type,
|
|
239
261
|
title: draft.title,
|
|
@@ -243,8 +265,8 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
243
265
|
startsAt: toISOString(draft.startsAt),
|
|
244
266
|
endsAt: toISOString(draft.endsAt),
|
|
245
267
|
coverImageUrl: draft.coverImageUrl || undefined,
|
|
246
|
-
...
|
|
247
|
-
...
|
|
268
|
+
...buildEventTypeConfig(draft),
|
|
269
|
+
...buildEventRaffleFields(draft),
|
|
248
270
|
};
|
|
249
271
|
if (eventId) {
|
|
250
272
|
payload.status = draft.status;
|
|
@@ -316,7 +338,7 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
316
338
|
return "Poll events require at least 2 options";
|
|
317
339
|
return null;
|
|
318
340
|
},
|
|
319
|
-
render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Event Settings" }), isEdit && (_jsx(Select, { label: "Status", value: values.status, options: EVENT_STATUS_OPTIONS, onChange: (e) => onChange({ status: e.target.value }) })), values.createdBy && (_jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", className: "mb-1", children: "Created by" }), _jsx(Text, { size: "sm", color: "muted", children: values.createdBy })] })), values.type === "sale" && (_jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "sm", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "Sale configuration" }), _jsxs(Grid, { cols: 2, gap: "sm", children: [_jsx(Input, { label: "Discount %", type: "number", value: values.discountPercent, onChange: (e) => onChange({ discountPercent: e.target.value }), placeholder: "10" }), _jsx(Input, { label: "Banner text (optional)", value: values.saleBannerText, onChange: (e) => onChange({ saleBannerText: e.target.value }), placeholder: "Limited time \u2014 ends Sunday!" })] })] })), values.type === "offer" && (_jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "sm", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "Offer configuration" }), _jsxs(Grid, { cols: 2, gap: "sm", children: [_jsx(
|
|
341
|
+
render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Event Settings" }), isEdit && (_jsx(Select, { label: "Status", value: values.status, options: EVENT_STATUS_OPTIONS, onChange: (e) => onChange({ status: e.target.value }) })), values.createdBy && (_jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", className: "mb-1", children: "Created by" }), _jsx(Text, { size: "sm", color: "muted", children: values.createdBy })] })), values.type === "sale" && (_jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "sm", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "Sale configuration" }), _jsxs(Grid, { cols: 2, gap: "sm", children: [_jsx(Input, { label: "Discount %", type: "number", value: values.discountPercent, onChange: (e) => onChange({ discountPercent: e.target.value }), placeholder: "10" }), _jsx(Input, { label: "Banner text (optional)", value: values.saleBannerText, onChange: (e) => onChange({ saleBannerText: e.target.value }), placeholder: "Limited time \u2014 ends Sunday!" })] })] })), values.type === "offer" && (_jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "sm", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "Offer configuration" }), _jsxs(Grid, { cols: 2, gap: "sm", children: [_jsxs(Div, { children: [_jsx(Text, { size: "sm", weight: "medium", className: "mb-1", children: "Coupon" }), _jsx(CouponInlineSelect, { scope: "admin", value: values.couponId, onChange: (id) => onChange({ couponId: id ?? "" }), placeholder: "Search coupons\u2026", allowCreate: false })] }), _jsx(Input, { label: "Display code", value: values.displayCode, onChange: (e) => onChange({ displayCode: e.target.value }), placeholder: "CHARIZARD25", required: true })] }), _jsx(Input, { label: "Banner text (optional)", value: values.offerBannerText, onChange: (e) => onChange({ offerBannerText: e.target.value }), placeholder: "Use CHARIZARD25 at checkout" })] })), values.type === "poll" && (_jsxs(Stack, { gap: "md", rounded: "xl", border: "default", padding: "md", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "Poll configuration" }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Options (minimum 2)" }), values.pollOptions.map((opt, idx) => (_jsxs(Row, { gap: "xs", children: [_jsx(Div, { className: "flex-1", children: _jsx(Input, { value: opt.label, onChange: (e) => onChange({ pollOptions: values.pollOptions.map((o) => o.id === opt.id ? { ...o, label: e.target.value } : o) }), placeholder: `Option ${idx + 1}` }) }), values.pollOptions.length > 2 && (_jsx(Button, { variant: "ghost", type: "button", onClick: () => onChange({ pollOptions: values.pollOptions.filter((o) => o.id !== opt.id) }), className: CLS_REMOVE_BTN_PX2, "aria-label": "Remove option", children: "\u00D7" }))] }, opt.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onChange({ pollOptions: [...values.pollOptions, { id: `opt${Date.now()}`, label: "" }] }), children: "+ Add option" })] }), _jsx(Select, { label: "Results visibility", value: values.resultsVisibility, options: POLL_VISIBILITY_OPTIONS, onChange: (e) => onChange({ resultsVisibility: e.target.value }) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Toggle, { checked: values.allowMultiSelect, onChange: (v) => onChange({ allowMultiSelect: v }), label: "Allow multi-select" }), _jsx(Toggle, { checked: values.allowComment, onChange: (v) => onChange({ allowComment: v }), label: "Allow comment with vote" })] })] })), values.type === "survey" && (_jsxs(Stack, { gap: "md", rounded: "xl", border: "default", padding: "md", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "Survey configuration" }), _jsxs(Grid, { cols: 2, gap: "sm", children: [_jsx(Input, { label: "Max entries per user", type: "number", value: values.maxEntriesPerUser, onChange: (e) => onChange({ maxEntriesPerUser: e.target.value }), placeholder: "1" }), values.hasPointSystem && (_jsx(Input, { label: "Points label", value: values.pointsLabel, onChange: (e) => onChange({ pointsLabel: e.target.value }), placeholder: "Points" }))] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Toggle, { checked: values.requireLogin, onChange: (v) => onChange({ requireLogin: v }), label: "Require login to participate" }), _jsx(Toggle, { checked: values.hasLeaderboard, onChange: (v) => onChange({ hasLeaderboard: v }), label: "Show leaderboard" }), _jsx(Toggle, { checked: values.hasPointSystem, onChange: (v) => onChange({ hasPointSystem: v }), label: "Enable point system" }), _jsx(Toggle, { checked: values.entryReviewRequired, onChange: (v) => onChange({ entryReviewRequired: v }), label: "Require employee review before entry is approved" })] }), _jsx(FormFieldBuilder, { fields: values.surveyFields, setFields: (f) => onChange({ surveyFields: f }) })] })), values.type === "feedback" && (_jsxs(Stack, { gap: "md", rounded: "xl", border: "default", padding: "md", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "Feedback configuration" }), _jsx(Toggle, { checked: values.anonymous, onChange: (v) => onChange({ anonymous: v }), label: "Allow anonymous submissions" }), _jsx(FormFieldBuilder, { fields: values.feedbackFields, setFields: (f) => onChange({ feedbackFields: f }) })] }))] })),
|
|
320
342
|
},
|
|
321
343
|
{
|
|
322
344
|
label: "Raffle / Spin",
|
|
@@ -324,14 +346,14 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
324
346
|
const isRaffleType = values.type === "raffle" || values.type === "spin_wheel";
|
|
325
347
|
const showRaffleConfig = isRaffleType || values.hasRaffle;
|
|
326
348
|
const isSpinMode = values.type === "spin_wheel" || values.raffleType === "spin_wheel";
|
|
327
|
-
return (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Raffle & Spin Wheel" }), !isRaffleType && (_jsx(Toggle, { checked: values.hasRaffle, onChange: (v) => onChange({ hasRaffle: v }), label: "Attach a raffle to this event" })), !isRaffleType && !values.hasRaffle && (_jsx(Text, { size: "sm", color: "muted", children: "Enable the toggle above to attach a raffle draw to this event. Not applicable for this event type by default." })), showRaffleConfig && (_jsxs(Stack, { gap: "md", rounded: "xl", border: "default", padding: "md", children: [_jsxs(Grid, { cols: 2, gap: "sm", children: [_jsx(Select, { label: "Raffle type", value: values.type === "spin_wheel" ? "spin_wheel" : values.raffleType, options: RAFFLE_TYPE_OPTIONS, onChange: (e) => onChange({ raffleType: e.target.value }), disabled: values.type === "spin_wheel" }), _jsx(Input, { label: "Top N (for top-N raffle types)", type: "number", value: values.raffleTopN, onChange: (e) => onChange({ raffleTopN: e.target.value }), placeholder: "10" })] }), _jsx(Input, { label: "Prize description", value: values.rafflePrize, onChange: (e) => onChange({ rafflePrize: e.target.value }), placeholder: "\u20B92,000 store credit + exclusive Pikachu sticker" }), _jsx(
|
|
349
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Raffle & Spin Wheel" }), !isRaffleType && (_jsx(Toggle, { checked: values.hasRaffle, onChange: (v) => onChange({ hasRaffle: v }), label: "Attach a raffle to this event" })), !isRaffleType && !values.hasRaffle && (_jsx(Text, { size: "sm", color: "muted", children: "Enable the toggle above to attach a raffle draw to this event. Not applicable for this event type by default." })), showRaffleConfig && (_jsxs(Stack, { gap: "md", rounded: "xl", border: "default", padding: "md", children: [_jsxs(Grid, { cols: 2, gap: "sm", children: [_jsx(Select, { label: "Raffle type", value: values.type === "spin_wheel" ? "spin_wheel" : values.raffleType, options: RAFFLE_TYPE_OPTIONS, onChange: (e) => onChange({ raffleType: e.target.value }), disabled: values.type === "spin_wheel" }), _jsx(Input, { label: "Top N (for top-N raffle types)", type: "number", value: values.raffleTopN, onChange: (e) => onChange({ raffleTopN: e.target.value }), placeholder: "10" })] }), _jsx(Input, { label: "Prize description", value: values.rafflePrize, onChange: (e) => onChange({ rafflePrize: e.target.value }), placeholder: "\u20B92,000 store credit + exclusive Pikachu sticker" }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", weight: "medium", className: "mb-1", children: "Coupon for winner (optional)" }), _jsx(CouponInlineSelect, { scope: "admin", value: values.rafflePrizeCouponId, onChange: (id) => onChange({ rafflePrizeCouponId: id ?? "" }), placeholder: "Search coupons\u2026", allowCreate: false })] }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", weight: "medium", className: "mb-1", children: "Prize products (optional)" }), _jsx(ProductInlineSelect, { scope: "admin", multiple: true, value: values.rafflePrizeProductIds, onChange: (ids) => onChange({ rafflePrizeProductIds: ids }), placeholder: "Search products to feature as prizes\u2026" }), _jsx(Text, { size: "xs", color: "muted", className: "mt-1", children: "Products shown to winners or displayed as the prize showcase." })] }), isSpinMode && (_jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "sm", children: [_jsx(Text, { size: "xs", weight: "medium", color: "muted", children: "Spin prizes (weighted random)" }), values.spinPrizes.length === 0 && (_jsx(Text, { size: "xs", color: "muted", children: "No spin prizes yet. Add at least one to enable spinning." })), values.spinPrizes.map((p) => (_jsxs(Grid, { gap: "xs", className: "grid-cols-12 items-end", children: [_jsx(Div, { className: "col-span-5", children: _jsx(Input, { label: "Label", value: p.label, onChange: (e) => onChange({ spinPrizes: values.spinPrizes.map((sp) => sp.id === p.id ? { ...sp, label: e.target.value } : sp) }), placeholder: "\u20B9100 off" }) }), _jsxs(Div, { className: "col-span-3", children: [_jsx(Text, { size: "xs", weight: "medium", className: "mb-1", children: "Coupon" }), _jsx(CouponInlineSelect, { scope: "admin", value: p.couponId ?? "", onChange: (id) => onChange({ spinPrizes: values.spinPrizes.map((sp) => sp.id === p.id ? { ...sp, couponId: id || undefined } : sp) }), placeholder: "Search\u2026", allowCreate: false })] }), _jsx(Div, { className: "col-span-2", children: _jsx(Input, { label: "Weight", type: "number", value: String(p.weight), onChange: (e) => onChange({ spinPrizes: values.spinPrizes.map((sp) => sp.id === p.id ? { ...sp, weight: Number(e.target.value) || 0 } : sp) }) }) }), _jsx(Row, { centered: true, className: "col-span-1 pb-2", children: _jsx(Toggle, { checked: p.isActive, onChange: (v) => onChange({ spinPrizes: values.spinPrizes.map((sp) => sp.id === p.id ? { ...sp, isActive: v } : sp) }), label: "" }) }), _jsx(Row, { centered: true, className: "col-span-1 pb-2", children: _jsx(Button, { variant: "ghost", type: "button", onClick: () => onChange({ spinPrizes: values.spinPrizes.filter((sp) => sp.id !== p.id) }), className: CLS_REMOVE_BTN_LG, "aria-label": "Remove prize", children: "\u00D7" }) })] }, p.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onChange({ spinPrizes: [...values.spinPrizes, { id: `prize-${Date.now()}`, label: "", couponId: undefined, weight: 1, isActive: true }] }), children: "+ Add spin prize" }), _jsx(Input, { label: "Max spins per user", type: "number", value: values.spinMaxPerUser, onChange: (e) => onChange({ spinMaxPerUser: e.target.value }), placeholder: "1" }), _jsxs(Grid, { cols: 2, gap: "sm", children: [_jsx(Input, { label: "Spin window start", type: "datetime-local", value: values.spinWindowStart, onChange: (e) => onChange({ spinWindowStart: e.target.value }) }), _jsx(Input, { label: "Spin window end", type: "datetime-local", value: values.spinWindowEnd, onChange: (e) => onChange({ spinWindowEnd: e.target.value }) })] })] })), isEdit && (_jsxs(Stack, { gap: "xs", rounded: "lg", padding: "sm", className: CLS_RAFFLE_PANEL, children: [_jsxs(Row, { justify: "between", gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", className: CLS_RAFFLE_HEADING, children: raffleWinnerName ? `Winner: ${raffleWinnerName}` : "Raffle not yet triggered" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => triggerRaffleMutation.mutate(), disabled: triggerRaffleMutation.isPending || Boolean(raffleWinnerName), children: triggerRaffleMutation.isPending ? "Triggering…" : raffleWinnerName ? "Already triggered" : "Trigger Raffle Now" })] }), raffleEntryCount !== null && _jsxs(Text, { size: "xs", className: CLS_RAFFLE_BODY, children: ["Pool size: ", raffleEntryCount] }), raffleGithubUrl && _jsxs(Text, { size: "xs", className: `break-all ${CLS_RAFFLE_BODY}`, children: ["Fairness proof: ", raffleGithubUrl] }), triggerMessage && _jsx(Text, { size: "xs", className: CLS_RAFFLE_HEADING, children: triggerMessage })] }))] }))] }));
|
|
328
350
|
},
|
|
329
351
|
},
|
|
330
352
|
];
|
|
331
353
|
const alertSection = eventQuery.error ? (_jsx(Alert, { variant: "error", title: "Could not load event", children: eventQuery.error instanceof Error ? eventQuery.error.message : "Unknown error" })) : null;
|
|
332
354
|
const formContent = (_jsxs(Stack, { gap: "sm", children: [alertSection, _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: () => { saveMutation.mutate(); }, formId: "admin-event", currentStep: currentStep, onStepChange: setCurrentStep, completeLabel: isEdit ? "Save Changes" : "Create Event", isLoading: isLoading }), saveMessage && (_jsx(Alert, { variant: saveMessage.toLowerCase().includes("fail") || saveMessage.toLowerCase().includes("error") ? "error" : "success", title: "Save status", children: saveMessage }))] }));
|
|
333
355
|
if (embedded) {
|
|
334
|
-
return _jsx(Div, { className:
|
|
356
|
+
return _jsx(Div, { className: `${__O.yAuto} ${__P.p4}`, children: formContent });
|
|
335
357
|
}
|
|
336
358
|
return (_jsx(StackedViewShell, { portal: "admin", ...rest, title: isEdit ? "Edit Event" : "Create Event", sections: [formContent] }));
|
|
337
359
|
}
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
5
|
-
import { Alert, Button, Input, Select, StackedViewShell, Text, } from "../../../ui";
|
|
5
|
+
import { Alert, Button, Div, Input, Select, StackedViewShell, Text, } from "../../../ui";
|
|
6
6
|
import { apiClient } from "../../../http";
|
|
7
7
|
import { DataTable } from "../../admin/components/DataTable";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
|
+
const __P = {
|
|
10
|
+
p4: "p-4",
|
|
11
|
+
};
|
|
9
12
|
const REVIEW_STATUS_OPTIONS = [
|
|
10
13
|
{ label: "All", value: "all" },
|
|
11
14
|
{ label: "Pending", value: "pending" },
|
|
@@ -107,7 +110,7 @@ export function AdminEventEntriesView({ eventId, entriesEndpoint, entryReviewEnd
|
|
|
107
110
|
{
|
|
108
111
|
key: "points",
|
|
109
112
|
header: "Points",
|
|
110
|
-
render: (row) => (_jsxs(
|
|
113
|
+
render: (row) => (_jsxs(Div, { className: "flex items-center gap-1.5", children: [_jsx("input", { type: "number", min: 0, className: "w-20 rounded border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-sm px-2 py-1 text-zinc-800 dark:text-zinc-200 focus:outline-none focus:ring-2 focus:ring-primary/50", placeholder: "0", value: pointsInputs[row.id] ?? (row.points !== undefined ? String(row.points) : ""), onChange: (e) => setPointsInputs((prev) => ({ ...prev, [row.id]: e.target.value })) }), _jsx(Button, { size: "sm", variant: "outline", disabled: pointsMutation.isPending || !(row.id in pointsInputs), onClick: () => {
|
|
111
114
|
const val = Number(pointsInputs[row.id]);
|
|
112
115
|
if (!isNaN(val)) {
|
|
113
116
|
pointsMutation.mutate({ entryId: row.id, points: val, currentStatus: row.reviewStatus });
|
|
@@ -118,11 +121,11 @@ export function AdminEventEntriesView({ eventId, entriesEndpoint, entryReviewEnd
|
|
|
118
121
|
{
|
|
119
122
|
key: "actions",
|
|
120
123
|
header: "Actions",
|
|
121
|
-
render: (row) => (_jsxs(
|
|
124
|
+
render: (row) => (_jsxs(Div, { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Button, { size: "sm", variant: "secondary", disabled: reviewMutation.isPending || row.reviewStatus === "approved", onClick: () => reviewMutation.mutate({ entryId: row.id, status: "approved" }), children: "Approve" }), _jsx(Button, { size: "sm", variant: "outline", disabled: reviewMutation.isPending || row.reviewStatus === "flagged", onClick: () => reviewMutation.mutate({ entryId: row.id, status: "flagged" }), children: "Flag" }), row.formResponses && Object.keys(row.formResponses).length > 0 && (_jsx(Button, { size: "sm", variant: "outline", onClick: () => setExpandedEntryId((prev) => (prev === row.id ? null : row.id)), children: expandedEntryId === row.id ? "Hide" : "Responses" }))] })),
|
|
122
125
|
},
|
|
123
126
|
], [reviewMutation, pointsMutation, pointsInputs, expandedEntryId]);
|
|
124
|
-
const statsSection = (_jsxs(
|
|
125
|
-
const filtersSection = (_jsxs(
|
|
127
|
+
const statsSection = (_jsxs(Div, { className: "grid grid-cols-1 sm:grid-cols-3 gap-3", children: [_jsx(Alert, { variant: "info", title: "Total entries", children: String(statsQuery.data?.stats?.totalEntries ?? 0) }), _jsx(Alert, { variant: "success", title: "Approved", children: String(statsQuery.data?.stats?.approvedEntries ?? 0) }), _jsx(Alert, { variant: "warning", title: "Flagged", children: String(statsQuery.data?.stats?.flaggedEntries ?? 0) })] }));
|
|
128
|
+
const filtersSection = (_jsxs(Div, { className: "grid grid-cols-1 md:grid-cols-3 gap-3", children: [_jsx(Input, { label: "Search entries", value: searchQuery, onChange: (event) => {
|
|
126
129
|
setPage(1);
|
|
127
130
|
setSearchQuery(event.target.value);
|
|
128
131
|
}, placeholder: "Search by user, email, or id" }), _jsx(Select, { label: "Review status", value: statusFilter, options: REVIEW_STATUS_OPTIONS, onChange: (event) => {
|
|
@@ -130,7 +133,7 @@ export function AdminEventEntriesView({ eventId, entriesEndpoint, entryReviewEnd
|
|
|
130
133
|
setStatusFilter(event.target.value);
|
|
131
134
|
} })] }));
|
|
132
135
|
const expandedEntry = expandedEntryId ? rows.find((r) => r.id === expandedEntryId) : null;
|
|
133
|
-
const responsesPanelSection = expandedEntry?.formResponses && Object.keys(expandedEntry.formResponses).length > 0 ? (_jsxs(
|
|
136
|
+
const responsesPanelSection = expandedEntry?.formResponses && Object.keys(expandedEntry.formResponses).length > 0 ? (_jsxs(Div, { className: `rounded-xl border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 ${__P.p4} space-y-3`, children: [_jsxs(Text, { className: "text-sm font-semibold text-zinc-800 dark:text-zinc-200", children: ["Responses \u2014 ", expandedEntry.userDisplayName || expandedEntry.userId || "Anonymous"] }), _jsx(Div, { className: "space-y-2", children: Object.entries(expandedEntry.formResponses).map(([key, value]) => (_jsxs(Div, { className: "space-y-0.5", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-500 dark:text-zinc-400", children: key }), _jsx(Text, { className: "text-sm text-zinc-800 dark:text-zinc-200 whitespace-pre-wrap break-words", children: Array.isArray(value) ? value.join(", ") : String(value ?? "—") })] }, key))) })] })) : null;
|
|
134
137
|
const tableSection = (_jsx(DataTable, { columns: columns, rows: rows, isLoading: entriesQuery.isLoading, currentPage: entriesQuery.data?.page ?? page, totalPages: entriesQuery.data?.totalPages ?? 1, onPageChange: setPage, emptyLabel: "No entries found" }));
|
|
135
138
|
const eventTitle = statsQuery.data?.event?.title;
|
|
136
139
|
return (_jsx(StackedViewShell, { portal: "admin", ...rest, title: eventTitle ? `${eventTitle} Entries` : "Event Entries", renderHeader: renderHeader, sections: [
|
|
@@ -8,6 +8,12 @@ import { EVENT_FIELDS } from "../schemas";
|
|
|
8
8
|
import { EventStatusBadge } from "./EventStatusBadge";
|
|
9
9
|
import { ROUTES } from "../../../next";
|
|
10
10
|
import { useLongPress } from "../../../react/hooks/useLongPress";
|
|
11
|
+
const __P = {
|
|
12
|
+
p4: "p-4",
|
|
13
|
+
};
|
|
14
|
+
const __O = {
|
|
15
|
+
hidden: "overflow-hidden",
|
|
16
|
+
};
|
|
11
17
|
const TYPE_ICONS = {
|
|
12
18
|
sale: "🏷️",
|
|
13
19
|
offer: "🎁",
|
|
@@ -25,6 +31,6 @@ export function EventCard({ event, labels = {}, onParticipate, className = "", s
|
|
|
25
31
|
const msLeft = endsAt.getTime() - now.getTime();
|
|
26
32
|
const daysLeft = Math.max(0, Math.ceil(msLeft / (1000 * 60 * 60 * 24)));
|
|
27
33
|
const detailHref = String(ROUTES.PUBLIC.EVENT_DETAIL(event.slug ?? event.id));
|
|
28
|
-
return (_jsxs(Article, { className: `group relative flex h-full ${LAYOUT.cardHeight.event} flex-col overflow-hidden rounded-xl border bg-white shadow-sm transition-shadow hover:shadow-md dark:bg-slate-900 ${isSelected ? "border-primary outline outline-2 outline-primary" : "border-zinc-200 dark:border-slate-700"} ${className}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(event.id, !isSelected); }, label: isSelected ? "Deselect event" : "Select event", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsx(Link, { href: detailHref, className: "block flex-shrink-0", children: event.coverImageUrl ? (_jsx(Div, { className:
|
|
34
|
+
return (_jsxs(Article, { className: `group relative flex h-full ${LAYOUT.cardHeight.event} flex-col overflow-hidden rounded-xl border bg-white shadow-sm transition-shadow hover:shadow-md dark:bg-slate-900 ${isSelected ? "border-primary outline outline-2 outline-primary" : "border-zinc-200 dark:border-slate-700"} ${className}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(event.id, !isSelected); }, label: isSelected ? "Deselect event" : "Select event", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsx(Link, { href: detailHref, className: "block flex-shrink-0", children: event.coverImageUrl ? (_jsx(Div, { className: `aspect-video ${__O.hidden}`, children: _jsx(Div, { role: "img", "aria-label": safeTitle, className: "h-full w-full bg-cover bg-center transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${event.coverImageUrl})` } }) })) : (_jsx(Div, { className: "aspect-video bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700 flex items-center justify-center", children: _jsx(Span, { className: "text-5xl opacity-40", "aria-hidden": "true", children: TYPE_ICONS[event.type] }) })) }), _jsxs(Div, { className: `flex flex-1 flex-col ${__P.p4}`, children: [_jsxs(Div, { className: "flex items-start justify-between gap-2 mb-2", children: [_jsx(Span, { size: "lg", "aria-hidden": "true", children: TYPE_ICONS[event.type] }), _jsx(EventStatusBadge, { status: event.status })] }), _jsx(Link, { href: detailHref, className: "block", children: _jsx(Heading, { level: 3, className: "font-semibold text-gray-900 dark:text-zinc-100 text-base leading-snug mb-1 group-hover:text-primary transition-colors", children: safeTitle }) }), _jsx(RichText, { html: normalizeRichTextHtml(event.description ?? ""), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `mb-3 ${THEME_CONSTANTS.utilities.textClamp3} text-sm text-zinc-600 dark:text-zinc-400` }), _jsxs(Div, { className: "mb-3 mt-auto flex items-center justify-between text-xs text-zinc-500 dark:text-zinc-400", children: [event.status === EVENT_FIELDS.STATUS_VALUES.ACTIVE &&
|
|
29
35
|
daysLeft > 0 && _jsxs(Span, { children: ["\u23F1 ", daysLeft, "d remaining"] }), _jsxs(Span, { children: ["\uD83D\uDC65 ", event.stats.totalEntries, " ", labels.entries ?? "entries"] })] }), event.status === EVENT_FIELDS.STATUS_VALUES.ACTIVE && onParticipate ? (_jsx(Button, { type: "button", onClick: () => onParticipate(event), className: "w-full rounded-lg bg-primary py-2 text-sm font-medium text-white transition-colors hover:bg-primary-600", children: labels.participate ?? "Participate" })) : (_jsxs(TextLink, { href: detailHref, className: "inline-flex w-full items-center justify-center gap-1.5 rounded-lg border border-zinc-300 px-3 py-2 text-sm font-medium text-zinc-700 transition-colors hover:bg-zinc-100 dark:border-slate-600 dark:text-zinc-200 dark:hover:bg-slate-800", children: [labels.viewDetails ?? "View details", " \u2192"] }))] })] }));
|
|
30
36
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Div } from "../../../ui";
|
|
2
3
|
export function EventFormDrawer({ isOpen, onClose, renderHeader, renderBaseFields, renderMediaFields, renderTypeConfig, renderTypeSelector, renderFooter, className = "", }) {
|
|
3
4
|
if (!isOpen)
|
|
4
5
|
return null;
|
|
5
|
-
return (_jsxs(
|
|
6
|
+
return (_jsxs(Div, { className: `space-y-6 ${className}`, children: [renderHeader?.(), renderTypeSelector?.(), renderBaseFields?.(), renderMediaFields?.(), renderTypeConfig?.(), renderFooter?.()] }));
|
|
6
7
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import { Span, Text } from "../../../ui";
|
|
3
|
+
import { Div, Span, Text } from "../../../ui";
|
|
4
4
|
export function EventLeaderboard({ isLoading = false, isEmpty = false, renderList, renderEntry, entries = [], renderEmpty, renderSkeleton, labels = {}, className = "", }) {
|
|
5
5
|
if (isLoading) {
|
|
6
6
|
if (renderSkeleton)
|
|
7
7
|
return _jsx(_Fragment, { children: renderSkeleton() });
|
|
8
|
-
return _jsx(
|
|
8
|
+
return _jsx(Div, { className: "flex justify-center py-8", children: "Loading\u2026" });
|
|
9
9
|
}
|
|
10
10
|
if (isEmpty) {
|
|
11
11
|
if (renderEmpty)
|
|
@@ -13,6 +13,6 @@ export function EventLeaderboard({ isLoading = false, isEmpty = false, renderLis
|
|
|
13
13
|
return (_jsx(Text, { variant: "secondary", size: "sm", className: "text-center py-4", children: labels.noEntries ?? "No entries yet." }));
|
|
14
14
|
}
|
|
15
15
|
if (renderList)
|
|
16
|
-
return _jsx(
|
|
17
|
-
return (_jsx(
|
|
16
|
+
return _jsx(Div, { className: className, children: renderList() });
|
|
17
|
+
return (_jsx(Div, { className: `space-y-2 ${className}`, children: entries.map((entry, i) => renderEntry ? (_jsx(React.Fragment, { children: renderEntry(entry, i) }, entry.userId)) : (_jsxs("div", { className: "flex items-center justify-between p-3 rounded-xl border", "data-section": "eventleaderboard-div-282", children: [_jsxs(Span, { weight: "medium", children: ["#", entry.rank, " ", entry.userDisplayName] }), _jsxs(Span, { children: [entry.totalPoints, " ", labels.points ?? "pts"] })] }, entry.userId))) }));
|
|
18
18
|
}
|
|
@@ -5,6 +5,9 @@ import Link from "next/link";
|
|
|
5
5
|
import { Div, Heading, Stack, Text } from "../../../ui";
|
|
6
6
|
import { Button } from "../../../ui/components/Button";
|
|
7
7
|
import { ROUTES } from "../../../next/routing/route-map";
|
|
8
|
+
const __P = {
|
|
9
|
+
p6: "p-6",
|
|
10
|
+
};
|
|
8
11
|
/**
|
|
9
12
|
* `EventOfferCard` — W1-18 — offer event UI noted as missing in the plan.
|
|
10
13
|
* Renders a coupon code with copy-to-clipboard + a CTA to the filtered
|
|
@@ -25,5 +28,5 @@ export function EventOfferCard({ couponCode, offerDescription, title = "Exclusiv
|
|
|
25
28
|
/* clipboard unavailable — leave button as-is */
|
|
26
29
|
}
|
|
27
30
|
};
|
|
28
|
-
return (_jsx(Div, { className:
|
|
31
|
+
return (_jsx(Div, { className: `rounded-2xl border border-zinc-200 dark:border-zinc-700 ${__P.p6}`, children: _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, className: "text-2xl font-semibold", children: title }), offerDescription ? (_jsx(Text, { className: "text-sm text-zinc-600 dark:text-zinc-400", children: offerDescription })) : null] }), _jsxs(Div, { className: "flex items-center gap-3 rounded-lg bg-zinc-50 dark:bg-zinc-900 px-4 py-3", children: [_jsx("code", { className: "flex-1 font-mono text-lg font-bold text-zinc-900 dark:text-zinc-100", children: couponCode }), _jsx(Button, { type: "button", variant: "outline", onClick: copyCode, children: copied ? "Copied!" : "Copy code" })] }), _jsx(Div, { children: _jsx(Link, { href: href, children: _jsx(Button, { variant: "primary", children: "Apply at checkout" }) }) })] }) }));
|
|
29
32
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Div } from "../../../ui";
|
|
2
3
|
export function EventParticipateView({ isLoading = false, isSubmitted = false, renderEventInfo, renderForm, renderAction, renderSuccess, renderSkeleton, renderAuthGate, className = "", }) {
|
|
3
4
|
if (isLoading) {
|
|
4
5
|
if (renderSkeleton)
|
|
5
6
|
return _jsx(_Fragment, { children: renderSkeleton() });
|
|
6
|
-
return _jsx(
|
|
7
|
+
return _jsx(Div, { className: "flex justify-center py-20", children: "Loading\u2026" });
|
|
7
8
|
}
|
|
8
9
|
if (renderAuthGate)
|
|
9
10
|
return _jsx(_Fragment, { children: renderAuthGate() });
|
|
10
11
|
if (isSubmitted && renderSuccess)
|
|
11
12
|
return _jsx(_Fragment, { children: renderSuccess() });
|
|
12
|
-
return (_jsxs(
|
|
13
|
+
return (_jsxs(Div, { className: `max-w-xl mx-auto space-y-6 ${className}`, children: [renderEventInfo?.(), renderForm?.(), renderAction?.()] }));
|
|
13
14
|
}
|
|
@@ -3,7 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { useAuth } from "../../../react/contexts/SessionContext";
|
|
5
5
|
import { ROUTES } from "../../../next";
|
|
6
|
-
import { LoginRequiredModal, Text } from "../../../ui";
|
|
6
|
+
import { Div, LoginRequiredModal, Span, Text } from "../../../ui";
|
|
7
|
+
const CLS_THANKS_BOX = "rounded-xl border border-green-200 dark:border-green-800 bg-success-surface px-6 py-8 text-center space-y-2";
|
|
7
8
|
export function EventPollWidget({ eventId, pollConfig, eventStatus, totalEntries, entriesEndpoint, className = "", }) {
|
|
8
9
|
const { user } = useAuth();
|
|
9
10
|
const endpoint = entriesEndpoint ?? `/api/events/${eventId}/entries`;
|
|
@@ -56,13 +57,13 @@ export function EventPollWidget({ eventId, pollConfig, eventStatus, totalEntries
|
|
|
56
57
|
}
|
|
57
58
|
};
|
|
58
59
|
if (isEnded) {
|
|
59
|
-
return (_jsxs(
|
|
60
|
+
return (_jsxs(Div, { className: `rounded-xl border border-zinc-200 dark:border-zinc-700 px-5 py-4 space-y-3 ${className}`, children: [_jsx(Text, { className: "text-sm font-medium text-zinc-500 dark:text-zinc-400", children: "This poll has closed." }), totalEntries !== undefined && (_jsxs(Text, { className: "text-sm text-zinc-400 dark:text-zinc-400", children: [totalEntries.toLocaleString(), " vote", totalEntries !== 1 ? "s" : "", " cast"] })), _jsx(Div, { className: "space-y-2", children: pollConfig.options.map((opt) => (_jsx(Div, { className: "rounded-lg border border-zinc-100 dark:border-zinc-800 px-4 py-2.5 text-sm text-zinc-600 dark:text-zinc-400", children: opt.label }, opt.id))) })] }));
|
|
60
61
|
}
|
|
61
62
|
if (pollConfig.requireLogin && !user) {
|
|
62
|
-
return (_jsxs(
|
|
63
|
+
return (_jsxs(Div, { className: `rounded-xl border border-zinc-200 dark:border-zinc-700 px-6 py-8 text-center space-y-3 ${className}`, children: [_jsx(Text, { className: "font-semibold text-zinc-900 dark:text-zinc-100", children: "Login to vote" }), _jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "You need an account to participate in this poll." }), _jsx("a", { href: String(ROUTES.AUTH.LOGIN), className: "inline-block rounded-xl bg-primary px-6 py-2.5 text-sm font-semibold text-white hover:bg-primary-600", children: "Log In" })] }));
|
|
63
64
|
}
|
|
64
65
|
if (isSubmitted) {
|
|
65
|
-
return (_jsxs(
|
|
66
|
+
return (_jsxs(Div, { className: `${CLS_THANKS_BOX} ${className}`, children: [_jsx(Text, { className: "font-semibold text-success", children: "Vote recorded!" }), _jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "Thanks for participating." })] }));
|
|
66
67
|
}
|
|
67
|
-
return (_jsxs(
|
|
68
|
+
return (_jsxs(Div, { className: `space-y-4 ${className}`, children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to vote in this poll. Please log in or create an account to continue." }), _jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-200", children: isMulti ? "Select all that apply:" : "Choose one:" }), _jsx(Div, { className: "space-y-2", children: pollConfig.options.map((opt) => (_jsxs("label", { className: "flex items-center gap-3 cursor-pointer rounded-lg border border-zinc-200 dark:border-zinc-700 px-4 py-3 hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors", children: [_jsx("input", { type: isMulti ? "checkbox" : "radio", name: `poll-${eventId}`, value: opt.id, checked: selectedVotes.includes(opt.id), onChange: () => toggleVote(opt.id), className: "accent-primary" }), _jsx(Span, { size: "sm", className: "text-zinc-700 dark:text-zinc-300", children: opt.label })] }, opt.id))) }), pollConfig.allowComment && (_jsx("textarea", { value: comment, onChange: (e) => setComment(e.target.value), placeholder: "Add a comment (optional)", rows: 3, className: "w-full rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-800 dark:text-zinc-200 placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary" })), error && _jsx(Text, { className: "text-sm text-error", children: error }), _jsx("button", { type: "button", onClick: handleSubmit, disabled: isLoading || selectedVotes.length === 0, className: "w-full rounded-xl bg-primary px-6 py-3 text-sm font-semibold text-white hover:bg-primary-600 disabled:opacity-60", children: isLoading ? "Submitting…" : "Submit Vote" })] }));
|
|
68
69
|
}
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { Button } from "../../../ui/components/Button";
|
|
5
|
-
import { Div, Heading, Stack, Text } from "../../../ui";
|
|
5
|
+
import { Div, Heading, Label, Span, Stack, Text } from "../../../ui";
|
|
6
6
|
import { Badge } from "../../../ui/components/Badge";
|
|
7
|
+
const __P = {
|
|
8
|
+
p6: "p-6",
|
|
9
|
+
};
|
|
7
10
|
/**
|
|
8
11
|
* `EventRaffleEntryForm` — W1-18 — fills the gap noted in the plan ("Raffle
|
|
9
12
|
* entry form (71 LOC stub) — no entry form"). Renders the prize hero,
|
|
@@ -33,5 +36,5 @@ export function EventRaffleEntryForm({ eventId: _eventId, prizeLabel, raffleType
|
|
|
33
36
|
: raffleType === "top_n_participants"
|
|
34
37
|
? `Earliest ${topN ?? "N"} entries qualify.`
|
|
35
38
|
: "Spin the wheel to claim your prize.";
|
|
36
|
-
return (_jsxs(Div, { className:
|
|
39
|
+
return (_jsxs(Div, { className: `rounded-2xl border border-zinc-200 dark:border-zinc-700 ${__P.p6} space-y-4`, children: [prizeLabel ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Badge, { variant: "warning", children: "Prize" }), _jsx(Heading, { level: 2, className: "text-2xl font-bold", children: prizeLabel })] })) : null, _jsx(Text, { className: "text-sm text-zinc-600 dark:text-zinc-400", children: eligibilityCopy }), submitted ? (_jsx(Div, { className: "rounded-lg bg-success-surface px-4 py-3 text-sm text-success", children: "You're in. Check the leaderboard for results." })) : overLimit ? (_jsxs(Div, { className: "rounded-lg bg-warning-surface px-4 py-3 text-sm text-warning", children: ["You've used all ", maxEntriesPerUser, " entry slot", maxEntriesPerUser === 1 ? "" : "s", " for this raffle."] })) : (_jsxs("form", { onSubmit: handleSubmit, className: "space-y-3", children: [_jsxs(Label, { htmlFor: "raffle-message", className: "block text-sm font-medium", children: ["Message ", _jsx(Span, { className: "text-zinc-400", children: "(optional)" })] }), _jsx("textarea", { id: "raffle-message", value: message, onChange: (e) => setMessage(e.target.value), rows: 3, maxLength: 500, className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 px-3 py-2 text-sm bg-white dark:bg-zinc-900 focus:outline-none focus:ring-2 focus:ring-[var(--appkit-color-primary)]", placeholder: "Share why you'd like to win", disabled: busy }), _jsx(Button, { type: "submit", variant: "primary", disabled: busy, children: busy ? "Submitting…" : "Enter raffle" })] }))] }));
|
|
37
40
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Article, ClaimCouponButton, Div, Heading, Span, Text, TextLink } from "../../../ui";
|
|
3
|
+
const __P = {
|
|
4
|
+
p4: "p-4",
|
|
5
|
+
};
|
|
6
|
+
const CLS_PANEL = "rounded-2xl border border-amber-200 bg-amber-50/60 dark:border-amber-700 dark:bg-amber-900/20 p-6 space-y-3";
|
|
7
|
+
const CLS_LABEL = "text-xs uppercase tracking-wide text-amber-700 dark:text-amber-300";
|
|
8
|
+
const CLS_WINNER_NAME = "mt-1 text-2xl font-bold text-amber-900 dark:text-amber-100";
|
|
3
9
|
const DEFAULT_LABELS = {
|
|
4
10
|
heading: "Raffle Winner",
|
|
5
11
|
winnerLabel: "Winner",
|
|
@@ -14,5 +20,5 @@ export function EventRaffleWinnerView({ event, currentUserIsWinner, labels }) {
|
|
|
14
20
|
if (!event.raffleWinnerUserId) {
|
|
15
21
|
return (_jsx(Div, { className: "rounded-xl border border-dashed border-zinc-300 dark:border-zinc-700 px-6 py-10 text-center", children: _jsx(Text, { className: "text-zinc-500 dark:text-zinc-400", children: l.notDrawnYet }) }));
|
|
16
22
|
}
|
|
17
|
-
return (_jsxs(Article, { className: "space-y-4", children: [_jsxs(Heading, { level: 2, className: "text-xl font-semibold text-zinc-900 dark:text-zinc-100", children: ["\uD83C\uDF9F\uFE0F ", l.heading] }), _jsxs(Div, { className:
|
|
23
|
+
return (_jsxs(Article, { className: "space-y-4", children: [_jsxs(Heading, { level: 2, className: "text-xl font-semibold text-zinc-900 dark:text-zinc-100", children: ["\uD83C\uDF9F\uFE0F ", l.heading] }), _jsxs(Div, { className: CLS_PANEL, children: [_jsxs(Div, { children: [_jsx(Text, { className: CLS_LABEL, children: l.winnerLabel }), _jsx(Text, { className: CLS_WINNER_NAME, children: event.raffleWinnerDisplayName?.trim() || l.anonymous })] }), event.rafflePrize ? (_jsxs(Div, { children: [_jsx(Text, { className: CLS_LABEL, children: l.prizeLabel }), _jsx(Text, { className: "mt-1 text-base text-zinc-800 dark:text-zinc-100", children: event.rafflePrize })] })) : null, currentUserIsWinner && event.rafflePrizeCouponCode ? (_jsx(Div, { className: "pt-2", children: _jsx(ClaimCouponButton, { couponCode: event.rafflePrizeCouponCode, source: "raffle", size: "sm" }) })) : null, _jsxs(Div, { className: "flex flex-wrap gap-x-6 gap-y-2 pt-2 text-sm text-zinc-600 dark:text-zinc-300", children: [typeof event.raffleEntryCount === "number" ? (_jsxs(Span, { children: [l.poolLabel, ":", " ", _jsx(Span, { weight: "medium", className: "text-zinc-900 dark:text-zinc-100", children: event.raffleEntryCount })] })) : null, event.raffleTriggeredAt ? (_jsx(Span, { children: new Date(event.raffleTriggeredAt).toLocaleString() })) : null] })] }), event.raffleGithubFunctionUrl ? (_jsxs(Div, { className: `rounded-lg border border-zinc-200 dark:border-zinc-700 ${__P.p4} text-sm`, children: [_jsx(Text, { className: "font-medium text-zinc-700 dark:text-zinc-200", children: l.fairnessLabel }), _jsx(TextLink, { href: event.raffleGithubFunctionUrl, className: "mt-1 block break-all text-primary-600 dark:text-primary-400 hover:underline", children: event.raffleGithubFunctionUrl })] })) : null] }));
|
|
18
24
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import Link from "next/link";
|
|
3
|
-
import { Container, Heading, Section, Stack, Text, } from "../../../ui";
|
|
3
|
+
import { Container, Div, Heading, Section, Stack, Text, } from "../../../ui";
|
|
4
4
|
import { ROUTES } from "../../../next";
|
|
5
5
|
import { eventRepository } from "../repository/events.repository";
|
|
6
6
|
import { EventCard } from "./EventCard";
|
|
@@ -24,5 +24,5 @@ export async function EventRafflesSection({ config, }) {
|
|
|
24
24
|
catch {
|
|
25
25
|
events = [];
|
|
26
26
|
}
|
|
27
|
-
return (_jsx(Section, { className: "py-10", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, className: "text-2xl font-semibold text-zinc-900 dark:text-zinc-50", children: title }), subtitle ? (_jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: subtitle })) : null] }), events.length === 0 ? (_jsxs(Stack, { align: "center", gap: "sm", className: "rounded-2xl border border-dashed border-zinc-300 px-6 py-12 text-center dark:border-slate-700", children: [_jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "No upcoming raffles \u2014 keep an eye on the events page." }), _jsx(Link, { href: String(ROUTES.PUBLIC.EVENTS), className: "text-sm font-medium text-primary hover:underline", children: "Browse all events \u2192" })] })) : (_jsx(
|
|
27
|
+
return (_jsx(Section, { className: "py-10", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, className: "text-2xl font-semibold text-zinc-900 dark:text-zinc-50", children: title }), subtitle ? (_jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: subtitle })) : null] }), events.length === 0 ? (_jsxs(Stack, { align: "center", gap: "sm", className: "rounded-2xl border border-dashed border-zinc-300 px-6 py-12 text-center dark:border-slate-700", children: [_jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "No upcoming raffles \u2014 keep an eye on the events page." }), _jsx(Link, { href: String(ROUTES.PUBLIC.EVENTS), className: "text-sm font-medium text-primary hover:underline", children: "Browse all events \u2192" })] })) : (_jsx(Div, { className: "fluid-grid-card gap-3", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) }))] }) }) }));
|
|
28
28
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import Link from "next/link";
|
|
4
|
-
import { Div, Heading, Stack, Text } from "../../../ui";
|
|
4
|
+
import { Div, Heading, Span, Stack, Text } from "../../../ui";
|
|
5
5
|
import { Button } from "../../../ui/components/Button";
|
|
6
6
|
import { ROUTES } from "../../../next/routing/route-map";
|
|
7
|
+
const __P = {
|
|
8
|
+
p6: "p-6",
|
|
9
|
+
};
|
|
7
10
|
/**
|
|
8
11
|
* `EventSaleBanner` — W1-18 — sale event UI noted as missing in the plan.
|
|
9
12
|
* Renders a hero banner with the discount, optional coupon, and a CTA to the
|
|
@@ -14,5 +17,5 @@ export function EventSaleBanner({ discountPercent, couponCode, title, subtitle,
|
|
|
14
17
|
const href = filterParam
|
|
15
18
|
? `${ROUTES.PUBLIC.PRODUCTS}?${encodeURIComponent(filterParam.key)}=${encodeURIComponent(filterParam.value)}`
|
|
16
19
|
: `${ROUTES.PUBLIC.PRODUCTS}?onSale=true`;
|
|
17
|
-
return (_jsx(Div, { className:
|
|
20
|
+
return (_jsx(Div, { className: `rounded-2xl bg-gradient-to-br from-[var(--appkit-color-primary)] to-[var(--appkit-color-secondary)] ${__P.p6} text-white shadow-lg`, children: _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, className: "text-3xl font-bold", children: headline }), subtitle ? _jsx(Text, { className: "text-base text-white/90", children: subtitle }) : null] }), couponCode ? (_jsxs(Div, { className: "inline-flex items-center gap-3 rounded-lg bg-white/10 px-4 py-2 backdrop-blur", children: [_jsx(Span, { size: "xs", className: "uppercase tracking-wide opacity-80", children: "Code" }), _jsx(Span, { size: "lg", weight: "bold", className: "font-mono", children: couponCode })] })) : null, _jsx(Div, { children: _jsx(Link, { href: href, children: _jsx(Button, { variant: "secondary", children: "Shop the sale" }) }) })] }) }));
|
|
18
21
|
}
|
|
@@ -3,13 +3,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useCallback, useMemo } from "react";
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { useEvents } from "../hooks/useEvents";
|
|
6
|
-
import { ListingFilterDrawer, ListingToolbar, Pagination, Text } from "../../../ui";
|
|
6
|
+
import { Div, ListingFilterDrawer, ListingToolbar, Pagination, Text } from "../../../ui";
|
|
7
7
|
import { EventCard } from "./EventCard";
|
|
8
8
|
import { EventFilters, EVENT_PUBLIC_SORT_OPTIONS } from "./EventFilters";
|
|
9
9
|
import { EVENT_FIELDS } from "../../../constants/field-names";
|
|
10
10
|
import { sieveFilter, sieveMultiEq, sieveAnd, SIEVE_OP } from "../../../utils/sieve-builder";
|
|
11
11
|
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
12
12
|
import { sortBy } from "../../../constants/sort";
|
|
13
|
+
const __P = {
|
|
14
|
+
p4: "p-4",
|
|
15
|
+
};
|
|
16
|
+
const __O = {
|
|
17
|
+
hidden: "overflow-hidden",
|
|
18
|
+
};
|
|
13
19
|
const PAGE_SIZE = 24;
|
|
14
20
|
const FILTER_KEYS = [TABLE_KEYS.TYPE, TABLE_KEYS.STATUS, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO, TABLE_KEYS.SHOW_EXPIRED];
|
|
15
21
|
export function EventsIndexListing({ initialData }) {
|
|
@@ -115,5 +121,5 @@ export function EventsIndexListing({ initialData }) {
|
|
|
115
121
|
const commitSearch = useCallback(() => {
|
|
116
122
|
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
117
123
|
}, [searchInput, table]);
|
|
118
|
-
return (_jsxs(
|
|
124
|
+
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || sortBy(EVENT_FIELDS.STARTS_AT, "ASC"), sortOptions: EVENT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), 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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx(Div, { className: "py-6", children: isLoading ? (_jsx(Div, { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Div, { className: `rounded-xl border border-zinc-100 dark:border-slate-700 ${__O.hidden} animate-pulse`, children: [_jsx(Div, { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs(Div, { className: `${__P.p4} space-y-2`, children: [_jsx(Div, { className: "h-4 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-full" }), _jsx(Div, { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx(Div, { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded mt-2" })] })] }, i))) })) : events.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No events found." })) : view === "list" ? (_jsx(Div, { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) : (_jsx(Div, { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(EventFilters, { table: pendingTable, variant: "public" }) })] }));
|
|
119
125
|
}
|