@mohasinac/appkit 2.7.56 → 2.7.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +7 -1
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -2
- package/dist/_internal/shared/actions/action-registry.js +31 -5
- package/dist/client.d.ts +2 -4
- package/dist/client.js +2 -3
- package/dist/configs/tailwind.d.ts +16 -3
- package/dist/configs/tailwind.js +18 -5
- package/dist/constants/field-names.d.ts +3 -0
- package/dist/constants/field-names.js +3 -0
- package/dist/features/about/components/FAQPageView.js +2 -2
- package/dist/features/about/components/FeesView.js +1 -1
- package/dist/features/about/components/PolicyPageView.js +1 -1
- package/dist/features/about/components/PublicProfileView.js +1 -1
- package/dist/features/account/components/AddressBook.js +1 -1
- package/dist/features/account/components/AddressesIndexListing.js +1 -1
- package/dist/features/account/components/BuyerAccountGuideView.js +7 -7
- package/dist/features/account/components/BuyerAuctionsGuideView.js +7 -7
- package/dist/features/account/components/BuyerOrdersGuideView.js +6 -6
- package/dist/features/account/components/BuyerShoppingGuideView.js +7 -7
- package/dist/features/account/components/MessagesView.js +1 -1
- package/dist/features/account/components/NotificationPreferencesPanel.js +12 -3
- package/dist/features/account/components/UserOffersPanel.js +2 -2
- package/dist/features/account/components/UserSidebar.js +3 -3
- package/dist/features/account/components/UserSupportView.js +5 -5
- package/dist/features/admin/components/AdminAdsView.js +1 -1
- package/dist/features/admin/components/AdminAllEventEntriesView.js +2 -2
- package/dist/features/admin/components/AdminAnalyticsGuideView.js +3 -3
- package/dist/features/admin/components/AdminBidsView.js +6 -2
- package/dist/features/admin/components/AdminBlogView.js +7 -2
- package/dist/features/admin/components/AdminBrandsView.js +7 -2
- package/dist/features/admin/components/AdminBundleEditorView.js +13 -9
- package/dist/features/admin/components/AdminBundlesView.js +4 -2
- package/dist/features/admin/components/AdminCarouselEditorView.js +7 -8
- package/dist/features/admin/components/AdminCarouselView.js +6 -4
- package/dist/features/admin/components/AdminCartsView.js +1 -1
- package/dist/features/admin/components/AdminCatalogGuideView.js +5 -5
- package/dist/features/admin/components/AdminCategoriesView.js +7 -2
- package/dist/features/admin/components/AdminContactEditorView.js +2 -2
- package/dist/features/admin/components/AdminContactView.js +4 -2
- package/dist/features/admin/components/AdminContentGuideView.js +6 -6
- package/dist/features/admin/components/AdminCouponsView.js +4 -2
- package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -1
- package/dist/features/admin/components/AdminFaqsView.js +4 -2
- package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -1
- package/dist/features/admin/components/AdminFeaturesView.js +5 -1
- package/dist/features/admin/components/AdminGuideHubView.js +1 -1
- package/dist/features/admin/components/AdminHistoryView.js +1 -1
- package/dist/features/admin/components/AdminListingScaffold.js +1 -1
- package/dist/features/admin/components/AdminMediaView.js +10 -5
- package/dist/features/admin/components/AdminNavigationView.js +1 -1
- package/dist/features/admin/components/AdminNewsletterView.js +4 -2
- package/dist/features/admin/components/AdminNotificationsView.js +7 -2
- package/dist/features/admin/components/AdminOrdersGuideView.js +5 -5
- package/dist/features/admin/components/AdminOrdersView.js +11 -2
- package/dist/features/admin/components/AdminPayoutsView.js +6 -2
- package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -0
- package/dist/features/admin/components/AdminProductsView.js +14 -2
- package/dist/features/admin/components/AdminReturnRequestsView.js +1 -1
- package/dist/features/admin/components/AdminReviewsView.js +7 -2
- package/dist/features/admin/components/AdminScammerEditorView.js +3 -3
- package/dist/features/admin/components/AdminScammersView.js +5 -5
- package/dist/features/admin/components/AdminSectionsView.js +2 -2
- package/dist/features/admin/components/AdminSessionsView.js +6 -2
- package/dist/features/admin/components/AdminSidebar.js +3 -3
- package/dist/features/admin/components/AdminSiteSettingsView.js +1 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +1 -1
- package/dist/features/admin/components/AdminStoreEditorView.js +1 -1
- package/dist/features/admin/components/AdminStoresGuideView.js +6 -6
- package/dist/features/admin/components/AdminStoresView.js +7 -2
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +3 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +4 -4
- package/dist/features/admin/components/AdminSupportTicketsView.js +8 -8
- package/dist/features/admin/components/AdminTeamGuideView.js +4 -4
- package/dist/features/admin/components/AdminTeamView.js +4 -2
- package/dist/features/admin/components/AdminTrustGuideView.js +5 -5
- package/dist/features/admin/components/AdminUserEditorView.js +2 -2
- package/dist/features/admin/components/AdminUsersGuideView.js +5 -5
- package/dist/features/admin/components/AdminUsersView.js +6 -2
- package/dist/features/admin/components/AdminViewCards.js +3 -3
- package/dist/features/admin/components/AdminWishlistsView.js +1 -1
- package/dist/features/admin/components/DataTable.js +1 -1
- package/dist/features/admin/constants/filter-tabs.d.ts +9 -0
- package/dist/features/admin/constants/filter-tabs.js +3 -0
- package/dist/features/auctions/components/AuctionBidsTable.js +1 -1
- package/dist/features/auctions/components/AuctionBottomActions.d.ts +7 -0
- package/dist/features/auctions/components/AuctionBottomActions.js +24 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +7 -7
- package/dist/features/auctions/components/AuctionsListView.js +1 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +2 -2
- package/dist/features/auctions/components/MarketplaceAuctionGrid.js +1 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +1 -1
- package/dist/features/auth/components/LoginForm.js +1 -1
- package/dist/features/auth/components/VerifyEmailView.js +2 -2
- package/dist/features/before-after/components/BeforeAfterGallery.js +1 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +1 -1
- package/dist/features/blog/components/BlogIndexListing.js +1 -1
- package/dist/features/blog/components/BlogListView.js +2 -2
- package/dist/features/blog/components/BlogPostView.js +8 -8
- package/dist/features/cart/components/CartDrawer.js +2 -2
- package/dist/features/cart/repository/cart.repository.js +11 -4
- package/dist/features/cart/schemas/firestore.d.ts +3 -0
- package/dist/features/categories/components/BrandDetailPageView.js +1 -1
- package/dist/features/categories/components/CategoriesIndexListing.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +39 -3
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +4 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +7 -2
- package/dist/features/categories/components/CategoryGrid.js +1 -1
- package/dist/features/categories/components/CategoryProductsView.js +1 -1
- package/dist/features/categories/components/CategoryStoresListing.d.ts +5 -0
- package/dist/features/categories/components/CategoryStoresListing.js +11 -0
- package/dist/features/categories/components/CategoryTableColumns.js +2 -2
- package/dist/features/categories/components/CategoryTree.js +1 -1
- package/dist/features/categories/components/index.d.ts +2 -0
- package/dist/features/categories/components/index.js +1 -0
- package/dist/features/checkout/components/CheckoutStepper.js +2 -2
- package/dist/features/classified/components/ClassifiedIndexListing.js +2 -0
- package/dist/features/classified/components/ClassifiedListView.js +1 -1
- package/dist/features/collections/components/CollectionCard.js +1 -1
- package/dist/features/contact/components/ContactInfoSidebar.js +1 -1
- package/dist/features/copilot/components/AdminCopilotView.js +2 -2
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +2 -0
- package/dist/features/digital-codes/components/DigitalCodesListView.js +1 -1
- package/dist/features/events/components/AdminEventsView.js +5 -1
- package/dist/features/events/components/EventCard.js +1 -1
- package/dist/features/events/components/EventPollWidget.js +3 -3
- package/dist/features/events/components/EventStatusBadge.js +5 -5
- package/dist/features/events/components/EventsIndexListing.js +1 -1
- package/dist/features/events/components/EventsListView.js +2 -2
- package/dist/features/events/components/SpinWheelView.js +5 -3
- package/dist/features/faq/components/FAQAccordion.js +2 -2
- package/dist/features/filters/AsyncFacetSection.js +3 -3
- package/dist/features/filters/FilterFacetSection.js +5 -5
- package/dist/features/filters/RangeFilter.js +1 -1
- package/dist/features/forms/Input.js +5 -5
- package/dist/features/forms/Select.js +3 -3
- package/dist/features/forms/Slider.js +1 -1
- package/dist/features/forms/Textarea.js +3 -3
- package/dist/features/forms/utils.d.ts +2 -2
- package/dist/features/forms/utils.js +2 -2
- package/dist/features/history/hooks/useHistory.js +23 -10
- package/dist/features/homepage/components/AdvertisementBanner.js +2 -2
- package/dist/features/homepage/components/CharacterHotspotForm.js +2 -2
- package/dist/features/homepage/components/FeaturedResultsSection.js +1 -1
- package/dist/features/homepage/components/HeroSection.js +1 -1
- package/dist/features/homepage/components/NewsletterSection.js +1 -1
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/layout/BottomActions.js +2 -2
- package/dist/features/layout/ListingLayout.js +3 -3
- package/dist/features/layout/TitleBarLayout.js +1 -1
- package/dist/features/live/components/LiveItemsIndexListing.js +2 -0
- package/dist/features/live/components/LiveItemsListView.js +1 -1
- package/dist/features/media/index.d.ts +2 -0
- package/dist/features/media/index.js +1 -0
- package/dist/features/media/modals/ImageCropModal.js +1 -1
- package/dist/features/media/modals/ImageEditor.d.ts +12 -0
- package/dist/features/media/modals/ImageEditor.js +73 -0
- package/dist/features/media/upload/ImageUpload.d.ts +5 -1
- package/dist/features/media/upload/ImageUpload.js +33 -8
- package/dist/features/media/upload/MediaUploadField.d.ts +5 -1
- package/dist/features/media/upload/MediaUploadField.js +26 -12
- package/dist/features/orders/components/OrdersList.js +12 -12
- package/dist/features/orders/components/RefundHistoryTable.js +2 -2
- package/dist/features/orders/components/RefundRequestView.js +3 -3
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
- package/dist/features/pre-orders/components/PreOrderBadge.js +5 -5
- package/dist/features/pre-orders/components/PreOrderBottomActions.d.ts +5 -0
- package/dist/features/pre-orders/components/PreOrderBottomActions.js +22 -0
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +5 -5
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +47 -0
- package/dist/features/pre-orders/components/PreOrdersListView.js +1 -1
- package/dist/features/pre-orders/components/PreorderCard.js +3 -3
- package/dist/features/products/components/AuctionsIndexListing.js +23 -0
- package/dist/features/products/components/BidHistory.js +2 -2
- package/dist/features/products/components/CompareOverlay.js +1 -1
- package/dist/features/products/components/CustomFieldsEditor.js +1 -1
- package/dist/features/products/components/CustomSectionTabContent.js +1 -1
- package/dist/features/products/components/CustomSectionsEditor.js +1 -1
- package/dist/features/products/components/GroupSettingsPanel.js +2 -2
- package/dist/features/products/components/MakeOfferButton.js +2 -2
- package/dist/features/products/components/PrizeDrawBottomActions.d.ts +6 -0
- package/dist/features/products/components/PrizeDrawBottomActions.js +24 -0
- package/dist/features/products/components/PrizeDrawDetailPageView.js +5 -7
- package/dist/features/products/components/PrizeDrawEntryActions.js +5 -18
- package/dist/features/products/components/PrizeDrawItemsEditor.js +3 -3
- package/dist/features/products/components/PrizeRevealModal.js +7 -4
- package/dist/features/products/components/ProductDetailActions.d.ts +6 -1
- package/dist/features/products/components/ProductDetailActions.js +36 -6
- package/dist/features/products/components/ProductDetailPageView.js +19 -18
- package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
- package/dist/features/products/components/ProductGalleryClient.js +1 -1
- package/dist/features/products/components/ProductGrid.js +8 -8
- package/dist/features/products/components/ProductTabs.js +1 -1
- package/dist/features/products/components/ProductTabsShell.js +1 -1
- package/dist/features/products/components/ProductsIndexListing.js +47 -0
- package/dist/features/products/components/RelatedProducts.js +1 -1
- package/dist/features/products/components/ShowGroupSection.js +1 -1
- package/dist/features/products/components/SublistingCarouselSection.js +1 -1
- package/dist/features/products/components/SublistingCategorySelect.js +1 -1
- package/dist/features/products/components/index.d.ts +0 -2
- package/dist/features/products/components/index.js +0 -1
- package/dist/features/products/constants/action-defs.d.ts +7 -0
- package/dist/features/products/constants/action-defs.js +14 -0
- package/dist/features/products/constants/listing-tabs.d.ts +6 -0
- package/dist/features/products/constants/listing-tabs.js +1 -0
- package/dist/features/promotions/components/CouponCard.js +3 -3
- package/dist/features/promotions/components/CouponsIndexListing.js +2 -2
- package/dist/features/promotions/components/PromotionsView.js +1 -1
- package/dist/features/reviews/components/ReviewDetailPageView.js +1 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +4 -4
- package/dist/features/reviews/components/ReviewModal.js +2 -2
- package/dist/features/reviews/components/ReviewSummary.js +1 -1
- package/dist/features/reviews/components/ReviewsIndexListing.js +1 -1
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +4 -3
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +2 -2
- package/dist/features/seller/components/SellerAuctionsView.js +11 -4
- package/dist/features/seller/components/SellerBidsView.js +22 -7
- package/dist/features/seller/components/SellerBundlesView.js +11 -4
- package/dist/features/seller/components/SellerClassifiedView.js +12 -5
- package/dist/features/seller/components/SellerCouponEditorView.js +2 -2
- package/dist/features/seller/components/SellerCouponsView.js +13 -4
- package/dist/features/seller/components/SellerDigitalCodesView.js +11 -4
- package/dist/features/seller/components/SellerFeaturesView.js +1 -1
- package/dist/features/seller/components/SellerGoogleReviewsView.js +10 -5
- package/dist/features/seller/components/SellerGroupedListingsView.js +2 -0
- package/dist/features/seller/components/SellerLiveView.js +13 -6
- package/dist/features/seller/components/SellerOffersPanel.js +2 -2
- package/dist/features/seller/components/SellerOffersView.js +2 -2
- package/dist/features/seller/components/SellerOrdersView.js +41 -18
- package/dist/features/seller/components/SellerPayoutMethodsView.js +14 -5
- package/dist/features/seller/components/SellerPayoutsView.js +2 -2
- package/dist/features/seller/components/SellerPreOrdersView.js +7 -2
- package/dist/features/seller/components/SellerPrizeDrawsView.js +7 -2
- package/dist/features/seller/components/SellerProductShell.js +27 -9
- package/dist/features/seller/components/SellerProductsCards.js +2 -1
- package/dist/features/seller/components/SellerProductsView.js +22 -9
- package/dist/features/seller/components/SellerReviewsView.js +9 -1
- package/dist/features/seller/components/SellerShippingConfigsView.js +15 -6
- package/dist/features/seller/components/SellerSidebar.js +2 -2
- package/dist/features/seller/components/SellerStoreCategoriesView.js +11 -4
- package/dist/features/seller/components/SellerTemplatesView.js +20 -7
- package/dist/features/seller/components/analytics/SellerTopProducts.js +1 -1
- package/dist/features/shell/StepForm.js +9 -3
- package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
- package/dist/features/site-settings/components/NavPermissionsManager.js +1 -1
- package/dist/features/stores/components/InteractiveStoreCard.js +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +1 -1
- package/dist/features/stores/components/StoreFinanceGuideView.js +2 -2
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/features/stores/components/StoreListingsGuideView.js +3 -3
- package/dist/features/stores/components/StoreOrdersGuideView.js +4 -4
- package/dist/features/stores/components/StoreReviewsListing.js +1 -1
- package/dist/features/stores/components/StoreSettingsGuideView.js +2 -2
- package/dist/features/stores/components/StoresIndexListing.js +17 -1
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +4 -4
- package/dist/features/wishlist/components/WishlistPage.js +3 -3
- package/dist/features/wishlist/hooks/useWishlistToggle.js +6 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +9 -3
- package/dist/react/hooks/use-action-dispatch.d.ts +5 -0
- package/dist/react/hooks/use-action-dispatch.js +10 -0
- package/dist/seed/cart-seed-data.js +50 -3
- package/dist/seed/categories-seed-data.js +80 -16
- package/dist/seed/index.d.ts +3 -0
- package/dist/seed/index.js +3 -0
- package/dist/seed/manifest.js +15 -1
- package/dist/seed/products-classifieds-seed-data.d.ts +10 -0
- package/dist/seed/products-classifieds-seed-data.js +197 -0
- package/dist/seed/products-digital-codes-seed-data.d.ts +10 -0
- package/dist/seed/products-digital-codes-seed-data.js +204 -0
- package/dist/seed/products-live-items-seed-data.d.ts +13 -0
- package/dist/seed/products-live-items-seed-data.js +190 -0
- package/dist/styles.css +301 -3
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/index.d.ts +3 -3
- package/dist/tokens/index.js +3 -3
- package/dist/tokens/motion.d.ts +162 -0
- package/dist/tokens/motion.js +89 -0
- package/dist/ui/DataTable.js +6 -5
- package/dist/ui/components/Accordion.js +2 -1
- package/dist/ui/components/Button.style.css +15 -0
- package/dist/ui/components/Card.d.ts +5 -2
- package/dist/ui/components/Card.js +29 -11
- package/dist/ui/components/Div.d.ts +2 -16
- package/dist/ui/components/Div.js +2 -16
- package/dist/ui/components/Input.js +1 -1
- package/dist/ui/components/Layout.d.ts +9 -19
- package/dist/ui/components/Layout.js +9 -7
- package/dist/ui/components/ListingToolbar.js +1 -1
- package/dist/ui/components/Modal.js +7 -4
- package/dist/ui/components/Motion.d.ts +75 -0
- package/dist/ui/components/Motion.js +122 -0
- package/dist/ui/components/Select.js +1 -1
- package/dist/ui/components/Semantic.d.ts +81 -14
- package/dist/ui/components/Semantic.js +75 -10
- package/dist/ui/components/Semantic.style.css +261 -2
- package/dist/ui/components/SideDrawer.d.ts +1 -1
- package/dist/ui/components/SideDrawer.js +8 -6
- package/dist/ui/components/SideModal.d.ts +1 -1
- package/dist/ui/components/SideModal.js +11 -9
- package/dist/ui/components/StackedViewShell.js +6 -1
- package/dist/ui/components/StatsGrid.js +1 -1
- package/dist/ui/components/Toast.js +5 -2
- package/dist/ui/components/Typography.d.ts +44 -3
- package/dist/ui/components/Typography.js +15 -30
- package/dist/ui/components/Typography.style.css +24 -0
- package/dist/ui/components/VacationBanner.js +2 -1
- package/dist/ui/components/surface-tokens.d.ts +70 -0
- package/dist/ui/components/surface-tokens.js +68 -0
- package/dist/ui/forms/FormShell.d.ts +6 -1
- package/dist/ui/forms/FormShell.js +32 -4
- package/dist/ui/index.d.ts +9 -6
- package/dist/ui/index.js +4 -4
- package/dist/validation/schemas.d.ts +28 -28
- package/package.json +3 -1
- package/dist/_internal/client/features/seller/print-center/InventoryLabel.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/InventoryLabel.js +0 -49
- package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.d.ts +0 -7
- package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.js +0 -32
- package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.d.ts +0 -28
- package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.js +0 -33
- package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.js +0 -30
- package/dist/_internal/client/features/seller/print-center/PrintCenterView.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/PrintCenterView.js +0 -102
- package/dist/_internal/client/features/seller/print-center/PrintGrid.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/PrintGrid.js +0 -13
- package/dist/_internal/client/features/seller/print-center/StoreCard.d.ts +0 -19
- package/dist/_internal/client/features/seller/print-center/StoreCard.js +0 -30
- package/dist/_internal/client/features/seller/print-center/WebsiteCard.d.ts +0 -11
- package/dist/_internal/client/features/seller/print-center/WebsiteCard.js +0 -25
- package/dist/_internal/client/features/seller/print-center/index.d.ts +0 -11
- package/dist/_internal/client/features/seller/print-center/index.js +0 -10
- package/dist/_internal/client/features/seller/print-center/types.d.ts +0 -20
- package/dist/_internal/client/features/seller/print-center/types.js +0 -20
- package/dist/_internal/client/features/seller/print-center/useInventoryPdf.d.ts +0 -18
- package/dist/_internal/client/features/seller/print-center/useInventoryPdf.js +0 -76
- package/dist/features/account/repository/address.repository.d.ts +0 -17
- package/dist/features/account/repository/address.repository.js +0 -184
- package/dist/features/auctions/components/AuctionCard.d.ts +0 -29
- package/dist/features/auctions/components/AuctionCard.js +0 -38
- package/dist/features/categories/components/BundleAddToCartCta.d.ts +0 -16
- package/dist/features/categories/components/BundleAddToCartCta.js +0 -54
- package/dist/features/categories/components/CategoriesListView.d.ts +0 -31
- package/dist/features/categories/components/CategoriesListView.js +0 -27
- package/dist/features/products/components/AuctionsView.d.ts +0 -6
- package/dist/features/products/components/AuctionsView.js +0 -5
- package/dist/features/products/components/BuyBar.d.ts +0 -21
- package/dist/features/products/components/BuyBar.js +0 -19
- package/dist/features/products/components/PreOrdersView.d.ts +0 -6
- package/dist/features/products/components/PreOrdersView.js +0 -5
- package/dist/features/reviews/components/ReviewsListView.d.ts +0 -31
- package/dist/features/reviews/components/ReviewsListView.js +0 -16
- package/dist/features/stores/components/StoresListView.d.ts +0 -19
- package/dist/features/stores/components/StoresListView.js +0 -25
- package/dist/features/stores/repository/store-address.repository.d.ts +0 -55
- package/dist/features/stores/repository/store-address.repository.js +0 -187
- package/dist/ui/components/BulkActionsBar.d.ts +0 -18
- package/dist/ui/components/BulkActionsBar.js +0 -25
- package/dist/ui/components/CollageLightbox.d.ts +0 -18
- package/dist/ui/components/CollageLightbox.js +0 -41
- package/dist/ui/components/StickyBottomBar.d.ts +0 -22
- package/dist/ui/components/StickyBottomBar.js +0 -20
|
@@ -10,6 +10,7 @@ import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } fr
|
|
|
10
10
|
import { DataTable } from "./DataTable";
|
|
11
11
|
import { BUNDLE_COPY, BUNDLE_STOCK_VARIANT, } from "../../../_internal/shared/features/categories/bundle-copy";
|
|
12
12
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
13
|
+
import { useBottomActions } from "../../layout";
|
|
13
14
|
const PAGE_SIZE = 25;
|
|
14
15
|
const FILTER_KEYS = ["isActive", "bundleStockStatus"];
|
|
15
16
|
const DEFAULT_SORT = "name";
|
|
@@ -53,7 +54,7 @@ const COLUMNS = [
|
|
|
53
54
|
function BundlesFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
54
55
|
if (!filterOpen)
|
|
55
56
|
return null;
|
|
56
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-
|
|
57
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [
|
|
57
58
|
{ label: "All", value: "" },
|
|
58
59
|
{ label: "Active", value: "true" },
|
|
59
60
|
{ label: "Inactive", value: "false" },
|
|
@@ -189,9 +190,10 @@ export function AdminBundlesView({ getEditHref, newHref }) {
|
|
|
189
190
|
},
|
|
190
191
|
},
|
|
191
192
|
];
|
|
193
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
192
194
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(Heading, { level: 1, className: "sr-only", children: "Bundles" }), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search bundles by name or slug\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
|
|
193
195
|
table.set("sort", v);
|
|
194
|
-
}, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsx(Button, { asChild: true, size: "sm", variant: "primary", children: _jsxs("a", { href: newHref, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), BUNDLE_COPY.adminList.newButton] }) }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions }), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-
|
|
196
|
+
}, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsx(Button, { asChild: true, size: "sm", variant: "primary", children: _jsxs("a", { href: newHref, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), BUNDLE_COPY.adminList.newButton] }) }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions }), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), _jsx(DataTable, { columns: COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: BUNDLE_COPY.adminList.empty, getRowHref: getEditHref, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next
|
|
195
197
|
? selection.setSelectedIds(rows.map((r) => r.id))
|
|
196
198
|
: selection.clearSelection(), renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", isLoading: rebuildingId === row.id, disabled: rebuildingId === row.id, onClick: () => void handleRebuild(row.id), children: ACTIONS.ADMIN["rebuild-bundle"].label })) })] }), _jsx(BundlesFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }));
|
|
197
199
|
}
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
5
|
-
import { Alert, Button, Form, FormActions, Input, Select, StackedViewShell, Toggle, Div, Text, Heading, Row, } from "../../../ui";
|
|
5
|
+
import { Alert, Button, ConfirmDeleteModal, Form, FormActions, Input, Select, StackedViewShell, Toggle, Div, Text, Heading, Row, } from "../../../ui";
|
|
6
6
|
import { FieldInput, FormShellContext, useFormShellState } from "../../../ui/forms";
|
|
7
7
|
import { apiClient } from "../../../http";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
9
10
|
const CLS_PANEL = "rounded-xl border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-4 space-y-3";
|
|
10
11
|
const CLS_ROW_BETWEEN = "flex items-center justify-between";
|
|
11
12
|
const CLS_SECTION_HEADING = "text-sm font-semibold text-zinc-900 dark:text-zinc-100";
|
|
@@ -55,7 +56,7 @@ const TEXT_ALIGN_OPTIONS = [
|
|
|
55
56
|
// ── Zone Picker ──────────────────────────────────────────────────────────────
|
|
56
57
|
function ZonePicker({ selected, onChange, disabled, }) {
|
|
57
58
|
const zones = [1, 2, 3, 4, 5, 6];
|
|
58
|
-
return (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-zinc-500 mb-1", children: "Zone (row \u00D7 col grid)" }), _jsx("div", { className: "grid gap-1", style: { gridTemplateRows: "repeat(2, 1fr)", gridTemplateColumns: "repeat(3, 1fr)", width: 150 }, children: zones.map((z) => {
|
|
59
|
+
return (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "Zone (row \u00D7 col grid)" }), _jsx("div", { className: "grid gap-1", style: { gridTemplateRows: "repeat(2, 1fr)", gridTemplateColumns: "repeat(3, 1fr)", width: 150 }, children: zones.map((z) => {
|
|
59
60
|
const isDisabled = disabled?.(z) ?? false;
|
|
60
61
|
return (_jsx("button", { type: "button", disabled: isDisabled, onClick: () => !isDisabled && onChange(z), className: `rounded text-xs font-mono py-2 border transition-colors ${selected === z
|
|
61
62
|
? "bg-primary text-white border-primary"
|
|
@@ -79,7 +80,7 @@ function CardEditor({ card, index, otherZones, onChange, onRemove, }) {
|
|
|
79
80
|
{ value: "", label: "— inherit desktop zone —" },
|
|
80
81
|
{ value: "2", label: "Zone 2 (row 1, center)" },
|
|
81
82
|
{ value: "5", label: "Zone 5 (row 2, center)" },
|
|
82
|
-
] }), _jsxs(Div, { children: [_jsx(Text, { className: "text-sm font-medium mb-2", children: "Background" }), _jsx(BackgroundEditor, { value: card.background, onChange: (bg) => set({ background: bg }), prefix: `card-${index}` })] }), _jsxs(Div, { className: "space-y-2", children: [_jsx(Text, { className: "text-sm font-medium", children: "Content" }), _jsx(Input, { label: "Eyebrow", value: card.content?.eyebrow ?? "", onChange: (e) => setContent({ eyebrow: e.target.value || undefined }), placeholder: "New \u00B7 Limited \u00B7 Featured" }), _jsx(Input, { label: "Title", value: card.content?.title ?? "", onChange: (e) => setContent({ title: e.target.value }) }), _jsx(Input, { label: "Subtitle", value: card.content?.subtitle ?? "", onChange: (e) => setContent({ subtitle: e.target.value || undefined }) }), _jsx(Input, { label: "Description", value: card.content?.description ?? "", onChange: (e) => setContent({ description: e.target.value || undefined }) }), _jsx(Input, { label: "Text colour (CSS)", value: card.content?.textColor ?? "#ffffff", onChange: (e) => setContent({ textColor: e.target.value }) }), _jsx(Select, { label: "Text align", value: card.content?.textAlign ?? "left", onChange: (e) => setContent({ textAlign: e.target.value }), options: TEXT_ALIGN_OPTIONS })] }), _jsxs(Div, { className: "space-y-2", children: [_jsxs(Row, { className: CLS_ROW_BETWEEN, children: [_jsx(Text, { className: "text-sm font-medium", children: "Buttons (max 3)" }), buttons.length < 3 && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => set({ buttons: [...buttons, makeButton()] }), children: "+ Add button" }))] }), buttons.map((btn, bi) => (_jsxs(Div, { className: "rounded-lg border border-zinc-200 dark:border-zinc-800 p-3 space-y-2", children: [_jsxs(Row, { className: CLS_ROW_BETWEEN, children: [_jsxs(Text, { className: "text-xs font-medium text-zinc-500", children: ["Button ", bi + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => set({ buttons: buttons.filter((_, i) => i !== bi) }), children: "\u2715" })] }), _jsx(Input, { label: "Label", value: btn.text, onChange: (e) => { const next = [...buttons]; next[bi] = { ...btn, text: e.target.value }; set({ buttons: next }); } }), _jsx(Input, { label: "Link (href)", value: btn.href, onChange: (e) => { const next = [...buttons]; next[bi] = { ...btn, href: e.target.value }; set({ buttons: next }); }, placeholder: "/products or https://..." }), _jsx(Select, { label: "Variant", value: btn.variant, onChange: (e) => { const next = [...buttons]; next[bi] = { ...btn, variant: e.target.value }; set({ buttons: next }); }, options: VARIANT_OPTIONS }), _jsx(Toggle, { label: "Open in new tab", checked: btn.openInNewTab ?? false, onChange: (v) => { const next = [...buttons]; next[bi] = { ...btn, openInNewTab: v }; set({ buttons: next }); } })] }, btn.id ?? bi)))] }), _jsxs(Div, { className: "space-y-2", children: [_jsx(Text, { className: "text-sm font-medium", children: "Hover effect" }), _jsx(Select, { label: "Effect", value: card.hover?.effect ?? "none", onChange: (e) => set({ hover: { effect: e.target.value } }), options: HOVER_OPTIONS })] }), _jsx(Toggle, { label: "Button-only card (no text content)", checked: card.isButtonOnly ?? false, onChange: (v) => set({ isButtonOnly: v }) })] }));
|
|
83
|
+
] }), _jsxs(Div, { children: [_jsx(Text, { className: "text-sm font-medium mb-2", children: "Background" }), _jsx(BackgroundEditor, { value: card.background, onChange: (bg) => set({ background: bg }), prefix: `card-${index}` })] }), _jsxs(Div, { className: "space-y-2", children: [_jsx(Text, { className: "text-sm font-medium", children: "Content" }), _jsx(Input, { label: "Eyebrow", value: card.content?.eyebrow ?? "", onChange: (e) => setContent({ eyebrow: e.target.value || undefined }), placeholder: "New \u00B7 Limited \u00B7 Featured" }), _jsx(Input, { label: "Title", value: card.content?.title ?? "", onChange: (e) => setContent({ title: e.target.value }) }), _jsx(Input, { label: "Subtitle", value: card.content?.subtitle ?? "", onChange: (e) => setContent({ subtitle: e.target.value || undefined }) }), _jsx(Input, { label: "Description", value: card.content?.description ?? "", onChange: (e) => setContent({ description: e.target.value || undefined }) }), _jsx(Input, { label: "Text colour (CSS)", value: card.content?.textColor ?? "#ffffff", onChange: (e) => setContent({ textColor: e.target.value }) }), _jsx(Select, { label: "Text align", value: card.content?.textAlign ?? "left", onChange: (e) => setContent({ textAlign: e.target.value }), options: TEXT_ALIGN_OPTIONS })] }), _jsxs(Div, { className: "space-y-2", children: [_jsxs(Row, { className: CLS_ROW_BETWEEN, children: [_jsx(Text, { className: "text-sm font-medium", children: "Buttons (max 3)" }), buttons.length < 3 && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => set({ buttons: [...buttons, makeButton()] }), children: "+ Add button" }))] }), buttons.map((btn, bi) => (_jsxs(Div, { className: "rounded-lg border border-zinc-200 dark:border-zinc-800 p-3 space-y-2", children: [_jsxs(Row, { className: CLS_ROW_BETWEEN, children: [_jsxs(Text, { className: "text-xs font-medium text-zinc-500 dark:text-zinc-400", children: ["Button ", bi + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => set({ buttons: buttons.filter((_, i) => i !== bi) }), children: "\u2715" })] }), _jsx(Input, { label: "Label", value: btn.text, onChange: (e) => { const next = [...buttons]; next[bi] = { ...btn, text: e.target.value }; set({ buttons: next }); } }), _jsx(Input, { label: "Link (href)", value: btn.href, onChange: (e) => { const next = [...buttons]; next[bi] = { ...btn, href: e.target.value }; set({ buttons: next }); }, placeholder: "/products or https://..." }), _jsx(Select, { label: "Variant", value: btn.variant, onChange: (e) => { const next = [...buttons]; next[bi] = { ...btn, variant: e.target.value }; set({ buttons: next }); }, options: VARIANT_OPTIONS }), _jsx(Toggle, { label: "Open in new tab", checked: btn.openInNewTab ?? false, onChange: (v) => { const next = [...buttons]; next[bi] = { ...btn, openInNewTab: v }; set({ buttons: next }); } })] }, btn.id ?? bi)))] }), _jsxs(Div, { className: "space-y-2", children: [_jsx(Text, { className: "text-sm font-medium", children: "Hover effect" }), _jsx(Select, { label: "Effect", value: card.hover?.effect ?? "none", onChange: (e) => set({ hover: { effect: e.target.value } }), options: HOVER_OPTIONS })] }), _jsx(Toggle, { label: "Button-only card (no text content)", checked: card.isButtonOnly ?? false, onChange: (v) => set({ isButtonOnly: v }) })] }));
|
|
83
84
|
}
|
|
84
85
|
// ── Main Editor ──────────────────────────────────────────────────────────────
|
|
85
86
|
export function AdminCarouselEditorView({ slideId, onSaved, onDeleted, onCancel, ...rest }) {
|
|
@@ -104,6 +105,7 @@ export function AdminCarouselEditorView({ slideId, onSaved, onDeleted, onCancel,
|
|
|
104
105
|
const [overlayBtnNewTab, setOverlayBtnNewTab] = React.useState(false);
|
|
105
106
|
const [errorMsg, setErrorMsg] = React.useState("");
|
|
106
107
|
const [successMsg, setSuccessMsg] = React.useState("");
|
|
108
|
+
const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
|
|
107
109
|
const { shellCtx, setFieldError, clearErrors } = useFormShellState();
|
|
108
110
|
const slideQuery = useQuery({
|
|
109
111
|
queryKey: ["admin", "carousel", slideId],
|
|
@@ -205,13 +207,10 @@ export function AdminCarouselEditorView({ slideId, onSaved, onDeleted, onCancel,
|
|
|
205
207
|
return;
|
|
206
208
|
}
|
|
207
209
|
saveMutation.mutate();
|
|
208
|
-
}, className: "space-y-6", children: [_jsxs(Div, { className: CLS_PANEL, children: [_jsx(Heading, { level: 3, className: CLS_SECTION_HEADING, children: "Slide info" }), _jsx(FieldInput, { name: "title", label: "Title", value: title, onChange: (v) => setTitle(v), required: true, placeholder: "e.g. Hot Wheels RLC Exclusives" }), _jsx(Toggle, { label: "Active (visible on homepage)", checked: active, onChange: setActive }), _jsx(Input, { label: "Display order", type: "number", value: order, onChange: (e) => setOrder(e.target.value), min: 0, placeholder: "1" }), _jsx(Select, { label: "Slide height", value: height, onChange: (e) => setHeight(e.target.value), options: HEIGHT_OPTIONS }), _jsx(Input, { label: "Autoplay delay (ms)", type: "number", value: autoplayDelayMs, onChange: (e) => setAutoplayDelayMs(e.target.value), min: 1000, max: 15000, placeholder: "4000", helperText: "How long this slide shows before auto-advancing. Default: 4000 ms." })] }), _jsxs(Div, { className: CLS_PANEL, children: [_jsx(Heading, { level: 3, className: CLS_SECTION_HEADING, children: "Background" }), _jsx(BackgroundEditor, { value: background, onChange: setBackground, prefix: "slide" })] }), _jsxs(Div, { className: CLS_PANEL, children: [_jsx(Heading, { level: 3, className: CLS_SECTION_HEADING, children: "Overlay text (optional)" }), _jsx(Text, { className: "text-sm text-zinc-500", children: "Centred text layered over the background. Leave blank to use cards only." }), _jsx(Input, { label: "Heading", value: overlayTitle, onChange: (e) => setOverlayTitle(e.target.value), placeholder: "India's #1 Collectibles Marketplace" }), _jsx(Input, { label: "Subtitle", value: overlaySubtitle, onChange: (e) => setOverlaySubtitle(e.target.value), placeholder: "Pok\u00E9mon TCG \u00B7 Hot Wheels \u00B7 Beyblade X" }), _jsx(Input, { label: "Description", value: overlayDesc, onChange: (e) => setOverlayDesc(e.target.value), placeholder: "One sentence description..." }), _jsxs(Div, { className: "rounded-lg border border-zinc-200 dark:border-zinc-800 p-3 space-y-2", children: [_jsx(Text, { className: "text-sm font-medium", children: "CTA button" }), _jsx(Input, { label: "Button text", value: overlayBtnText, onChange: (e) => setOverlayBtnText(e.target.value), placeholder: "Shop Now" }), _jsx(Input, { label: "Button link", value: overlayBtnLink, onChange: (e) => setOverlayBtnLink(e.target.value), placeholder: "/products" }), _jsx(Select, { label: "Variant", value: overlayBtnVariant, onChange: (e) => setOverlayBtnVariant(e.target.value), options: VARIANT_OPTIONS.filter((v) => ["primary", "secondary", "outline"].includes(v.value)) }), _jsx(Toggle, { label: "Open in new tab", checked: overlayBtnNewTab, onChange: setOverlayBtnNewTab })] })] }), _jsxs(Div, { className: CLS_PANEL, children: [_jsxs(Row, { className: CLS_ROW_BETWEEN, children: [_jsx(Heading, { level: 3, className: CLS_SECTION_HEADING, children: "Cards (0\u20132)" }), cards.length < 2 && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => {
|
|
210
|
+
}, className: "space-y-6", children: [_jsxs(Div, { className: CLS_PANEL, children: [_jsx(Heading, { level: 3, className: CLS_SECTION_HEADING, children: "Slide info" }), _jsx(FieldInput, { name: "title", label: "Title", value: title, onChange: (v) => setTitle(v), required: true, placeholder: "e.g. Hot Wheels RLC Exclusives" }), _jsx(Toggle, { label: "Active (visible on homepage)", checked: active, onChange: setActive }), _jsx(Input, { label: "Display order", type: "number", value: order, onChange: (e) => setOrder(e.target.value), min: 0, placeholder: "1" }), _jsx(Select, { label: "Slide height", value: height, onChange: (e) => setHeight(e.target.value), options: HEIGHT_OPTIONS }), _jsx(Input, { label: "Autoplay delay (ms)", type: "number", value: autoplayDelayMs, onChange: (e) => setAutoplayDelayMs(e.target.value), min: 1000, max: 15000, placeholder: "4000", helperText: "How long this slide shows before auto-advancing. Default: 4000 ms." })] }), _jsxs(Div, { className: CLS_PANEL, children: [_jsx(Heading, { level: 3, className: CLS_SECTION_HEADING, children: "Background" }), _jsx(BackgroundEditor, { value: background, onChange: setBackground, prefix: "slide" })] }), _jsxs(Div, { className: CLS_PANEL, children: [_jsx(Heading, { level: 3, className: CLS_SECTION_HEADING, children: "Overlay text (optional)" }), _jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "Centred text layered over the background. Leave blank to use cards only." }), _jsx(Input, { label: "Heading", value: overlayTitle, onChange: (e) => setOverlayTitle(e.target.value), placeholder: "India's #1 Collectibles Marketplace" }), _jsx(Input, { label: "Subtitle", value: overlaySubtitle, onChange: (e) => setOverlaySubtitle(e.target.value), placeholder: "Pok\u00E9mon TCG \u00B7 Hot Wheels \u00B7 Beyblade X" }), _jsx(Input, { label: "Description", value: overlayDesc, onChange: (e) => setOverlayDesc(e.target.value), placeholder: "One sentence description..." }), _jsxs(Div, { className: "rounded-lg border border-zinc-200 dark:border-zinc-800 p-3 space-y-2", children: [_jsx(Text, { className: "text-sm font-medium", children: "CTA button" }), _jsx(Input, { label: "Button text", value: overlayBtnText, onChange: (e) => setOverlayBtnText(e.target.value), placeholder: "Shop Now" }), _jsx(Input, { label: "Button link", value: overlayBtnLink, onChange: (e) => setOverlayBtnLink(e.target.value), placeholder: "/products" }), _jsx(Select, { label: "Variant", value: overlayBtnVariant, onChange: (e) => setOverlayBtnVariant(e.target.value), options: VARIANT_OPTIONS.filter((v) => ["primary", "secondary", "outline"].includes(v.value)) }), _jsx(Toggle, { label: "Open in new tab", checked: overlayBtnNewTab, onChange: setOverlayBtnNewTab })] })] }), _jsxs(Div, { className: CLS_PANEL, children: [_jsxs(Row, { className: CLS_ROW_BETWEEN, children: [_jsx(Heading, { level: 3, className: CLS_SECTION_HEADING, children: "Cards (0\u20132)" }), cards.length < 2 && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => {
|
|
209
211
|
const usedZones = cards.map((c) => c.zone);
|
|
210
212
|
const freeZone = [1, 2, 3, 4, 5, 6].find((z) => !usedZones.includes(z)) ?? 1;
|
|
211
213
|
setCards([...cards, makeCard(freeZone)]);
|
|
212
|
-
}, children: "+ Add card" }))] }), cards.length === 0 && (_jsx(Text, { className: "text-sm text-zinc-400", children: "No cards \u2014 the overlay text covers the whole slide." })), cards.map((card, i) => (_jsx(CardEditor, { card: card, index: i, otherZones: occupiedZones.filter((z) => z !== card.zone), onChange: (updated) => setCards(cards.map((c, ci) => (ci === i ? updated : c))), onRemove: () => setCards(cards.filter((_, ci) => ci !== i)) }, card.id)))] }), _jsxs(FormActions, { children: [_jsx(Button, { type: "submit", isLoading: saveMutation.isPending, disabled: !title || saveMutation.isPending, children: isEdit ? "Save changes" : "Create slide" }), onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, children: "Cancel" })), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => {
|
|
213
|
-
if (confirm("Delete this slide? This cannot be undone."))
|
|
214
|
-
deleteMutation.mutate();
|
|
215
|
-
}, children: "Delete slide" }))] })] }, "slide-form") }),
|
|
214
|
+
}, children: "+ Add card" }))] }), cards.length === 0 && (_jsx(Text, { className: "text-sm text-zinc-400", children: "No cards \u2014 the overlay text covers the whole slide." })), cards.map((card, i) => (_jsx(CardEditor, { card: card, index: i, otherZones: occupiedZones.filter((z) => z !== card.zone), onChange: (updated) => setCards(cards.map((c, ci) => (ci === i ? updated : c))), onRemove: () => setCards(cards.filter((_, ci) => ci !== i)) }, card.id)))] }), _jsxs(FormActions, { children: [_jsx(Button, { type: "submit", isLoading: saveMutation.isPending, disabled: !title || saveMutation.isPending, children: isEdit ? "Save changes" : "Create slide" }), onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, children: "Cancel" })), isEdit && (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => setDeleteConfirmOpen(true), children: ACTIONS.ADMIN["delete-carousel"].label }), _jsx(ConfirmDeleteModal, { isOpen: deleteConfirmOpen, onClose: () => setDeleteConfirmOpen(false), onConfirm: () => { setDeleteConfirmOpen(false); deleteMutation.mutate(); }, title: ACTIONS.ADMIN["delete-carousel"].confirmation.title, message: ACTIONS.ADMIN["delete-carousel"].confirmation.body, isDeleting: deleteMutation.isPending })] }))] })] }, "slide-form") }),
|
|
216
215
|
] }));
|
|
217
216
|
}
|
|
@@ -6,6 +6,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
8
|
import { BulkActionBar, ListingToolbar, ListingViewShell, Pagination, Text } from "../../../ui";
|
|
9
|
+
import { useBottomActions } from "../../layout";
|
|
9
10
|
import { ADMIN_ENDPOINTS, HOMEPAGE_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
11
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
@@ -18,7 +19,7 @@ const DEFAULT_SORT = "order";
|
|
|
18
19
|
function CarouselFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
19
20
|
if (!filterOpen)
|
|
20
21
|
return null;
|
|
21
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-
|
|
22
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, active: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.active || "") === opt.value ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
22
23
|
}
|
|
23
24
|
export function AdminCarouselView({ children, onBulkDelete, ...props }) {
|
|
24
25
|
const hasChildren = React.Children.count(children) > 0;
|
|
@@ -143,6 +144,7 @@ export function AdminCarouselView({ children, onBulkDelete, ...props }) {
|
|
|
143
144
|
onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
|
|
144
145
|
}] : []),
|
|
145
146
|
];
|
|
147
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
146
148
|
if (hasChildren) {
|
|
147
149
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
148
150
|
}
|
|
@@ -169,12 +171,12 @@ export function AdminCarouselView({ children, onBulkDelete, ...props }) {
|
|
|
169
171
|
{
|
|
170
172
|
key: "status",
|
|
171
173
|
header: "Status",
|
|
172
|
-
render: (row) => (_jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${row.status === "Active" ? "bg-
|
|
174
|
+
render: (row) => (_jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${row.status === "Active" ? "bg-success-surface text-success" : "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400"}`, children: row.status })),
|
|
173
175
|
},
|
|
174
176
|
{
|
|
175
177
|
key: "updatedAt",
|
|
176
178
|
header: "Updated",
|
|
177
|
-
render: (row) => _jsx("span", { className: "text-sm text-zinc-500", children: row.updatedAt }),
|
|
179
|
+
render: (row) => _jsx("span", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: row.updatedAt }),
|
|
178
180
|
},
|
|
179
181
|
{
|
|
180
182
|
key: "actions",
|
|
@@ -189,5 +191,5 @@ export function AdminCarouselView({ children, onBulkDelete, ...props }) {
|
|
|
189
191
|
] })),
|
|
190
192
|
},
|
|
191
193
|
];
|
|
192
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search slide titles", onSearchChange: setSearchInput, onSearchCommit: commitSearch, showTableView: true, view: view, onViewChange: (v) => setView(v), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-
|
|
194
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search slide titles", onSearchChange: setSearchInput, onSearchCommit: commitSearch, showTableView: true, view: view, onViewChange: (v) => setView(v), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), _jsx(DataTable, { rows: displayRows, columns: columns, isLoading: isLoading, emptyLabel: "No carousel slides \u2014 use 'New slide' to add one", getRowHref: (row) => `/admin/carousel/${row.id}/edit` })] }), _jsx(CarouselFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }));
|
|
193
195
|
}
|
|
@@ -91,5 +91,5 @@ export function AdminCartsView({ children, ...props }) {
|
|
|
91
91
|
if (hasChildren) {
|
|
92
92
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
93
93
|
}
|
|
94
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by user ID or session", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-
|
|
94
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by user ID or session", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No carts found", selectedIds: selectedIds, onToggleSelect: toggleSelect, onToggleSelectAll: (next) => setSelectedIds(next ? new Set(rows.map((r) => r.id)) : new Set()) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Type", tabs: TYPE_OPTIONS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
95
95
|
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Package, FolderTree, Tag, Star, AlertTriangle } from "lucide-react";
|
|
3
|
-
import { Div, Heading, Text, Section, Alert } from "../../../ui";
|
|
3
|
+
import { Div, Heading, Span, Text, Section, Alert } from "../../../ui";
|
|
4
4
|
import { GC } from "../../_guide-cls";
|
|
5
5
|
export function AdminCatalogGuideView() {
|
|
6
6
|
return (_jsxs(Div, { className: "space-y-8 pb-10 max-w-3xl mx-auto", children: [_jsxs(Section, { children: [_jsxs(Div, { className: "flex items-center gap-3 mb-2", children: [_jsx(Div, { className: "flex-shrink-0 w-10 h-10 rounded-xl flex items-center justify-center", style: { background: "linear-gradient(135deg,var(--appkit-color-primary-700,#4f46e5) 0%,var(--appkit-color-cobalt,#2563eb) 100%)" }, children: _jsx(Package, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text-muted)] uppercase tracking-widest", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-2xl md:text-3xl font-bold text-[var(--appkit-color-text)] mb-2", children: "Catalog" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Products, categories, brands, and reviews \u2014 how the LetItRip catalog is structured and managed." })] }), [
|
|
7
7
|
{
|
|
8
8
|
Icon: Package, title: "Product Management",
|
|
9
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
9
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Listing types" }), ": ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "standard" }), " (prefix ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "product-" }), "), ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "auction" }), " (prefix ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "auction-" }), "), ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "pre-order" }), " (prefix ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "preorder-" }), ")."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Admin vs store creation" }), ": Admins can create products on behalf of any store via the store picker in AdminProductEditorView."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Status lifecycle" }), ": DRAFT \u2192 PUBLISHED \u2192 ARCHIVED. Published products appear in search. Archived products are hidden but not deleted."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "J13 rule" }), ": Always use ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "listingType" }), " field \u2014 the legacy ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "isAuction" }), "/", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "isPreOrder" }), " booleans have been removed. All queries use ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "where(\"listingType\", \"==\", x)" }), "."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Media URLs" }), ": Never write raw Firebase Storage URLs to Firestore. All product image URLs must use the ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "/api/media/[slug]" }), " proxy format."] })] })),
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
Icon: FolderTree, title: "Category Taxonomy",
|
|
13
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
13
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "3-tier system" }), ": Root (tier 1) \u2192 Subcategory (tier 2) \u2192 Leaf (tier 3). Only leaf categories can be assigned to products."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Key fields" }), ": ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "parentId" }), " points to the direct parent; ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "rootId" }), " always points to the tier-1 root; ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "path" }), " stores the full hierarchy path."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "isLeaf" }), ": Must be ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "true" }), " on any category that can be selected in the product form. Tier-1 and tier-2 categories must have ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "isLeaf: false" }), "."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Slug prefix" }), ": All category slugs start with ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "category-" }), "."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Adding a new root" }), ": Rare. Requires: ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "display.icon" }), ", decision on ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "showOnHomepage" }), ", and a senior admin sign-off."] })] })),
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
Icon: Tag, title: "Brands",
|
|
17
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
17
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Slug prefix" }), ": ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "brand-" }), ". Slugs are immutable after products reference them."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "displayOrder" }), ": Controls the sort order on public brand pages. Lower numbers appear first."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "isActive: false" }), ": Hides the brand from public discovery but preserves historical product links. Use this instead of deleting a brand."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "logoURL / bannerURL" }), ": Must use the ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "/api/media/" }), " proxy \u2014 never raw Storage URLs."] })] })),
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
Icon: Star, title: "Reviews",
|
|
21
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
21
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "isVerifiedPurchase" }), ": Set automatically by the system after order DELIVERED. Never manually toggle this field."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "sellerResponse" }), ": Written by the seller via their store dashboard. Admins should not edit seller responses \u2014 only remove clearly violating ones."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "When to delete vs hide" }), ": Delete for violations (PII, slurs, spam). Hide (move to unlisted) if a dispute is pending resolution."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "helpfulCount" }), ": Read-only \u2014 incremented by buyer votes. Do not manually edit this field."] })] })),
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
Icon: AlertTriangle, title: "Common Admin Mistakes",
|
|
@@ -12,6 +12,7 @@ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
|
12
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
13
13
|
import { DataTable } from "./DataTable";
|
|
14
14
|
import { AdminCategoryEditorView } from "./AdminCategoryEditorView";
|
|
15
|
+
import { useBottomActions } from "../../layout";
|
|
15
16
|
const PAGE_SIZE = 50;
|
|
16
17
|
const FILTER_KEYS = ["isActive", "isFeatured"];
|
|
17
18
|
const DEFAULT_SORT = "name";
|
|
@@ -23,7 +24,7 @@ const SORT_OPTIONS = [
|
|
|
23
24
|
function CategoriesFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
24
25
|
if (!filterOpen)
|
|
25
26
|
return null;
|
|
26
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-
|
|
27
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Active" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Featured" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ label: "All", value: "" }, { label: "Featured only", value: "true" }].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isFeatured: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isFeatured || "") === opt.value ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] })] }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
27
28
|
}
|
|
28
29
|
export function AdminCategoriesView({ children, getRowHref, ...props }) {
|
|
29
30
|
const hasChildren = React.Children.count(children) > 0;
|
|
@@ -93,8 +94,12 @@ export function AdminCategoriesView({ children, getRowHref, ...props }) {
|
|
|
93
94
|
if (hasChildren) {
|
|
94
95
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
95
96
|
}
|
|
97
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
98
|
+
{ id: "edit", label: ACTIONS.ADMIN["edit-category"].label, variant: "primary", onClick: () => { const id = selection.selectedIds[0]; if (id)
|
|
99
|
+
openEditPanel(id); selection.clearSelection(); } },
|
|
100
|
+
] } } : {});
|
|
96
101
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(Heading, { level: 1, className: "sr-only", children: "Categories" }), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search categories, slugs, or parent category", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Category"] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
97
102
|
{ id: "edit", label: ACTIONS.ADMIN["edit-category"].label, variant: "primary", onClick: () => { const id = selection.selectedIds[0]; if (id)
|
|
98
103
|
openEditPanel(id); selection.clearSelection(); } },
|
|
99
|
-
] }), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-
|
|
104
|
+
] }), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No categories found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No categories found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(CategoriesFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Category" : "Edit Category", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminCategoryEditorView, { categoryId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
100
105
|
}
|
|
@@ -22,9 +22,9 @@ export function AdminContactEditorView({ open, onClose, submissionId, subject, n
|
|
|
22
22
|
},
|
|
23
23
|
});
|
|
24
24
|
const statusColor = {
|
|
25
|
-
new: "bg-
|
|
25
|
+
new: "bg-info-surface text-info",
|
|
26
26
|
read: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400",
|
|
27
|
-
resolved: "bg-
|
|
27
|
+
resolved: "bg-success-surface text-success",
|
|
28
28
|
};
|
|
29
29
|
return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: subject ?? "Contact Submission", children: _jsxs(Div, { className: "flex flex-col gap-5 p-4", children: [_jsxs(Div, { className: "flex items-center gap-2", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Status:" }), _jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${statusColor[currentStatus ?? "new"] ?? statusColor.new}`, children: currentStatus ?? "new" })] }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "From" }), _jsx(Text, { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: name ?? "Unknown" }), email && (_jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: email }))] }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Message" }), _jsx(Div, { className: "rounded-lg border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 p-3 max-h-64 overflow-y-auto", children: _jsx(Text, { className: "text-sm text-zinc-800 dark:text-zinc-200 whitespace-pre-wrap leading-relaxed", children: message ?? "No message body." }) })] }), _jsxs(FormActions, { align: "right", children: [email && (_jsx(Button, { type: "button", variant: "outline", onClick: () => {
|
|
30
30
|
window.location.href = `mailto:${email}?subject=Re: ${encodeURIComponent(subject ?? "")}`;
|
|
@@ -6,6 +6,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
8
|
import { BulkActionBar, ConfirmDeleteModal, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, useToast, } from "../../../ui";
|
|
9
|
+
import { useBottomActions } from "../../layout";
|
|
9
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
11
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
12
|
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
|
|
@@ -25,7 +26,7 @@ const STATUS_OPTIONS = ADMIN_CONTACT_STATUS_TABS;
|
|
|
25
26
|
function ContactFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
26
27
|
if (!filterOpen)
|
|
27
28
|
return null;
|
|
28
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-
|
|
29
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
29
30
|
}
|
|
30
31
|
export function AdminContactView({ children, onBulkMarkRead, onBulkArchive, onBulkDelete, ...props }) {
|
|
31
32
|
const hasChildren = React.Children.count(children) > 0;
|
|
@@ -141,10 +142,11 @@ export function AdminContactView({ children, onBulkMarkRead, onBulkArchive, onBu
|
|
|
141
142
|
onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
|
|
142
143
|
}] : []),
|
|
143
144
|
];
|
|
145
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
144
146
|
if (hasChildren) {
|
|
145
147
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
146
148
|
}
|
|
147
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by subject, name, or email", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-
|
|
149
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by subject, name, or email", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No contact submissions found", renderRowActions: (row) => {
|
|
148
150
|
const cr = row;
|
|
149
151
|
return (_jsx(RowActionMenu, { actions: [
|
|
150
152
|
{ label: ROW_ACTION_META[ROW_ACTION_ID.VIEW].label, onClick: () => { setSelectedRow(cr); setDrawerOpen(true); } },
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { FileText, Calendar, BookOpen, Layout, Grid, Megaphone, Mail } from "lucide-react";
|
|
3
|
-
import { Div, Heading, Text, Section, Alert } from "../../../ui";
|
|
3
|
+
import { Div, Heading, Span, Text, Section, Alert } from "../../../ui";
|
|
4
4
|
import { GC } from "../../_guide-cls";
|
|
5
5
|
export function AdminContentGuideView() {
|
|
6
6
|
return (_jsxs(Div, { className: "space-y-8 pb-10 max-w-3xl mx-auto", children: [_jsxs(Section, { children: [_jsxs(Div, { className: "flex items-center gap-3 mb-2", children: [_jsx(Div, { className: "flex-shrink-0 w-10 h-10 rounded-xl flex items-center justify-center", style: { background: "linear-gradient(135deg,var(--appkit-color-primary-700,#4f46e5) 0%,var(--appkit-color-cobalt,#2563eb) 100%)" }, children: _jsx(FileText, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text-muted)] uppercase tracking-widest", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-2xl md:text-3xl font-bold text-[var(--appkit-color-text)] mb-2", children: "Content & Marketing" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Blog posts, events, FAQs, carousel, homepage sections, ads, and newsletter on LetItRip." })] }), [
|
|
7
7
|
{
|
|
8
8
|
Icon: FileText, title: "Blog Posts",
|
|
9
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
9
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Statuses" }), ": ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "draft" }), " (not public), ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "published" }), " (live, indexed)."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "readTimeMinutes" }), ": Auto-calculated from content word count (200 wpm). Override manually if the post has heavy media."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "youtubeId" }), ": Optional embedded YouTube video. Use only the video ID (e.g. ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "dQw4w9WgXcQ" }), "), not the full URL."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Media in body" }), ": All images in rich text must use the ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "/api/media/" }), " proxy URL, not raw Firebase Storage links."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "isFeatured" }), ": Sets the post as featured in the blog listing. Different from the homepage featured section \u2014 that is driven by a separate ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "homepageSections" }), " config."] })] })),
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
Icon: Calendar, title: "Events",
|
|
13
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
13
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Event types" }), ": ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "sale" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "offer" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "poll" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "survey" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "feedback" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "raffle" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "spin_wheel" }), "."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Status" }), ": Computed from ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "startsAt" }), "/", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "endsAt" }), " (upcoming \u2192 active \u2192 ended). Do not set a status field manually \u2014 it will be overwritten."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "stats.totalEntries vs approvedEntries" }), ": ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "totalEntries" }), " = all submitted (including waitlisted); ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "approvedEntries" }), " = CONFIRMED only."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Cancelling an entry" }), ": Set status to CANCELLED in the entry doc. Do not delete entries \u2014 audit trail is needed for raffle fairness."] })] })),
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
Icon: BookOpen, title: "FAQs",
|
|
17
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
17
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Categories" }), " and where they appear: Shipping (help + footer), Returns (help), Payments (help), Auctions (help), Pre-orders (help). Set ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "showOnHomepage: true" }), " for up to 5 FAQs shown on the public home page."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "searchTokens[]" }), ": Must be manually filled \u2014 drives the FAQ search feature. Include synonyms, misspellings, and related keywords."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "priority + order" }), ": ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "priority" }), " sorts FAQs within their category; ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "order" }), " is the global absolute position. ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "isPinned: true" }), " floats the FAQ above non-pinned items in its category."] })] })),
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
Icon: Layout, title: "Carousel Slides",
|
|
21
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
21
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "MAX_ACTIVE_SLIDES = 5" }), ": You cannot activate a 6th slide. Deactivate an existing slide first."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "background.type" }), ": ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "image" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "video" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "color" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "gradient" }), ". Do not have 2 active video slides simultaneously \u2014 auto-playing two videos degrades mobile performance."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Reordering" }), ": Drag slides in AdminCarouselView to reorder. The ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "order" }), " field is updated automatically."] }), _jsxs("li", { children: [_jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "cards[]" }), ": Each slide contains an array of content cards displayed as an overlay. Max 3 cards per slide recommended for readability."] })] })),
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
Icon: Grid, title: "Homepage Sections",
|
|
25
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
25
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "21 section types" }), ": product-grid, brand-grid, category-grid, event-highlight, blog-preview, etc. The full list and config shapes are in ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "appkit/src/features/homepage/schemas/firestore.ts" }), "."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "order" }), ": Controls render sequence on the public homepage. Lower numbers appear higher on the page."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "enabled: false" }), ": Hides the section without deleting it \u2014 useful for temporary removal."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Never edit Firestore directly" }), ": Always use AdminSectionsView. The config shapes are validated on write \u2014 direct Firestore edits can produce invalid shapes that crash the home page render."] })] })),
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
Icon: Megaphone, title: "Ads",
|
|
@@ -6,6 +6,7 @@ import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
7
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
8
8
|
import { BulkActionBar, Button, Div, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, SideDrawer, Text, useToast } from "../../../ui";
|
|
9
|
+
import { useBottomActions } from "../../layout";
|
|
9
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
11
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
12
|
import { ADMIN_COUPON_TYPE_TABS } from "../constants/filter-tabs";
|
|
@@ -25,7 +26,7 @@ const TYPE_OPTIONS = ADMIN_COUPON_TYPE_TABS;
|
|
|
25
26
|
function CouponsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
26
27
|
if (!filterOpen)
|
|
27
28
|
return null;
|
|
28
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-
|
|
29
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Type", tabs: TYPE_OPTIONS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
29
30
|
}
|
|
30
31
|
export function AdminCouponsView({ children, getRowHref, onBulkArchive, onBulkDelete, ...props }) {
|
|
31
32
|
const hasChildren = React.Children.count(children) > 0;
|
|
@@ -116,8 +117,9 @@ export function AdminCouponsView({ children, getRowHref, onBulkArchive, onBulkDe
|
|
|
116
117
|
onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
|
|
117
118
|
}] : []),
|
|
118
119
|
];
|
|
120
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
119
121
|
if (hasChildren) {
|
|
120
122
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
121
123
|
}
|
|
122
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search codes, campaigns, or seller scopes", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Coupon"] }) }), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-
|
|
124
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search codes, campaigns, or seller scopes", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Coupon"] }) }), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), isLoading ? (_jsx(Div, { className: "fluid-grid-card gap-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Div, { className: "rounded-xl border-2 border-zinc-100 dark:border-slate-700 p-4 animate-pulse space-y-3", children: [_jsx(Div, { className: "h-6 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx(Div, { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx(Div, { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-400", children: "No coupons found" }) })) : (_jsx(Div, { className: "fluid-grid-card gap-3", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: openEditPanel, onToggleActive: handleToggle, onDelete: handleDelete }, row.id))) }))] }), _jsx(CouponsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Coupon" : "Edit Coupon", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminCouponEditorView, { couponId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
123
125
|
}
|
|
@@ -75,7 +75,7 @@ function PermissionDomainsPanel({ permissions, togglePerm }) {
|
|
|
75
75
|
if (domainPerms.length === 0)
|
|
76
76
|
return null;
|
|
77
77
|
const checked = domainPerms.filter((p) => permissions.has(p)).length;
|
|
78
|
-
return (_jsxs("details", { className: "group", children: [_jsxs("summary", { className: "flex cursor-pointer items-center justify-between px-3 py-2 text-xs font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400 select-none hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [_jsx("span", { children: domain.label }), _jsxs("span", { className: "text-xs font-normal normal-case text-zinc-400 dark:text-zinc-
|
|
78
|
+
return (_jsxs("details", { className: "group", children: [_jsxs("summary", { className: "flex cursor-pointer items-center justify-between px-3 py-2 text-xs font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400 select-none hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [_jsx("span", { children: domain.label }), _jsxs("span", { className: "text-xs font-normal normal-case text-zinc-400 dark:text-zinc-400", children: [checked, "/", domainPerms.length] })] }), _jsx("div", { className: "grid grid-cols-2 gap-x-2 gap-y-1.5 px-3 py-2.5 bg-zinc-50/60 dark:bg-slate-800/40", children: domainPerms.map((perm) => (_jsxs("label", { className: "flex items-center gap-2 cursor-pointer text-xs text-zinc-700 dark:text-zinc-300", children: [_jsx("input", { type: "checkbox", checked: permissions.has(perm), onChange: () => togglePerm(perm), className: "h-3.5 w-3.5 rounded border-zinc-300 dark:border-slate-600 accent-primary" }), formatPermLabel(perm)] }, perm))) })] }, domain.prefix));
|
|
79
79
|
}) })] }));
|
|
80
80
|
}
|
|
81
81
|
// --- Component ---------------------------------------------------------------
|