@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
|
@@ -19,10 +19,10 @@ const PRIORITY_OPTIONS = [
|
|
|
19
19
|
{ label: "Urgent", value: "urgent" },
|
|
20
20
|
];
|
|
21
21
|
const STATUS_COLOR = {
|
|
22
|
-
open: "bg-
|
|
23
|
-
in_progress: "bg-
|
|
22
|
+
open: "bg-info-surface text-info",
|
|
23
|
+
in_progress: "bg-warning-surface text-warning",
|
|
24
24
|
waiting_on_user: "bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300",
|
|
25
|
-
resolved: "bg-
|
|
25
|
+
resolved: "bg-success-surface text-success",
|
|
26
26
|
closed: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400",
|
|
27
27
|
};
|
|
28
28
|
const ROLE_LABEL = {
|
|
@@ -79,5 +79,5 @@ export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject,
|
|
|
79
79
|
});
|
|
80
80
|
return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: subject ?? "Support Ticket", children: _jsxs(Div, { className: "flex flex-col gap-4 p-4", children: [_jsxs(Div, { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${STATUS_COLOR[currentStatus ?? "open"] ?? STATUS_COLOR.open}`, children: (currentStatus ?? "open").replace(/_/g, " ") }), _jsx("span", { className: "rounded-full bg-zinc-100 px-2.5 py-1 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300", children: category }), orderId && (_jsxs("span", { className: "rounded-full bg-zinc-100 px-2.5 py-1 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300", children: ["Order: ", orderId] }))] }), _jsxs(Div, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: ["From: ", _jsx("span", { className: "font-medium text-zinc-700 dark:text-zinc-200", children: userDisplayName })] }), description && (_jsxs(Div, { className: "rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsx(Text, { className: "mb-1 text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Description" }), _jsx(Text, { className: "whitespace-pre-wrap text-sm text-zinc-700 dark:text-zinc-200", children: description })] })), messages.length > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Text, { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: ["Messages (", messages.length, ")"] }), _jsx("div", { className: "space-y-2 max-h-64 overflow-y-auto", children: messages.map((msg, i) => (_jsxs("div", { className: `rounded-lg p-3 text-sm ${msg.authorRole === "user"
|
|
81
81
|
? "bg-zinc-50 border border-zinc-200 dark:bg-zinc-900/40 dark:border-zinc-700"
|
|
82
|
-
: "bg-
|
|
82
|
+
: "bg-info-surface border border-blue-200 dark:border-blue-800"}`, children: [_jsxs("div", { className: "mb-1 flex items-center gap-2 text-xs text-zinc-400 dark:text-zinc-400", children: [_jsx("span", { className: "font-medium text-zinc-600 dark:text-zinc-300", children: ROLE_LABEL[msg.authorRole ?? "user"] ?? msg.authorRole }), msg.createdAt && (_jsx("span", { children: new Date(msg.createdAt).toLocaleString() }))] }), _jsx(Text, { className: "whitespace-pre-wrap text-zinc-700 dark:text-zinc-200", children: msg.body })] }, msg.id ?? i))) })] })), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Reply to user" }), _jsx("textarea", { value: replyBody, onChange: (e) => setReplyBody(e.target.value), rows: 3, placeholder: "Type a reply\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: replyMutation.isPending, disabled: !replyBody.trim() || !ticketId || replyMutation.isPending, onClick: () => replyMutation.mutate(), children: "Send reply" })] }), _jsx("hr", { className: "border-zinc-200 dark:border-zinc-700" }), _jsx(Select, { label: "Status", options: STATUS_OPTIONS, value: status, onValueChange: setStatus }), _jsx(Select, { label: "Priority", options: PRIORITY_OPTIONS, value: priority, onValueChange: setPriority }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Internal notes (staff only)" }), _jsx("textarea", { value: notes, onChange: (e) => setNotes(e.target.value), rows: 3, placeholder: "Notes visible only to admins and employees\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "button", isLoading: updateMutation.isPending, disabled: !ticketId || updateMutation.isPending, onClick: () => updateMutation.mutate(), children: "Save changes" })] })] }) }));
|
|
83
83
|
}
|
|
@@ -20,22 +20,22 @@ const SORT_OPTIONS = [
|
|
|
20
20
|
{ value: "-updatedAt", label: "Recently updated" },
|
|
21
21
|
];
|
|
22
22
|
const PRIORITY_BADGE = {
|
|
23
|
-
urgent: "bg-
|
|
24
|
-
high: "bg-
|
|
25
|
-
normal: "bg-
|
|
23
|
+
urgent: "bg-error-surface text-error",
|
|
24
|
+
high: "bg-warning-surface text-warning",
|
|
25
|
+
normal: "bg-info-surface text-info",
|
|
26
26
|
low: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400",
|
|
27
27
|
};
|
|
28
28
|
const STATUS_BADGE = {
|
|
29
|
-
open: "bg-
|
|
30
|
-
in_progress: "bg-
|
|
29
|
+
open: "bg-info-surface text-info",
|
|
30
|
+
in_progress: "bg-warning-surface text-warning",
|
|
31
31
|
waiting_on_user: "bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300",
|
|
32
|
-
resolved: "bg-
|
|
32
|
+
resolved: "bg-success-surface text-success",
|
|
33
33
|
closed: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400",
|
|
34
34
|
};
|
|
35
35
|
function SupportTicketsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
36
36
|
if (!filterOpen)
|
|
37
37
|
return null;
|
|
38
|
-
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-
|
|
38
|
+
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: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_SUPPORT_TICKET_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Priority", tabs: ADMIN_SUPPORT_TICKET_PRIORITY_TABS, value: pendingFilters.priority ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, priority: 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})` : ""] }) })] })] }));
|
|
39
39
|
}
|
|
40
40
|
function buildTicketColumns(setSelectedRow, setDrawerOpen) {
|
|
41
41
|
return [
|
|
@@ -144,7 +144,7 @@ export function AdminSupportTicketsView({ children, ...props }) {
|
|
|
144
144
|
}
|
|
145
145
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by subject", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
|
|
146
146
|
table.set("sort", v);
|
|
147
|
-
}, 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-
|
|
147
|
+
}, 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 support tickets found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
148
148
|
{ label: ROW_ACTION_META[ROW_ACTION_ID.VIEW].label, onClick: () => { setSelectedRow(row); setDrawerOpen(true); } },
|
|
149
149
|
] })), columns: buildTicketColumns(setSelectedRow, setDrawerOpen) })] }), _jsx(SupportTicketsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }), _jsx(AdminSupportTicketDetailView, { open: drawerOpen, onClose: () => setDrawerOpen(false), ticketId: selectedRow?.id, subject: selectedRow?.primary, userDisplayName: toStringValue(selectedRow?._raw?.userDisplayName, ""), category: toStringValue(selectedRow?._raw?.category, "general"), currentStatus: selectedRow?.status, currentPriority: toStringValue(selectedRow?._raw?.priority, "normal"), description: toStringValue(selectedRow?._raw?.description, ""), messages: Array.isArray(selectedRow?._raw?.messages)
|
|
150
150
|
? selectedRow._raw.messages
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Users, Shield } from "lucide-react";
|
|
3
|
-
import { Div, Heading, Text, Section, Alert } from "../../../ui";
|
|
3
|
+
import { Div, Heading, Span, Text, Section, Alert } from "../../../ui";
|
|
4
4
|
export function AdminTeamGuideView() {
|
|
5
5
|
const PERMISSION_GROUPS = [
|
|
6
6
|
{ name: "content_manager", desc: "Blog, FAQs, Events, Carousel, Sections, Ads, Newsletter read + write." },
|
|
@@ -15,7 +15,7 @@ export function AdminTeamGuideView() {
|
|
|
15
15
|
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(Users, { 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: "Team & Permissions" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "How roles, permissions, and employee accounts work on LetItRip." })] }), [
|
|
16
16
|
{
|
|
17
17
|
Icon: Shield, title: "Role vs Permission: Key Difference",
|
|
18
|
-
content: (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mb-3", children: [_jsx("
|
|
18
|
+
content: (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mb-3", children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Role" }), " (", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "user.role" }), ") is a coarse-grained classification: ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "user" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "seller" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "moderator" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "employee" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "admin" }), "."] }), _jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mb-3", children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Permissions" }), " (", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "user.permissions[]" }), ") are fine-grained capabilities: e.g. ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "admin:orders:read" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "admin:products:write" }), "."] }), _jsxs(Alert, { variant: "info", children: [_jsx("code", { className: "text-xs", children: "role: \"admin\"" }), " bypasses all RBAC permission checks \u2014 it's a superuser flag. ", _jsx("code", { className: "text-xs", children: "role: \"employee\"" }), " is governed entirely by ", _jsx("code", { className: "text-xs", children: "user.permissions[]" }), "."] })] })),
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
Icon: Users, title: "Permission Groups",
|
|
@@ -23,11 +23,11 @@ export function AdminTeamGuideView() {
|
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
Icon: Users, title: "Employee Invite Flow",
|
|
26
|
-
content: (_jsxs("ul", { className: "space-y-2 text-sm text-[var(--appkit-color-text-muted)]", children: [_jsxs("li", { children: ["Go to ", _jsx("
|
|
26
|
+
content: (_jsxs("ul", { className: "space-y-2 text-sm text-[var(--appkit-color-text-muted)]", children: [_jsxs("li", { children: ["Go to ", _jsx(Span, { weight: "bold", children: "Admin \u2192 Team \u2192 Invite Employee" }), ". Enter the person's email and select their permission group."] }), _jsxs("li", { children: ["They receive an invitation email with a sign-up link. On first login, their Firestore profile is created with ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "role: \"employee\"" }), " and the selected permissions array."] }), _jsx("li", { children: "To update permissions later: edit the employee in the Team section. Changes take effect on their next login (session tokens carry the old permissions until refreshed)." }), _jsxs("li", { children: ["To offboard: use ", _jsx(Span, { weight: "bold", children: "Revoke Access" }), " in Team \u2014 sets ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "isDisabled: true" }), " on their Auth account and clears all permissions."] })] })),
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
Icon: Shield, title: "The 85+ Permission System",
|
|
30
|
-
content: (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mb-3", children: ["Permissions follow the pattern ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "admin:{resource}:{action}" }), ". Actions: ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "read" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "write" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "delete" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "view" }), "."] }), _jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: ["The full permission list is defined in ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "appkit/src/features/auth/schemas/firestore.ts" }), ". Custom roles (specific permission combinations) can be created at ", _jsx("
|
|
30
|
+
content: (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mb-3", children: ["Permissions follow the pattern ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "admin:{resource}:{action}" }), ". Actions: ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "read" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "write" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "delete" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "view" }), "."] }), _jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: ["The full permission list is defined in ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "appkit/src/features/auth/schemas/firestore.ts" }), ". Custom roles (specific permission combinations) can be created at ", _jsx(Span, { weight: "bold", children: "Admin \u2192 Custom Roles" }), " \u2014 use these when a predefined group is too broad or too narrow for a hire."] })] })),
|
|
31
31
|
},
|
|
32
32
|
].map(({ Icon, title, content }) => (_jsxs(Section, { className: "rounded-2xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden", children: [_jsxs(Div, { className: "flex items-center gap-3 px-6 py-4 border-b border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-2,var(--appkit-color-border))]/20", children: [_jsx(Icon, { className: "w-5 h-5 text-[var(--appkit-color-primary)]" }), _jsx(Heading, { level: 2, className: "text-base font-semibold text-[var(--appkit-color-text)]", children: title })] }), _jsx(Div, { className: "px-6 py-5", children: content })] }, title)))] }));
|
|
33
33
|
}
|
|
@@ -5,6 +5,7 @@ import { X, UserPlus } from "lucide-react";
|
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
7
|
import { BulkActionBar, Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, RowActionMenu, } from "../../../ui";
|
|
8
|
+
import { useBottomActions } from "../../layout";
|
|
8
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
11
|
import { ALL_TAB } from "../constants/filter-tabs";
|
|
@@ -47,7 +48,7 @@ function formatGroup(group) {
|
|
|
47
48
|
function TeamFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingGroup, setPendingGroup, applyFilters, }) {
|
|
48
49
|
if (!filterOpen)
|
|
49
50
|
return null;
|
|
50
|
-
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-
|
|
51
|
+
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", children: _jsx(FilterChipGroup, { label: "Permission Group", tabs: GROUP_TABS, value: pendingGroup, onChange: (id) => setPendingGroup(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})` : ""] }) })] })] }));
|
|
51
52
|
}
|
|
52
53
|
export function AdminTeamView({ children, onBulkRemove, ...props }) {
|
|
53
54
|
const hasChildren = React.Children.count(children) > 0;
|
|
@@ -139,12 +140,13 @@ export function AdminTeamView({ children, onBulkRemove, ...props }) {
|
|
|
139
140
|
onClick: async () => { await onBulkRemove(selection.selectedIds); selection.clearSelection(); },
|
|
140
141
|
}] : []),
|
|
141
142
|
];
|
|
143
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
142
144
|
if (hasChildren) {
|
|
143
145
|
return (_jsx(ListingViewShell, { portal: "admin", ...props, children: children }));
|
|
144
146
|
}
|
|
145
147
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by name or email", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
|
|
146
148
|
table.set("sort", v);
|
|
147
|
-
}, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { type: "button", variant: "primary", onClick: openInvite, className: "flex items-center gap-1.5 whitespace-nowrap", children: [_jsx(UserPlus, { className: "h-4 w-4" }), "Invite Employee"] }) }), 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-
|
|
149
|
+
}, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { type: "button", variant: "primary", onClick: openInvite, className: "flex items-center gap-1.5 whitespace-nowrap", children: [_jsx(UserPlus, { className: "h-4 w-4" }), "Invite Employee"] }) }), 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 })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No employees found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
148
150
|
{
|
|
149
151
|
label: ACTIONS.ADMIN["edit-team-member"].label,
|
|
150
152
|
onClick: () => openEdit(row),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Shield, Ban, AlertTriangle, Headphones, Flag, ClipboardList } 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
|
const STILL_ALLOWED = "✓ Still allowed";
|
|
6
6
|
export function AdminTrustGuideView() {
|
|
@@ -14,19 +14,19 @@ export function AdminTrustGuideView() {
|
|
|
14
14
|
["Sell", "Depends on scope", "✗ Blocked — store suspended"],
|
|
15
15
|
["Bid", "Can restrict with place_bids scope", "✗ Blocked"],
|
|
16
16
|
["Duration", "Set expiry date", "Permanent until admin lifts"],
|
|
17
|
-
].map(([action, soft, hard]) => (_jsxs("tr", { className: "border-b border-[var(--appkit-color-border)]/50", children: [_jsx("td", { className: "py-2 pr-4 font-medium text-[var(--appkit-color-text)]", children: action }), _jsx("td", { className: "py-2 pr-4", children: soft }), _jsx("td", { className: "py-2", children: hard })] }, action))) })] }) }), _jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mt-3", children: [_jsx("
|
|
17
|
+
].map(([action, soft, hard]) => (_jsxs("tr", { className: "border-b border-[var(--appkit-color-border)]/50", children: [_jsx("td", { className: "py-2 pr-4 font-medium text-[var(--appkit-color-text)]", children: action }), _jsx("td", { className: "py-2 pr-4", children: soft }), _jsx("td", { className: "py-2", children: hard })] }, action))) })] }) }), _jsxs(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mt-3", children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Soft ban scopes" }), ": ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "place_bids" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "write_reviews" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "create_listings" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "open_tickets" }), ", ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "join_events" }), ". Multiple scopes can be combined."] })] })),
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
Icon: AlertTriangle, title: "Scam Registry",
|
|
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: "27 scam types" }), ": Documented in the registry, covering fake payments, empty-box shipping, counterfeit graded cards, and more."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Scammer profile" }), ": Stored with slug prefix ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "scammer-" }), ". Includes phone, UPI VPA, and social handles as identifiers."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Report review" }), ": All community-submitted reports go through a review queue before publishing. Require at least 2 corroborating reports or 1 strong piece of evidence (payment screenshot, police complaint)."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Takedown requests" }), ": Route to senior admin with legal involvement. Preserve evidence before any deletion."] })] })),
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
Icon: Headphones, title: "Support Tickets",
|
|
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: "SLA" }), ": 24\u201348 hours first response. Escalation: 72-hour unresponded tickets go to senior admin queue automatically."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Categories" }), ": order_issue, billing_payment, account_access, seller_dispute, technical, other."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Limit" }), ": Buyers can have 2 active general tickets + 1 per active order. This prevents abuse. Admins can override the limit for legitimate edge cases."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Closing tickets" }), ": Only close a ticket after confirming the issue is resolved. Auto-close (7 days no response from buyer) is enabled for status-inquiry tickets only."] })] })),
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
Icon: Flag, title: "Moderation Queue",
|
|
29
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsx("li", { children: "The moderation queue surfaces flagged listings, reviews, and user-generated content for review." }), _jsxs("li", { children: [_jsx("
|
|
29
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsx("li", { children: "The moderation queue surfaces flagged listings, reviews, and user-generated content for review." }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Listing flags" }), ": Counterfeit claim, inappropriate images, misleading description. Each flag requires a decision: dismiss, warn seller, remove listing."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Review flags" }), ": PII in review text, slurs, spam. Decision: edit (remove PII), hide (dispute pending), or delete (violation confirmed)."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Appeals" }), ": Any moderation action can be appealed by the seller/user via a support ticket. Document your original decision clearly in the mod log."] })] })),
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
Icon: ClipboardList, title: "Item Requests",
|
|
@@ -34,10 +34,10 @@ function formatExpiry(expiresAt) {
|
|
|
34
34
|
return `Until ${d.toLocaleDateString()}`;
|
|
35
35
|
}
|
|
36
36
|
function HardBanPanel({ userId, isHardBanned, currentHardBanReason, showHardBanForm, setShowHardBanForm, hardBanReasonInput, setHardBanReasonInput, hardBanPending, unbanPending, onHardBan, onUnban, }) {
|
|
37
|
-
return (_jsxs("div", { className: "mb-4 rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsx("span", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Hard ban" }), isHardBanned ? (_jsx("span", { className: "rounded-full bg-
|
|
37
|
+
return (_jsxs("div", { className: "mb-4 rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsx("span", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Hard ban" }), isHardBanned ? (_jsx("span", { className: "rounded-full bg-error-surface px-2 py-0.5 text-xs font-semibold text-error", children: "Banned" })) : (_jsx("span", { className: "rounded-full bg-success-surface px-2 py-0.5 text-xs font-semibold text-success", children: "Active" }))] }), isHardBanned ? (_jsxs("div", { className: "space-y-2", children: [currentHardBanReason && _jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-400", children: ["Reason: ", currentHardBanReason] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: unbanPending, disabled: unbanPending, onClick: onUnban, children: "Lift hard ban" })] })) : showHardBanForm ? (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Ban reason (required)" }), _jsx("textarea", { value: hardBanReasonInput, onChange: (e) => setHardBanReasonInput(e.target.value), rows: 2, placeholder: "e.g. Repeated fraud, scam activity\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-red-500" })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "danger", size: "sm", isLoading: hardBanPending, disabled: !hardBanReasonInput.trim() || hardBanPending, onClick: () => onHardBan(hardBanReasonInput.trim()), children: "Confirm hard ban" }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => { setShowHardBanForm(false); setHardBanReasonInput(""); }, children: "Cancel" })] })] })) : (_jsx(Button, { type: "button", variant: "danger", size: "sm", disabled: !userId, onClick: () => setShowHardBanForm(true), children: "Impose hard ban" }))] }));
|
|
38
38
|
}
|
|
39
39
|
function SoftBanPanel({ userId, softBans, showAddSoftBan, setShowAddSoftBan, softBanAction, setSoftBanAction, softBanReason, setSoftBanReason, softBanExpiry, setSoftBanExpiry, softBanPending, liftPending, onAddSoftBan, onLiftSoftBan, }) {
|
|
40
|
-
return (_jsxs("div", { className: "rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsxs("span", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: ["Soft bans", softBans.length > 0 ? ` (${softBans.length})` : ""] }), !showAddSoftBan && (_jsx(Button, { type: "button", variant: "secondary", size: "sm", disabled: !userId, onClick: () => setShowAddSoftBan(true), children: "Add soft ban" }))] }), softBans.length > 0 && (_jsx("ul", { className: "mb-3 space-y-2", children: softBans.map((ban) => (_jsxs("li", { className: "flex items-start justify-between gap-2 rounded-md border border-zinc-200 bg-white px-3 py-2 text-xs dark:border-zinc-700 dark:bg-zinc-800", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-semibold text-zinc-800 dark:text-zinc-200", children: formatBanAction(ban.action) }), _jsx("div", { className: "text-zinc-500 dark:text-zinc-400", children: ban.reason }), _jsx("div", { className: "text-zinc-400 dark:text-zinc-
|
|
40
|
+
return (_jsxs("div", { className: "rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsxs("span", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: ["Soft bans", softBans.length > 0 ? ` (${softBans.length})` : ""] }), !showAddSoftBan && (_jsx(Button, { type: "button", variant: "secondary", size: "sm", disabled: !userId, onClick: () => setShowAddSoftBan(true), children: "Add soft ban" }))] }), softBans.length > 0 && (_jsx("ul", { className: "mb-3 space-y-2", children: softBans.map((ban) => (_jsxs("li", { className: "flex items-start justify-between gap-2 rounded-md border border-zinc-200 bg-white px-3 py-2 text-xs dark:border-zinc-700 dark:bg-zinc-800", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-semibold text-zinc-800 dark:text-zinc-200", children: formatBanAction(ban.action) }), _jsx("div", { className: "text-zinc-500 dark:text-zinc-400", children: ban.reason }), _jsx("div", { className: "text-zinc-400 dark:text-zinc-400", children: formatExpiry(ban.expiresAt) })] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: liftPending, disabled: liftPending, onClick: () => onLiftSoftBan(ban.action), children: "Lift" })] }, ban.action))) })), showAddSoftBan && (_jsxs("div", { className: "space-y-2", children: [_jsx(Select, { label: "Action to restrict", options: BANNED_ACTION_OPTIONS, value: softBanAction, onValueChange: setSoftBanAction }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Reason (required)" }), _jsx("textarea", { value: softBanReason, onChange: (e) => setSoftBanReason(e.target.value), rows: 2, placeholder: "e.g. Suspicious bid activity\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Expires at (optional \u2014 leave blank for permanent)" }), _jsx("input", { type: "datetime-local", value: softBanExpiry, onChange: (e) => setSoftBanExpiry(e.target.value), className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: softBanPending, disabled: !softBanReason.trim() || softBanPending, onClick: () => onAddSoftBan({ action: softBanAction, reason: softBanReason.trim(), ...(softBanExpiry ? { expiresAt: new Date(softBanExpiry).toISOString() } : {}) }), children: "Apply soft ban" }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => { setShowAddSoftBan(false); setSoftBanReason(""); setSoftBanExpiry(""); }, children: "Cancel" })] })] })), softBans.length === 0 && !showAddSoftBan && (_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: "No active soft bans." }))] }));
|
|
41
41
|
}
|
|
42
42
|
// --- Component ---------------------------------------------------------------
|
|
43
43
|
export function AdminUserEditorView({ open, onClose, userId, displayName, currentRole, currentIsDisabled, currentEmailVerified, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, }) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Users, Search, Edit, Monitor, UserCog, 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 AdminUsersGuideView() {
|
|
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(Users, { 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: "Users & Accounts" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Managing users, roles, sessions, and employee accounts on LetItRip." })] }), [
|
|
@@ -16,19 +16,19 @@ export function AdminUsersGuideView() {
|
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
Icon: Search, title: "User List & Search",
|
|
19
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
19
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Columns" }), ": display name, email, role badge, emailVerified, createdAt, last login."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Filter chips" }), ": All, by role, verified only, banned."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Search" }), ": by email or displayName. Email matches use the HMAC blind index \u2014 searching partial emails returns no results."] })] })),
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
Icon: Edit, title: "Editing a User",
|
|
23
|
-
content: (_jsxs(_Fragment, { children: [_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mb-3", children: "The AdminUserEditorView opens in a side drawer. Fields:" }), _jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
23
|
+
content: (_jsxs(_Fragment, { children: [_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mb-3", children: "The AdminUserEditorView opens in a side drawer. Fields:" }), _jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "role Select" }), " \u2014 changing to ", _jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "admin" }), " bypasses all RBAC checks. Only do this with explicit senior approval."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "emailVerified toggle" }), " \u2014 manually mark an email as verified (e.g. after manual ID check)."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "isDisabled" }), " \u2014 disables Firebase Auth login without a full hard ban. User sees \"account suspended\" on login."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "PII note" }), " \u2014 email and phone are HMAC-indexed. The UI shows the hashed index, not the plaintext value. Never store plaintext in Firestore."] })] })] })),
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
Icon: Monitor, title: "Sessions Management",
|
|
27
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx("
|
|
27
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Session table" }), " shows: browser, OS, device type, masked IP (last octet zeroed), last activity, and estimated location."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Force expire" }), " a session if you suspect account takeover \u2014 the user will be signed out on their next request."] }), _jsxs("li", { children: [_jsx(Span, { weight: "bold", className: "text-[var(--appkit-color-text)]", children: "Hard ban cascade" }), " \u2014 issuing a hard ban automatically expires all active sessions for that user."] })] })),
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
Icon: UserCog, title: "Employee Accounts",
|
|
31
|
-
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: ["Create employee accounts via ", _jsx("
|
|
31
|
+
content: (_jsxs("ul", { className: GC.listMuted, children: [_jsxs("li", { children: ["Create employee accounts via ", _jsx(Span, { weight: "bold", children: "Admin \u2192 Team" }), ", not by manually writing to Firestore."] }), _jsxs("li", { children: [_jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "role: \"employee\"" }), " has no elevated Firestore rules by default \u2014 access is governed entirely by the permission group assigned to them."] }), _jsxs("li", { children: [_jsx("code", { className: "text-xs bg-[var(--appkit-color-border)] px-1 rounded", children: "role: \"admin\"" }), " bypasses all RBAC \u2014 use sparingly. One admin account per real person maximum."] })] })),
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
Icon: AlertTriangle, title: "PII Handling Notice",
|
|
@@ -14,6 +14,7 @@ import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } fr
|
|
|
14
14
|
import { apiClient } from "../../../http";
|
|
15
15
|
import { DataTable } from "./DataTable";
|
|
16
16
|
import { AdminUserEditorView } from "./AdminUserEditorView";
|
|
17
|
+
import { useBottomActions } from "../../layout";
|
|
17
18
|
const PAGE_SIZE = 25;
|
|
18
19
|
const FILTER_KEYS = ["status", "role"];
|
|
19
20
|
const DEFAULT_SORT = "-createdAt";
|
|
@@ -27,7 +28,7 @@ const ROLE_OPTIONS = ADMIN_USER_ROLE_TABS;
|
|
|
27
28
|
function UsersFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
28
29
|
if (!filterOpen)
|
|
29
30
|
return null;
|
|
30
|
-
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-
|
|
31
|
+
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: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Role", tabs: ROLE_OPTIONS, value: pendingFilters.role ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, role: 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})` : ""] }) })] })] }));
|
|
31
32
|
}
|
|
32
33
|
export function AdminUsersView({ children, ...props }) {
|
|
33
34
|
const hasChildren = React.Children.count(children) > 0;
|
|
@@ -148,9 +149,12 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
148
149
|
}
|
|
149
150
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search users, email, or seller handles", 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 }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
150
151
|
{ id: "manage", label: ACTIONS.ADMIN["manage-user"].label, variant: "primary", onClick: () => { setSelectedRow(rows.find(r => r.id === selection.selectedIds[0]) ?? null); setDrawerOpen(true); selection.clearSelection(); } },
|
|
151
|
-
] }), 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-
|
|
152
|
+
] }), 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 users found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), renderRowActions: (row) => {
|
|
152
153
|
const ur = row;
|
|
153
154
|
const isBanned = ur.status === "Hard banned";
|
|
155
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
156
|
+
{ id: "manage", label: ACTIONS.ADMIN["manage-user"].label, variant: "primary", onClick: () => { setSelectedRow(rows.find(r => r.id === selection.selectedIds[0]) ?? null); setDrawerOpen(true); selection.clearSelection(); } },
|
|
157
|
+
] } } : {});
|
|
154
158
|
return (_jsx(RowActionMenu, { actions: [
|
|
155
159
|
{
|
|
156
160
|
label: ACTIONS.ADMIN["manage-user"].label,
|
|
@@ -4,7 +4,7 @@ import { Text } from "../../../ui";
|
|
|
4
4
|
const FLAG_BADGES = [
|
|
5
5
|
{ key: "featured", label: "Featured", color: "bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300" },
|
|
6
6
|
{ key: "isPromoted", label: "Promoted", color: "bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300" },
|
|
7
|
-
{ key: "isOnSale", label: "Sale", color: "bg-
|
|
7
|
+
{ key: "isOnSale", label: "Sale", color: "bg-success-surface text-success" },
|
|
8
8
|
{ key: "isSold", label: "Sold", color: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400" },
|
|
9
9
|
];
|
|
10
10
|
function StatusBadge({ status }) {
|
|
@@ -27,14 +27,14 @@ function AdminCardItem({ row, view, selected, onToggleSelect, onRowClick, }) {
|
|
|
27
27
|
return (_jsxs("div", { className: [
|
|
28
28
|
"flex items-center gap-3 px-4 py-3 cursor-pointer transition-colors hover:bg-zinc-50 dark:hover:bg-slate-800/50",
|
|
29
29
|
selected ? "bg-primary-50/40 dark:bg-primary-900/10" : "",
|
|
30
|
-
].filter(Boolean).join(" "), onClick: handleClick, role: onRowClick ? "button" : undefined, children: [onToggleSelect && (_jsx("div", { "data-checkbox": true, className: "shrink-0", onClick: (e) => { e.stopPropagation(); onToggleSelect(row.id); }, children: _jsx("input", { type: "checkbox", checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }) })), _jsxs("div", { className: "flex-1 min-w-0 space-y-0.5", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 truncate", children: row.primary }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 truncate", children: row.secondary })] }), flags.length > 0 && (_jsx("div", { className: "hidden sm:flex items-center gap-1 shrink-0", children: flags.map(({ key, label, color }) => (_jsx("span", { className: `inline-flex rounded-full px-1.5 py-0.5 text-[10px] font-medium ${color}`, children: label }, key))) })), _jsx(StatusBadge, { status: row.status }), _jsx("span", { className: "hidden sm:block text-xs text-zinc-400 dark:text-zinc-
|
|
30
|
+
].filter(Boolean).join(" "), onClick: handleClick, role: onRowClick ? "button" : undefined, children: [onToggleSelect && (_jsx("div", { "data-checkbox": true, className: "shrink-0", onClick: (e) => { e.stopPropagation(); onToggleSelect(row.id); }, children: _jsx("input", { type: "checkbox", checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }) })), _jsxs("div", { className: "flex-1 min-w-0 space-y-0.5", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 truncate", children: row.primary }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 truncate", children: row.secondary })] }), flags.length > 0 && (_jsx("div", { className: "hidden sm:flex items-center gap-1 shrink-0", children: flags.map(({ key, label, color }) => (_jsx("span", { className: `inline-flex rounded-full px-1.5 py-0.5 text-[10px] font-medium ${color}`, children: label }, key))) })), _jsx(StatusBadge, { status: row.status }), _jsx("span", { className: "hidden sm:block text-xs text-zinc-400 dark:text-zinc-400 shrink-0 w-24 text-right", children: row.updatedAt })] }));
|
|
31
31
|
}
|
|
32
32
|
return (_jsxs("div", { className: [
|
|
33
33
|
"rounded-xl border overflow-hidden cursor-pointer transition-all hover:shadow-md hover:-translate-y-0.5",
|
|
34
34
|
selected
|
|
35
35
|
? "border-primary ring-1 ring-primary/20 bg-primary-50/30 dark:bg-primary-900/10"
|
|
36
36
|
: "border-zinc-100 dark:border-slate-700 bg-white dark:bg-slate-900",
|
|
37
|
-
].filter(Boolean).join(" "), onClick: handleClick, role: onRowClick ? "button" : undefined, children: [onToggleSelect && (_jsxs("div", { "data-checkbox": true, className: "flex items-center gap-2 px-3 pt-3", onClick: (e) => { e.stopPropagation(); onToggleSelect(row.id); }, children: [_jsx("input", { type: "checkbox", checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }), flags.length > 0 && (_jsx("div", { className: "flex items-center gap-1 flex-wrap", children: flags.map(({ key, label, color }) => (_jsx("span", { className: `inline-flex rounded-full px-1.5 py-0.5 text-[10px] font-medium ${color}`, children: label }, key))) }))] })), _jsxs("div", { className: "p-3.5 space-y-2", children: [_jsxs("div", { className: "space-y-0.5", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 line-clamp-2 leading-snug", children: row.primary }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 truncate", children: row.secondary })] }), _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(StatusBadge, { status: row.status }), _jsx("span", { className: "text-[11px] text-zinc-400 dark:text-zinc-
|
|
37
|
+
].filter(Boolean).join(" "), onClick: handleClick, role: onRowClick ? "button" : undefined, children: [onToggleSelect && (_jsxs("div", { "data-checkbox": true, className: "flex items-center gap-2 px-3 pt-3", onClick: (e) => { e.stopPropagation(); onToggleSelect(row.id); }, children: [_jsx("input", { type: "checkbox", checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }), flags.length > 0 && (_jsx("div", { className: "flex items-center gap-1 flex-wrap", children: flags.map(({ key, label, color }) => (_jsx("span", { className: `inline-flex rounded-full px-1.5 py-0.5 text-[10px] font-medium ${color}`, children: label }, key))) }))] })), _jsxs("div", { className: "p-3.5 space-y-2", children: [_jsxs("div", { className: "space-y-0.5", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 line-clamp-2 leading-snug", children: row.primary }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 truncate", children: row.secondary })] }), _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(StatusBadge, { status: row.status }), _jsx("span", { className: "text-[11px] text-zinc-400 dark:text-zinc-400 shrink-0", children: row.updatedAt })] })] })] }));
|
|
38
38
|
}
|
|
39
39
|
export function AdminViewCards({ rows, view, isLoading, emptyLabel = "No items found", onRowClick, selectedIdSet, onToggleSelect, }) {
|
|
40
40
|
if (isLoading) {
|
|
@@ -63,5 +63,5 @@ export function AdminWishlistsView({ children, ...props }) {
|
|
|
63
63
|
if (hasChildren) {
|
|
64
64
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
65
65
|
}
|
|
66
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search by user ID", 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-
|
|
66
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search by user ID", 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 user wishlists found", selectedIds: selectedIds, onToggleSelect: toggleSelect, onToggleSelectAll: (next) => setSelectedIds(next ? new Set(rows.map((r) => r.id)) : new Set()) })] })] }));
|
|
67
67
|
}
|
|
@@ -43,5 +43,5 @@ export function DataTable({ columns: columnsProp, rows, isLoading, sortKey, sort
|
|
|
43
43
|
const columns = (columnsProp ?? DEFAULT_COLUMNS);
|
|
44
44
|
const selectionEnabled = Boolean(onToggleSelect);
|
|
45
45
|
const allRowsSelected = selectionEnabled && rows.length > 0 && rows.every((r) => selectedIds?.has(r.id));
|
|
46
|
-
return (_jsxs(Div, { className: "overflow-hidden
|
|
46
|
+
return (_jsxs(Div, { surface: "card", className: "overflow-hidden", children: [_jsx(Div, { className: "overflow-x-auto", children: _jsxs("table", { "data-testid": "data-table", className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b border-neutral-200 dark:border-slate-700 bg-neutral-50 dark:bg-slate-800", children: [selectionEnabled && (_jsx("th", { scope: "col", className: "w-10 px-2 py-3", children: onToggleSelectAll && (_jsx("input", { type: "checkbox", "data-testid": "select-all-checkbox", "aria-label": allRowsSelected ? "Deselect all" : "Select all", checked: allRowsSelected, onChange: () => onToggleSelectAll(!allRowsSelected), className: "h-4 w-4 rounded border-zinc-300 dark:border-slate-600 accent-zinc-900 dark:accent-zinc-100" })) })), columns.map((col) => (_jsxs("th", { scope: "col", onClick: col.sortable && onSort ? () => onSort(col.key) : undefined, className: `px-4 py-3 text-left font-semibold text-neutral-900 dark:text-zinc-100 ${col.sortable && onSort ? "cursor-pointer select-none hover:text-primary" : ""} ${col.className ?? ""}`, children: [col.header, col.sortable && sortKey === col.key && (_jsx(Span, { className: "ml-1", children: sortDir === "asc" ? "↑" : "↓" }))] }, col.key))), renderRowActions && _jsx("th", { scope: "col", className: "w-12 px-2 py-3" })] }) }), _jsx("tbody", { children: isLoading ? (Array.from({ length: 5 }).map((_, i) => (_jsxs("tr", { className: "border-b border-neutral-100 dark:border-slate-700", children: [selectionEnabled && _jsx("td", { className: "w-10 px-2 py-3" }), columns.map((col) => (_jsx("td", { className: "px-4 py-3", children: _jsx(Div, { className: "h-4 w-full animate-pulse rounded bg-neutral-200 dark:bg-slate-700" }) }, col.key)))] }, i)))) : rows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: columns.length + (selectionEnabled ? 1 : 0) + (renderRowActions ? 1 : 0), className: "px-4 py-12 text-center text-neutral-500 dark:text-zinc-400", children: emptyLabel }) })) : (rows.map((row) => (_jsx(SelectableRow, { row: row, columns: columns, isSelected: selectedIds?.has(row.id) ?? false, onToggle: onToggleSelect, renderRowActions: renderRowActions, onRowClick: onRowClick, rowHref: getRowHref?.(row), selectionEnabled: selectionEnabled }, row.id)))) })] }) }), totalPages > 1 && onPageChange && (_jsx(Div, { className: "flex items-center justify-end gap-2 border-t border-neutral-200 dark:border-slate-700 px-4 py-3", children: Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (_jsx(Button, { onClick: () => onPageChange(p), variant: p === currentPage ? "primary" : "ghost", size: "sm", className: `h-8 w-8 rounded text-xs font-medium transition ${p === currentPage ? "bg-neutral-900 text-white" : "text-neutral-600 dark:text-zinc-300 hover:bg-neutral-100 dark:hover:bg-slate-800"}`, children: p }, p))) }))] }));
|
|
47
47
|
}
|
|
@@ -53,6 +53,15 @@ export declare const ADMIN_PRODUCT_LISTING_TYPE_TABS: readonly [{
|
|
|
53
53
|
}, {
|
|
54
54
|
readonly id: "Prize Draws";
|
|
55
55
|
readonly label: "Prize Draws";
|
|
56
|
+
}, {
|
|
57
|
+
readonly id: "Classifieds";
|
|
58
|
+
readonly label: "Classifieds";
|
|
59
|
+
}, {
|
|
60
|
+
readonly id: "Digital Codes";
|
|
61
|
+
readonly label: "Digital Codes";
|
|
62
|
+
}, {
|
|
63
|
+
readonly id: "Live Items";
|
|
64
|
+
readonly label: "Live Items";
|
|
56
65
|
}];
|
|
57
66
|
/** Admin > Blog — post status filter chip set. */
|
|
58
67
|
export declare const ADMIN_BLOG_STATUS_TABS: readonly [{
|
|
@@ -26,6 +26,9 @@ export const ADMIN_PRODUCT_LISTING_TYPE_TABS = [
|
|
|
26
26
|
{ id: "Auctions", label: "Auctions" },
|
|
27
27
|
{ id: "Pre-orders", label: "Pre-orders" },
|
|
28
28
|
{ id: "Prize Draws", label: "Prize Draws" },
|
|
29
|
+
{ id: "Classifieds", label: "Classifieds" },
|
|
30
|
+
{ id: "Digital Codes", label: "Digital Codes" },
|
|
31
|
+
{ id: "Live Items", label: "Live Items" },
|
|
29
32
|
];
|
|
30
33
|
/** Admin > Blog — post status filter chip set. */
|
|
31
34
|
export const ADMIN_BLOG_STATUS_TABS = [
|
|
@@ -51,7 +51,7 @@ function AuctionRow({ auction, portal, }) {
|
|
|
51
51
|
const sorted = useMemo(() => [...auction.bids].sort((a, b) => b.bidAmount - a.bidAmount), [auction.bids]);
|
|
52
52
|
const highest = sorted[0]?.bidAmount ?? 0;
|
|
53
53
|
const isWinning = auction.bids.some((b) => b.isWinning);
|
|
54
|
-
return (_jsxs(Div, { className: "border border-[var(--appkit-color-border)] rounded-xl overflow-hidden bg-[var(--appkit-color-surface)] shadow-sm", children: [_jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "w-full flex items-center gap-3 px-4 py-3 hover:bg-[var(--appkit-color-border-subtle)] transition-colors text-left", "aria-expanded": expanded, children: [_jsx(ChevronRight, { className: `shrink-0 text-[var(--appkit-color-text-muted)] transition-transform ${expanded ? "rotate-90" : ""}`, size: 16 }), _jsx(Div, { className: "flex-1 min-w-0", children: _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)] line-clamp-1", children: auction.productTitle }) }), _jsxs(Row, { gap: "sm", className: "shrink-0
|
|
54
|
+
return (_jsxs(Div, { className: "border border-[var(--appkit-color-border)] rounded-xl overflow-hidden bg-[var(--appkit-color-surface)] shadow-sm", children: [_jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "w-full flex items-center gap-3 px-4 py-3 hover:bg-[var(--appkit-color-border-subtle)] transition-colors text-left", "aria-expanded": expanded, children: [_jsx(ChevronRight, { className: `shrink-0 text-[var(--appkit-color-text-muted)] transition-transform ${expanded ? "rotate-90" : ""}`, size: 16 }), _jsx(Div, { className: "flex-1 min-w-0", children: _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)] line-clamp-1", children: auction.productTitle }) }), _jsxs(Row, { gap: "sm", className: "shrink-0", children: [isWinning && (_jsx(Badge, { variant: "active", children: "Winning" })), _jsxs(Text, { variant: "secondary", className: "text-xs", children: [auction.bids.length, " bid", auction.bids.length !== 1 ? "s" : ""] }), _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)]", children: paise(highest) })] })] }), expanded && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", children: [_jsxs("div", { className: "grid text-xs font-medium text-[var(--appkit-color-text-muted)] uppercase tracking-wide px-4 py-2 border-b border-[var(--appkit-color-border-subtle)]", style: { gridTemplateColumns: portal === "buyer" ? "1fr auto auto" : "1fr 1fr auto auto" }, children: [_jsx("span", { children: portal === "buyer" ? "Amount" : "Bidder" }), portal !== "buyer" && _jsx("span", { children: "Amount" }), _jsx("span", { children: "Status" }), _jsx("span", { className: "text-right", children: "Time" })] }), sorted.map((bid) => (_jsxs("div", { className: "grid items-center px-4 py-2.5 border-b border-[var(--appkit-color-border-subtle)] last:border-0 hover:bg-[var(--appkit-color-border-subtle)] transition-colors", style: { gridTemplateColumns: portal === "buyer" ? "1fr auto auto" : "1fr 1fr auto auto" }, children: [portal !== "buyer" && (_jsx(Text, { className: "text-sm text-[var(--appkit-color-text)] truncate pr-3", children: bid.userName || bid.userId })), _jsx(Text, { className: "text-sm font-medium text-[var(--appkit-color-text)]", children: paise(bid.bidAmount) }), _jsx(Badge, { variant: STATUS_VARIANT[bid.status] ?? "pending", className: "capitalize", children: bid.status }), _jsx(Text, { variant: "secondary", className: "text-xs text-right", children: relDate(bid.bidDate) })] }, bid.id)))] }))] }));
|
|
55
55
|
}
|
|
56
56
|
export function AuctionBidsTable({ bids, portal = "buyer", emptyLabel = "No bids found.", }) {
|
|
57
57
|
const auctions = useMemo(() => groupByAuction(bids), [bids]);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useBottomActions } from "../../layout/hooks/useBottomActions";
|
|
3
|
+
import { formatCurrency } from "../../../utils/number.formatter";
|
|
4
|
+
import { ACTION_ID } from "../../products/constants/action-defs";
|
|
5
|
+
export function AuctionBottomActions({ currentBid, currency, bidCount, isEnded, }) {
|
|
6
|
+
useBottomActions(isEnded
|
|
7
|
+
? {}
|
|
8
|
+
: {
|
|
9
|
+
actions: [
|
|
10
|
+
{
|
|
11
|
+
id: ACTION_ID.PLACE_BID,
|
|
12
|
+
label: "Place Bid",
|
|
13
|
+
variant: "primary",
|
|
14
|
+
onClick: () => {
|
|
15
|
+
document
|
|
16
|
+
.getElementById("auction-bid-form")
|
|
17
|
+
?.scrollIntoView({ behavior: "smooth" });
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
infoLabel: `${formatCurrency(currentBid, currency)} · ${bidCount} bid${bidCount !== 1 ? "s" : ""}`,
|
|
22
|
+
});
|
|
23
|
+
return null;
|
|
24
|
+
}
|