@mohasinac/appkit 3.4.8 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/classified/ClassifiedDetailView.js +1 -1
- package/dist/_internal/client/features/digital-code/CodeRevealPanel.js +1 -1
- package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +1 -1
- package/dist/_internal/client/features/filters/filter-load-options.js +10 -9
- package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +6 -1
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +11 -4
- package/dist/_internal/client/features/live/LiveItemDetailView.js +2 -2
- package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +10 -3
- package/dist/_internal/client/features/maintenance/views/ServerErrorsListView.js +2 -2
- package/dist/_internal/client/scaffolds/DashboardScaffold.js +3 -1
- package/dist/_internal/server/features/checkout/actions.d.ts +16 -0
- package/dist/_internal/server/features/checkout/actions.js +149 -35
- package/dist/_internal/server/features/checkout/bundle-expansion.d.ts +31 -0
- package/dist/_internal/server/features/checkout/bundle-expansion.js +41 -0
- package/dist/_internal/server/features/lottery/actions.js +14 -4
- package/dist/_internal/server/features/maintenance/analyze.js +4 -2
- package/dist/_internal/server/features/orders/actions.js +9 -0
- package/dist/_internal/server/features/orders/adapters.js +19 -0
- package/dist/_internal/server/features/seo/robots.js +1 -1
- package/dist/_internal/server/functions/firestore.d.ts +47 -1
- package/dist/_internal/server/functions/firestore.js +56 -1
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +9 -1
- package/dist/_internal/server/jobs/core/catalogueImageStalenessReminder.d.ts +10 -0
- package/dist/_internal/server/jobs/core/catalogueImageStalenessReminder.js +49 -0
- package/dist/_internal/server/jobs/core/cleanupRtdbEvents.js +32 -0
- package/dist/_internal/server/jobs/core/hardBanCascade.d.ts +26 -0
- package/dist/_internal/server/jobs/core/hardBanCascade.js +179 -0
- package/dist/_internal/server/jobs/core/index.d.ts +4 -1
- package/dist/_internal/server/jobs/core/index.js +4 -1
- package/dist/_internal/server/jobs/core/jobRunners.d.ts +28 -0
- package/dist/_internal/server/jobs/core/jobRunners.js +37 -0
- package/dist/_internal/server/jobs/core/onCatalogueSubmittedForApproval.d.ts +15 -0
- package/dist/_internal/server/jobs/core/onCatalogueSubmittedForApproval.js +38 -0
- package/dist/_internal/server/jobs/core/onJobCreated.d.ts +22 -0
- package/dist/_internal/server/jobs/core/onJobCreated.js +59 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.d.ts +6 -1
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +5 -2
- package/dist/_internal/server/jobs/handlers/catalogueImageStalenessReminder.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/catalogueImageStalenessReminder.js +2 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +6 -0
- package/dist/_internal/server/jobs/handlers/index.js +8 -0
- package/dist/_internal/server/jobs/handlers/onCatalogueSubmittedForApproval.d.ts +12 -0
- package/dist/_internal/server/jobs/handlers/onCatalogueSubmittedForApproval.js +20 -0
- package/dist/_internal/server/jobs/handlers/onJobCreated.d.ts +3 -0
- package/dist/_internal/server/jobs/handlers/onJobCreated.js +9 -0
- package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.d.ts +25 -0
- package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +71 -0
- package/dist/_internal/server/jobs/handlers/onShipmentDeleted.d.ts +14 -0
- package/dist/_internal/server/jobs/handlers/onShipmentDeleted.js +23 -0
- package/dist/_internal/server/jobs/handlers/onShipmentItemWrite.d.ts +17 -0
- package/dist/_internal/server/jobs/handlers/onShipmentItemWrite.js +26 -0
- package/dist/_internal/server/jobs/handlers/weeklyPayoutEligibility.js +3 -1
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +136 -0
- package/dist/_internal/shared/features/blog/schema.d.ts +8 -8
- package/dist/_internal/shared/features/brands/schema.d.ts +4 -4
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +8 -8
- package/dist/_internal/shared/features/events/schema.d.ts +4 -4
- package/dist/_internal/shared/features/products/schema.d.ts +8 -8
- package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
- package/dist/_internal/shared/features/reviews/schema.d.ts +4 -4
- package/dist/client/api/useFeatureFlags.d.ts +22 -0
- package/dist/client/api/useFeatureFlags.js +27 -0
- package/dist/client/observability/reportClientError.js +3 -2
- package/dist/client.d.ts +4 -2
- package/dist/client.js +2 -1
- package/dist/configs/next.js +1 -0
- package/dist/constants/api-endpoints.d.ts +279 -3
- package/dist/constants/api-endpoints.js +130 -2
- package/dist/core/hooks/useSyncManager.js +5 -4
- package/dist/errors/conflict-error.d.ts +4 -0
- package/dist/errors/conflict-error.js +6 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/features/about/components/FeesView.js +2 -2
- package/dist/features/about/components/HelpPageView.js +2 -2
- package/dist/features/about/components/HowAuctionsWorkView.js +2 -2
- package/dist/features/about/components/HowCheckoutWorksView.js +2 -2
- package/dist/features/about/components/HowEmiWorksView.js +2 -2
- package/dist/features/about/components/HowOffersWorkView.js +3 -3
- package/dist/features/about/components/HowOrdersWorkView.js +3 -3
- package/dist/features/about/components/HowPayoutsWorkView.js +2 -2
- package/dist/features/about/components/HowPreOrdersWorkView.js +2 -2
- package/dist/features/about/components/HowReviewsWorkView.js +2 -2
- package/dist/features/about/components/PublicProfileView.d.ts +8 -1
- package/dist/features/about/components/PublicProfileView.js +11 -9
- package/dist/features/about/components/SecurityPrivacyView.js +2 -2
- package/dist/features/about/components/UnauthorizedView.js +2 -2
- package/dist/features/account/components/AddressBook.js +1 -1
- package/dist/features/account/components/AddressesIndexListing.js +1 -1
- package/dist/features/account/components/NotificationBell.js +5 -5
- package/dist/features/account/components/NotificationPreferencesPanel.js +5 -2
- package/dist/features/account/components/UserOffersPanel.js +5 -4
- package/dist/features/account/components/UserSidebar.js +2 -2
- package/dist/features/account/components/UserSupportView.js +3 -3
- package/dist/features/account/schemas/index.d.ts +12 -12
- package/dist/features/addresses/schemas/index.d.ts +6 -6
- package/dist/features/admin/components/AdminAdEditorView.js +3 -3
- package/dist/features/admin/components/AdminAddressEditorView.js +3 -3
- package/dist/features/admin/components/AdminAdsView.js +3 -3
- package/dist/features/admin/components/AdminBlogEditorView.js +1 -1
- package/dist/features/admin/components/AdminBlogView.js +12 -15
- package/dist/features/admin/components/AdminBrandEditorView.js +1 -1
- package/dist/features/admin/components/AdminBrandsView.js +12 -13
- package/dist/features/admin/components/AdminBundleEditorView.js +5 -4
- package/dist/features/admin/components/AdminBundlesView.js +32 -23
- package/dist/features/admin/components/AdminCarouselEditorView.js +5 -5
- package/dist/features/admin/components/AdminCarouselView.js +2 -2
- package/dist/features/admin/components/AdminCatalogGuideView.js +1 -1
- package/dist/features/admin/components/AdminCategoriesView.js +12 -13
- package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
- package/dist/features/admin/components/AdminContactEditorView.js +2 -2
- package/dist/features/admin/components/AdminCouponEditorView.d.ts +10 -1
- package/dist/features/admin/components/AdminCouponEditorView.js +7 -2
- package/dist/features/admin/components/AdminCouponsView.js +32 -27
- package/dist/features/admin/components/AdminEmployeeEditorView.js +3 -3
- package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
- package/dist/features/admin/components/AdminFaqsView.js +20 -20
- package/dist/features/admin/components/AdminFeatureEditorView.js +5 -5
- package/dist/features/admin/components/AdminFeaturesView.js +9 -10
- package/dist/features/admin/components/AdminFilterBar.js +1 -1
- package/dist/features/admin/components/AdminFulfillmentView.js +4 -4
- package/dist/features/admin/components/AdminGuideHubView.js +2 -2
- package/dist/features/admin/components/AdminMediaView.js +3 -2
- package/dist/features/admin/components/AdminNavigationView.js +1 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +3 -3
- package/dist/features/admin/components/AdminOrdersView.js +21 -30
- package/dist/features/admin/components/AdminPageHeader.js +3 -3
- package/dist/features/admin/components/AdminPayoutsView.js +8 -8
- package/dist/features/admin/components/AdminPrizeDrawsView.js +8 -9
- package/dist/features/admin/components/AdminProductEditorView.d.ts +9 -1
- package/dist/features/admin/components/AdminProductEditorView.js +7 -4
- package/dist/features/admin/components/AdminProductsView.js +30 -41
- package/dist/features/admin/components/AdminReviewsView.js +14 -17
- package/dist/features/admin/components/AdminScammerEditorView.js +5 -5
- package/dist/features/admin/components/AdminSectionsView.js +3 -3
- package/dist/features/admin/components/AdminSessionsView.js +8 -10
- package/dist/features/admin/components/AdminSidebar.js +2 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +10 -3
- package/dist/features/admin/components/AdminStoreEditorView.js +4 -5
- package/dist/features/admin/components/AdminStoresView.js +18 -13
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +7 -7
- package/dist/features/admin/components/AdminTeamView.js +9 -5
- package/dist/features/admin/components/AdminUsersView.js +19 -15
- package/dist/features/admin/components/AdminViewCards.js +5 -5
- package/dist/features/admin/components/DashboardStats.js +1 -1
- package/dist/features/admin/components/DataListingView.js +2 -2
- package/dist/features/admin/components/DataTable.js +2 -2
- package/dist/features/admin/components/DrawerFormFooter.js +1 -1
- package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +2 -2
- package/dist/features/admin/components/analytics/AdminStatCard.js +1 -1
- package/dist/features/admin/hooks/useAdminListing.d.ts +3 -2
- package/dist/features/admin/hooks/useAdminListing.js +8 -2
- package/dist/features/admin/permission-map.js +2 -0
- package/dist/features/admin/schemas/firestore.d.ts +7 -0
- package/dist/features/admin/schemas/firestore.js +4 -0
- package/dist/features/auctions/components/AuctionBidsTable.js +1 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +7 -7
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -3
- package/dist/features/auctions/components/MarketplaceAuctionGrid.js +4 -4
- package/dist/features/auctions/components/PlaceBidFormClient.js +1 -1
- package/dist/features/auctions/schemas/index.d.ts +16 -16
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +2 -0
- package/dist/features/auth/schemas/index.d.ts +6 -6
- package/dist/features/before-after/schemas/index.d.ts +3 -3
- package/dist/features/blog/actions/blog-actions.d.ts +32 -32
- package/dist/features/blog/components/BlogFeaturedCard.js +2 -2
- package/dist/features/blog/components/BlogIndexListing.js +2 -2
- package/dist/features/blog/components/BlogListView.js +4 -4
- package/dist/features/blog/components/BlogPostView.js +4 -4
- package/dist/features/blog/schemas/index.d.ts +77 -77
- package/dist/features/cart/components/CartDrawer.js +2 -2
- package/dist/features/catalogue/actions/approve-catalogue-listing.d.ts +12 -0
- package/dist/features/catalogue/actions/approve-catalogue-listing.js +50 -0
- package/dist/features/catalogue/actions/list-from-catalogue.d.ts +17 -0
- package/dist/features/catalogue/actions/list-from-catalogue.js +50 -0
- package/dist/features/catalogue/actions/submit-for-approval.d.ts +8 -0
- package/dist/features/catalogue/actions/submit-for-approval.js +30 -0
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.d.ts +2 -0
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +53 -0
- package/dist/features/catalogue/components/CatalogueItemEditorView.d.ts +7 -0
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +79 -0
- package/dist/features/catalogue/components/PublicCatalogueView.d.ts +5 -0
- package/dist/features/catalogue/components/PublicCatalogueView.js +22 -0
- package/dist/features/catalogue/components/UserCatalogueView.d.ts +12 -0
- package/dist/features/catalogue/components/UserCatalogueView.js +51 -0
- package/dist/features/catalogue/index.d.ts +12 -0
- package/dist/features/catalogue/index.js +9 -0
- package/dist/features/catalogue/repository/catalogue.repository.d.ts +39 -0
- package/dist/features/catalogue/repository/catalogue.repository.js +68 -0
- package/dist/features/catalogue/schemas/firestore.d.ts +47 -0
- package/dist/features/catalogue/schemas/firestore.js +22 -0
- package/dist/features/catalogue/schemas/index.d.ts +2 -0
- package/dist/features/catalogue/schemas/index.js +2 -0
- package/dist/features/catalogue/schemas/validation.d.ts +71 -0
- package/dist/features/catalogue/schemas/validation.js +25 -0
- package/dist/features/catalogue/utils/freshness.d.ts +10 -0
- package/dist/features/catalogue/utils/freshness.js +15 -0
- package/dist/features/catalogue/utils/product-from-item.d.ts +15 -0
- package/dist/features/catalogue/utils/product-from-item.js +34 -0
- package/dist/features/categories/components/BrandDetailPageView.js +1 -1
- package/dist/features/categories/components/BrandDetailTabs.js +2 -2
- package/dist/features/categories/components/BundleCollage.js +2 -2
- package/dist/features/categories/components/BundleItemsPicker.js +3 -2
- package/dist/features/categories/components/CategoriesIndexListing.js +8 -6
- package/dist/features/categories/components/CategoryBundlesListing.js +10 -4
- package/dist/features/categories/components/CategoryDetailPageView.js +1 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +2 -2
- package/dist/features/categories/components/CategoryGrid.js +3 -3
- package/dist/features/categories/components/CategoryProductsListing.js +1 -1
- package/dist/features/categories/components/CategorySortSelect.js +1 -1
- package/dist/features/categories/components/CategoryStoresListing.js +1 -1
- package/dist/features/categories/components/CategoryTableColumns.js +1 -1
- package/dist/features/categories/components/CategoryTree.js +2 -2
- package/dist/features/categories/components/ConcernCard.js +1 -1
- package/dist/features/categories/hooks/useCategories.js +1 -1
- package/dist/features/categories/schemas/index.d.ts +32 -32
- package/dist/features/classified/components/ClassifiedFilters.js +2 -2
- package/dist/features/classified/components/ClassifiedIndexListing.js +9 -3
- package/dist/features/collections/schemas/index.d.ts +2 -2
- package/dist/features/consultation/components/ConsultationForm.js +1 -1
- package/dist/features/consultation/schemas/index.d.ts +10 -10
- package/dist/features/contact/components/ContactForm.js +4 -4
- package/dist/features/contact/components/ContactInfoSidebar.js +1 -1
- package/dist/features/contact/components/ContactPageView.js +2 -1
- package/dist/features/copilot/components/AdminCopilotView.js +5 -4
- package/dist/features/corporate/schemas/index.d.ts +2 -2
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +3 -3
- package/dist/features/events/components/AdminEventEditorView.js +4 -4
- package/dist/features/events/components/AdminEventEntriesView.js +2 -2
- package/dist/features/events/components/AdminEventsView.js +8 -8
- package/dist/features/events/components/EventBanner.js +1 -1
- package/dist/features/events/components/EventCard.js +3 -3
- package/dist/features/events/components/EventLeaderboard.js +1 -1
- package/dist/features/events/components/EventOfferCard.js +1 -1
- package/dist/features/events/components/EventPollWidget.js +6 -5
- package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
- package/dist/features/events/components/EventRaffleWinnerView.js +5 -5
- package/dist/features/events/components/EventRafflesSection.js +1 -1
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/events/components/EventsIndexListing.js +4 -2
- package/dist/features/events/components/EventsListView.js +2 -2
- package/dist/features/events/components/SpinWheelView.js +11 -5
- package/dist/features/events/schemas/index.d.ts +100 -100
- package/dist/features/faq/actions/faq-actions.d.ts +6 -6
- package/dist/features/faq/components/ContactCTA.js +3 -3
- package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
- package/dist/features/faq/components/FAQHelpfulButtons.js +3 -3
- package/dist/features/faq/schemas/index.d.ts +12 -12
- package/dist/features/filters/AsyncFacetSection.js +4 -4
- package/dist/features/filters/FilterFacetSection.js +8 -8
- package/dist/features/filters/RangeFilter.js +2 -2
- package/dist/features/filters/SwitchFilter.js +1 -1
- package/dist/features/forms/Checkbox.js +2 -8
- package/dist/features/forms/Input.js +2 -1
- package/dist/features/forms/Radio.js +3 -3
- package/dist/features/forms/Select.d.ts +1 -1
- package/dist/features/forms/Select.js +3 -2
- package/dist/features/forms/Slider.js +5 -11
- package/dist/features/forms/Textarea.js +2 -1
- package/dist/features/grouped/schemas/index.d.ts +6 -6
- package/dist/features/homepage/actions/homepage-section-actions.d.ts +4 -4
- package/dist/features/homepage/components/AdvertisementBanner.js +4 -4
- package/dist/features/homepage/components/AnnouncementBar.js +3 -3
- package/dist/features/homepage/components/BeforeAfterCard.js +1 -1
- package/dist/features/homepage/components/BrandsSection.js +9 -9
- package/dist/features/homepage/components/CTABannerSection.js +1 -1
- package/dist/features/homepage/components/CharacterHotspot.js +4 -4
- package/dist/features/homepage/components/CharacterHotspotForm.js +15 -13
- package/dist/features/homepage/components/CollectionCardsSection.js +1 -1
- package/dist/features/homepage/components/CustomCardsSection.js +6 -6
- package/dist/features/homepage/components/CustomerReviewsSection.js +2 -2
- package/dist/features/homepage/components/FAQSection.js +2 -2
- package/dist/features/homepage/components/FeaturedBundlesSection.js +3 -3
- package/dist/features/homepage/components/FeaturedResultsSection.js +2 -2
- package/dist/features/homepage/components/GoogleReviewsSection.js +6 -6
- package/dist/features/homepage/components/HeroBanner.js +1 -1
- package/dist/features/homepage/components/HeroCarousel.js +5 -4
- package/dist/features/homepage/components/HeroSection.js +1 -7
- package/dist/features/homepage/components/HomepageSkeleton.js +1 -1
- package/dist/features/homepage/components/HowItWorksInfoView.js +1 -1
- package/dist/features/homepage/components/HowItWorksSection.js +2 -2
- package/dist/features/homepage/components/NewsletterBanner.js +2 -2
- package/dist/features/homepage/components/NewsletterSection.js +1 -1
- package/dist/features/homepage/components/PromoGrid.js +1 -1
- package/dist/features/homepage/components/SectionCarousel.js +6 -6
- package/dist/features/homepage/components/SecurityHighlightsSection.js +2 -2
- package/dist/features/homepage/components/ShopByCategorySection.js +10 -10
- package/dist/features/homepage/components/SiteFeaturesSection.js +2 -2
- package/dist/features/homepage/components/SocialFeedSection.js +5 -5
- package/dist/features/homepage/components/SocialPostCard.js +1 -1
- package/dist/features/homepage/components/StatsCounterSection.js +1 -1
- package/dist/features/homepage/components/TestimonialsCarousel.js +1 -1
- package/dist/features/homepage/components/TrustBadges.js +1 -1
- package/dist/features/homepage/components/TrustFeaturesSection.js +2 -2
- package/dist/features/homepage/components/TrustIndicatorsSection.js +2 -2
- package/dist/features/homepage/components/WelcomeSection.js +2 -2
- package/dist/features/homepage/components/WhatsAppCommunitySection.js +1 -1
- package/dist/features/homepage/hooks/useActiveAd.js +2 -1
- package/dist/features/homepage/schemas/firestore.d.ts +10 -0
- package/dist/features/homepage/schemas/index.d.ts +116 -116
- package/dist/features/jobs/actions/enqueue-job.d.ts +24 -0
- package/dist/features/jobs/actions/enqueue-job.js +44 -0
- package/dist/features/jobs/repository/jobs.repository.d.ts +21 -0
- package/dist/features/jobs/repository/jobs.repository.js +48 -0
- package/dist/features/jobs/schemas/firestore.d.ts +54 -0
- package/dist/features/jobs/schemas/firestore.js +24 -0
- package/dist/features/layout/AppLayoutShell.js +17 -11
- package/dist/features/layout/AutoBreadcrumbs.js +1 -1
- package/dist/features/layout/BackToTop.js +2 -2
- package/dist/features/layout/BottomActions.js +7 -7
- package/dist/features/layout/BottomNavItem.js +2 -2
- package/dist/features/layout/BottomNavLayout.js +13 -4
- package/dist/features/layout/BottomNavbar.d.ts +9 -1
- package/dist/features/layout/BottomNavbar.js +12 -5
- package/dist/features/layout/BottomSheet.js +1 -1
- package/dist/features/layout/Breadcrumbs.js +1 -1
- package/dist/features/layout/FooterLayout.js +2 -2
- package/dist/features/layout/NavItem.js +3 -3
- package/dist/features/layout/NavbarLayout.js +6 -6
- package/dist/features/layout/SidebarLayout.d.ts +3 -0
- package/dist/features/layout/SidebarLayout.js +3 -3
- package/dist/features/layout/TitleBarLayout.js +4 -4
- package/dist/features/live/components/LiveItemFilters.js +2 -2
- package/dist/features/live/components/LiveItemsIndexListing.js +3 -3
- package/dist/features/lottery/types/index.d.ts +3 -1
- package/dist/features/lottery/types/index.js +16 -3
- package/dist/features/loyalty/components/CoinsDisplay.js +2 -2
- package/dist/features/media/MediaImage.d.ts +6 -1
- package/dist/features/media/MediaImage.js +10 -4
- package/dist/features/media/MediaLightbox.js +1 -1
- package/dist/features/media/MediaPickerModal.js +4 -3
- package/dist/features/media/MediaVideo.js +1 -1
- package/dist/features/media/modals/ImageCropModal.js +1 -1
- package/dist/features/media/modals/ImageEditor.js +1 -1
- package/dist/features/media/repository/media-assets.repository.d.ts +9 -0
- package/dist/features/media/types/index.d.ts +4 -4
- package/dist/features/media/upload/MediaUploadField.js +3 -3
- package/dist/features/media/upload/MediaUploadList.js +1 -1
- package/dist/features/messages/hooks/useConversation.js +4 -3
- package/dist/features/messages/hooks/useConversations.js +2 -1
- package/dist/features/messages/schemas/index.d.ts +6 -6
- package/dist/features/orders/components/MarketplaceOrderCard.js +1 -1
- package/dist/features/orders/components/OrderPaymentSummary.d.ts +16 -0
- package/dist/features/orders/components/OrderPaymentSummary.js +32 -0
- package/dist/features/orders/components/OrderSiblingPayments.js +1 -1
- package/dist/features/orders/components/OrdersList.d.ts +7 -2
- package/dist/features/orders/components/OrdersList.js +5 -5
- package/dist/features/orders/components/RefundHistoryTable.js +1 -1
- package/dist/features/orders/components/RefundRequestView.js +1 -1
- package/dist/features/orders/components/index.d.ts +2 -0
- package/dist/features/orders/components/index.js +1 -0
- package/dist/features/orders/repository/orders.repository.d.ts +8 -0
- package/dist/features/orders/repository/orders.repository.js +24 -0
- package/dist/features/orders/schemas/firestore.d.ts +59 -0
- package/dist/features/orders/schemas/firestore.js +5 -0
- package/dist/features/orders/schemas/index.d.ts +91 -40
- package/dist/features/orders/schemas/index.js +10 -0
- package/dist/features/orders/types/index.d.ts +15 -0
- package/dist/features/payments/schemas/index.d.ts +12 -12
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +5 -5
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
- package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +7 -7
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +3 -3
- package/dist/features/pre-orders/schemas/index.d.ts +12 -12
- package/dist/features/products/columns/productTableColumns.js +2 -2
- package/dist/features/products/components/AuctionsIndexListing.js +3 -3
- package/dist/features/products/components/CompareOverlay.js +15 -10
- package/dist/features/products/components/CustomFieldsEditor.js +2 -2
- package/dist/features/products/components/CustomSectionsEditor.js +3 -3
- package/dist/features/products/components/FeatureBadge.js +2 -2
- package/dist/features/products/components/GroupSettingsPanel.js +5 -4
- package/dist/features/products/components/MakeOfferButton.js +4 -4
- package/dist/features/products/components/MarketplaceBundleCard.js +3 -2
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +1 -1
- package/dist/features/products/components/PrizeDrawCollage.js +6 -6
- package/dist/features/products/components/PrizeDrawDetailPageView.js +5 -5
- package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +4 -4
- package/dist/features/products/components/PrizeDrawsIndexListing.js +11 -4
- package/dist/features/products/components/PrizeDrawsSection.js +1 -1
- package/dist/features/products/components/PrizeRevealModal.js +9 -4
- package/dist/features/products/components/ProductDetailActions.js +2 -1
- package/dist/features/products/components/ProductDetailPageView.js +16 -16
- package/dist/features/products/components/ProductFeaturesSelector.js +5 -5
- package/dist/features/products/components/ProductForm.js +1 -1
- package/dist/features/products/components/ProductGalleryClient.js +5 -3
- package/dist/features/products/components/ProductGrid.js +11 -11
- package/dist/features/products/components/ProductTabs.js +2 -2
- package/dist/features/products/components/ProductTabsShell.js +2 -1
- package/dist/features/products/components/ProductsIndexListing.js +6 -1
- package/dist/features/products/components/ShareButton.js +2 -1
- package/dist/features/products/components/ShowGroupSection.js +9 -5
- package/dist/features/products/components/SublistingCarouselSection.js +8 -4
- package/dist/features/products/constants/action-defs.d.ts +10 -2
- package/dist/features/products/constants/action-defs.js +20 -2
- package/dist/features/products/schemas/firestore.d.ts +5 -0
- package/dist/features/products/schemas/index.d.ts +40 -40
- package/dist/features/products/schemas/product-features.validators.d.ts +14 -14
- package/dist/features/promotions/components/CouponCard.js +13 -7
- package/dist/features/promotions/components/CouponsIndexListing.js +4 -6
- package/dist/features/promotions/components/PromotionsView.js +2 -2
- package/dist/features/promotions/schemas/index.d.ts +26 -26
- package/dist/features/reviews/components/ReviewDetailPageView.js +1 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +11 -11
- package/dist/features/reviews/components/ReviewModal.js +1 -1
- package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
- package/dist/features/reviews/components/ReviewsList.js +2 -2
- package/dist/features/reviews/schemas/index.d.ts +22 -22
- package/dist/features/scams/components/ScamAwarenessModal.js +2 -2
- package/dist/features/scams/components/ScamProfileView.js +3 -3
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/scams/schemas/index.d.ts +20 -20
- package/dist/features/search/components/Search.js +9 -9
- package/dist/features/search/components/SearchFiltersRow.js +1 -1
- package/dist/features/search/components/SearchResultsSection.js +3 -3
- package/dist/features/search/components/SearchView.js +1 -1
- package/dist/features/search/hooks/useNavSuggestions.js +2 -1
- package/dist/features/search/hooks/useSearch.js +2 -2
- package/dist/features/search/schemas/index.d.ts +3 -3
- package/dist/features/seller/components/BrandInlineSelect.js +2 -2
- package/dist/features/seller/components/CategoryInlineSelect.js +2 -2
- package/dist/features/seller/components/QuickProductForm.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +4 -4
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +3 -3
- package/dist/features/seller/components/SellerAnalyticsView.js +2 -2
- package/dist/features/seller/components/SellerArtView.js +11 -11
- package/dist/features/seller/components/SellerAuctionsView.js +11 -11
- package/dist/features/seller/components/SellerBidsView.js +3 -3
- package/dist/features/seller/components/SellerBundlesView.js +12 -12
- package/dist/features/seller/components/SellerClassifiedView.js +11 -11
- package/dist/features/seller/components/SellerCouponEditorView.js +2 -2
- package/dist/features/seller/components/SellerDigitalCodesView.js +11 -11
- package/dist/features/seller/components/SellerFeaturesView.js +4 -4
- package/dist/features/seller/components/SellerLiveView.js +11 -11
- package/dist/features/seller/components/SellerOffersPanel.js +3 -2
- package/dist/features/seller/components/SellerOrdersView.js +2 -2
- package/dist/features/seller/components/SellerPayoutMethodsView.js +1 -1
- package/dist/features/seller/components/SellerPayoutRequestView.js +6 -5
- package/dist/features/seller/components/SellerPayoutSettingsView.js +5 -4
- package/dist/features/seller/components/SellerPreOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPrizeDrawsView.js +1 -1
- package/dist/features/seller/components/SellerProductShell.js +20 -6
- package/dist/features/seller/components/SellerProductsCards.js +3 -3
- package/dist/features/seller/components/SellerProductsView.js +4 -4
- package/dist/features/seller/components/SellerReviewsView.js +7 -6
- package/dist/features/seller/components/SellerShippingView.js +2 -1
- package/dist/features/seller/components/SellerSidebar.js +5 -5
- package/dist/features/seller/components/SellerStickersView.js +11 -11
- package/dist/features/seller/components/SellerTemplatesView.js +1 -1
- package/dist/features/seller/schemas/index.d.ts +20 -20
- package/dist/features/server-errors/schemas/index.d.ts +6 -6
- package/dist/features/shell/CommandPalette.d.ts +27 -0
- package/dist/features/shell/CommandPalette.js +92 -0
- package/dist/features/shell/FormShell.js +4 -3
- package/dist/features/shell/QuickFormDrawer.js +3 -2
- package/dist/features/shell/StepForm.js +2 -2
- package/dist/features/shell/index.d.ts +2 -0
- package/dist/features/shell/index.js +1 -0
- package/dist/features/shipments/components/AdminShipmentEditorView.d.ts +7 -0
- package/dist/features/shipments/components/AdminShipmentEditorView.js +123 -0
- package/dist/features/shipments/components/AdminShipmentLotItemsView.d.ts +6 -0
- package/dist/features/shipments/components/AdminShipmentLotItemsView.js +96 -0
- package/dist/features/shipments/components/AdminShipmentProjectionsView.d.ts +9 -0
- package/dist/features/shipments/components/AdminShipmentProjectionsView.js +44 -0
- package/dist/features/shipments/components/AdminShipmentsView.d.ts +8 -0
- package/dist/features/shipments/components/AdminShipmentsView.js +82 -0
- package/dist/features/shipments/components/ShipmentItemLinkModal.d.ts +15 -0
- package/dist/features/shipments/components/ShipmentItemLinkModal.js +37 -0
- package/dist/features/shipments/hooks/useShipments.d.ts +69 -0
- package/dist/features/shipments/hooks/useShipments.js +96 -0
- package/dist/features/shipments/index.d.ts +10 -0
- package/dist/features/shipments/index.js +7 -0
- package/dist/features/shipments/manifest.d.ts +2 -0
- package/dist/features/shipments/manifest.js +26 -0
- package/dist/features/shipments/repository/shipment-items.repository.d.ts +56 -0
- package/dist/features/shipments/repository/shipment-items.repository.js +78 -0
- package/dist/features/shipments/repository/shipment-lots.repository.d.ts +48 -0
- package/dist/features/shipments/repository/shipment-lots.repository.js +55 -0
- package/dist/features/shipments/repository/shipments.repository.d.ts +50 -0
- package/dist/features/shipments/repository/shipments.repository.js +77 -0
- package/dist/features/shipments/schemas/firestore.d.ts +150 -0
- package/dist/features/shipments/schemas/firestore.js +76 -0
- package/dist/features/shipments/schemas/index.d.ts +2 -0
- package/dist/features/shipments/schemas/index.js +2 -0
- package/dist/features/shipments/schemas/validation.d.ts +350 -0
- package/dist/features/shipments/schemas/validation.js +96 -0
- package/dist/features/shipments/utils/cost-allocation.d.ts +25 -0
- package/dist/features/shipments/utils/cost-allocation.js +83 -0
- package/dist/features/site-settings/components/ActionPermissionsManager.js +2 -2
- package/dist/features/stores/components/InteractiveStoreCard.js +2 -2
- package/dist/features/stores/components/StoreAboutView.js +1 -1
- package/dist/features/stores/components/StoreAuctionsListing.js +3 -3
- package/dist/features/stores/components/StoreAuctionsView.js +1 -1
- package/dist/features/stores/components/StoreCapabilitiesGuideView.js +1 -1
- package/dist/features/stores/components/StoreClassifiedsListing.js +2 -2
- package/dist/features/stores/components/StoreDetailLayoutView.js +1 -1
- package/dist/features/stores/components/StoreDigitalCodesListing.js +2 -2
- package/dist/features/stores/components/StoreFinanceGuideView.js +2 -2
- package/dist/features/stores/components/StoreGuideHubView.js +2 -2
- package/dist/features/stores/components/StoreHeader.js +4 -4
- package/dist/features/stores/components/StoreLiveItemsListing.js +2 -2
- package/dist/features/stores/components/StoreNavTabs.js +2 -2
- package/dist/features/stores/components/StoreOrdersGuideView.js +1 -1
- package/dist/features/stores/components/StorePreOrdersListing.js +3 -3
- package/dist/features/stores/components/StoreProductsListing.js +1 -1
- package/dist/features/stores/components/StoreReviewsListing.js +2 -2
- package/dist/features/stores/components/StoreReviewsView.js +1 -1
- package/dist/features/stores/components/StoreSettingsGuideView.js +1 -1
- package/dist/features/stores/components/StoresIndexListing.js +9 -3
- package/dist/features/stores/schemas/index.d.ts +10 -10
- package/dist/features/support/schemas/index.d.ts +22 -22
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +3 -3
- package/dist/features/whatsapp-bot/components/WhatsAppChatButton.js +1 -1
- package/dist/features/wishlist/hooks/useWishlistCount.js +1 -2
- package/dist/index.d.ts +44 -1
- package/dist/index.js +37 -1
- package/dist/next/routing/route-map.d.ts +18 -0
- package/dist/next/routing/route-map.js +9 -0
- package/dist/react/hooks/useVisualViewportInset.d.ts +1 -0
- package/dist/react/hooks/useVisualViewportInset.js +59 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/dist/repositories/index.d.ts +5 -0
- package/dist/repositories/index.js +5 -0
- package/dist/schemas/registry.d.ts +398 -364
- package/dist/schemas/sieve.d.ts +2 -2
- package/dist/schemas/webhooks/google-oauth.d.ts +2 -2
- package/dist/schemas/webhooks/razorpay.d.ts +100 -100
- package/dist/seed/_helpers/media.js +1 -0
- package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
- package/dist/seed/catalogue-seed-data.d.ts +2 -0
- package/dist/seed/catalogue-seed-data.js +112 -0
- package/dist/seed/grouped-listings-seed-data.d.ts +5 -0
- package/dist/seed/homepage-sections-seed-data.js +0 -1
- package/dist/seed/index.d.ts +2 -0
- package/dist/seed/index.js +2 -0
- package/dist/seed/manifest.js +6 -0
- package/dist/seed/products-preorders-seed-data.d.ts +5 -0
- package/dist/seed/shipments-seed-data.d.ts +5 -0
- package/dist/seed/shipments-seed-data.js +185 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +5 -0
- package/dist/styles.css +75 -19
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/index.d.ts +0 -2
- package/dist/tokens/index.js +0 -2
- package/dist/tokens/tokens.css +12 -2
- package/dist/ui/DataTable.js +4 -3
- package/dist/ui/components/Button.d.ts +4 -1
- package/dist/ui/components/Button.js +11 -5
- package/dist/ui/components/Checkbox.d.ts +9 -1
- package/dist/ui/components/Checkbox.js +5 -2
- package/dist/ui/components/ClaimCouponButton.js +2 -1
- package/dist/ui/components/ConfirmDeleteModal.style.css +1 -1
- package/dist/ui/components/Drawer.style.css +13 -1
- package/dist/ui/components/Dropdown.style.css +2 -2
- package/dist/ui/components/FilterDrawer.d.ts +4 -1
- package/dist/ui/components/FilterDrawer.js +8 -2
- package/dist/ui/components/Form.d.ts +1 -1
- package/dist/ui/components/Form.js +2 -2
- package/dist/ui/components/FormActionBar.style.css +6 -1
- package/dist/ui/components/IconButton.d.ts +6 -1
- package/dist/ui/components/IconButton.js +5 -2
- package/dist/ui/components/IconButton.style.css +11 -0
- package/dist/ui/components/ImageGallery.style.css +2 -0
- package/dist/ui/components/ImageLightbox.style.css +1 -0
- package/dist/ui/components/ListingFilterDrawer.d.ts +4 -1
- package/dist/ui/components/ListingFilterDrawer.js +4 -3
- package/dist/ui/components/ListingLayout.style.css +2 -2
- package/dist/ui/components/Modal.style.css +1 -1
- package/dist/ui/components/Pagination.d.ts +5 -1
- package/dist/ui/components/Pagination.js +4 -2
- package/dist/ui/components/RowActionMenu.js +1 -1
- package/dist/ui/components/RowActionMenu.style.css +1 -1
- package/dist/ui/components/Select.d.ts +6 -1
- package/dist/ui/components/Select.js +18 -13
- package/dist/ui/components/Select.style.css +1 -1
- package/dist/ui/components/Semantic.d.ts +13 -0
- package/dist/ui/components/Semantic.js +16 -3
- package/dist/ui/components/SideDrawer.style.css +16 -4
- package/dist/ui/components/SideModal.style.css +1 -1
- package/dist/ui/components/StickyToolbar.d.ts +3 -2
- package/dist/ui/components/StickyToolbar.js +1 -1
- package/dist/ui/components/Textarea.d.ts +5 -0
- package/dist/ui/components/Textarea.js +8 -4
- package/dist/ui/components/Toast.style.css +3 -0
- package/dist/ui/components/UnsavedChangesModal.style.css +1 -1
- package/dist/ui/components/surface-tokens.d.ts +121 -113
- package/dist/ui/components/surface-tokens.js +110 -104
- package/dist/ui/forms/FormShell.d.ts +8 -27
- package/dist/ui/forms/FormShell.js +1 -194
- package/dist/ui/forms/index.d.ts +2 -2
- package/dist/ui/forms/index.js +1 -1
- package/dist/ui/index.d.ts +8 -4
- package/dist/ui/index.js +4 -2
- package/dist/utils/id-generators.d.ts +27 -0
- package/dist/utils/id-generators.js +45 -0
- package/dist/utils/number.formatter.d.ts +7 -0
- package/dist/utils/number.formatter.js +9 -0
- package/dist/validation/schemas.d.ts +5 -5
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* enqueueJob — the only way a Vercel API route may start heavy work.
|
|
3
|
+
*
|
|
4
|
+
* Writes a lightweight `jobs/{jobId}` doc and returns immediately. The
|
|
5
|
+
* `onJobCreated` Firestore trigger (a Firebase Function — see
|
|
6
|
+
* `appkit/src/_internal/server/functions/firestore.ts`) picks up the write
|
|
7
|
+
* and does the actual processing, per CLAUDE.md Rule #6 (Vercel Hobby 10s
|
|
8
|
+
* sync-function ceiling).
|
|
9
|
+
*
|
|
10
|
+
* The returned `customToken` carries a `bulkJobId` claim matching the
|
|
11
|
+
* `bulk_events/{jobId}` RTDB security rule
|
|
12
|
+
* (`appkit/firebase/base/database.rules.json`) — the caller signs in with it
|
|
13
|
+
* client-side and subscribes via `useBulkEvent({ rtdbPath: RTDB_PATHS.BULK_EVENTS })`.
|
|
14
|
+
*/
|
|
15
|
+
export interface EnqueueJobInput {
|
|
16
|
+
jobType: string;
|
|
17
|
+
payload: Record<string, unknown>;
|
|
18
|
+
requestedBy: string;
|
|
19
|
+
}
|
|
20
|
+
export interface EnqueueJobResult {
|
|
21
|
+
jobId: string;
|
|
22
|
+
customToken: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function enqueueJob(input: EnqueueJobInput): Promise<EnqueueJobResult>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* enqueueJob — the only way a Vercel API route may start heavy work.
|
|
3
|
+
*
|
|
4
|
+
* Writes a lightweight `jobs/{jobId}` doc and returns immediately. The
|
|
5
|
+
* `onJobCreated` Firestore trigger (a Firebase Function — see
|
|
6
|
+
* `appkit/src/_internal/server/functions/firestore.ts`) picks up the write
|
|
7
|
+
* and does the actual processing, per CLAUDE.md Rule #6 (Vercel Hobby 10s
|
|
8
|
+
* sync-function ceiling).
|
|
9
|
+
*
|
|
10
|
+
* The returned `customToken` carries a `bulkJobId` claim matching the
|
|
11
|
+
* `bulk_events/{jobId}` RTDB security rule
|
|
12
|
+
* (`appkit/firebase/base/database.rules.json`) — the caller signs in with it
|
|
13
|
+
* client-side and subscribes via `useBulkEvent({ rtdbPath: RTDB_PATHS.BULK_EVENTS })`.
|
|
14
|
+
*/
|
|
15
|
+
import { normalizeError } from "../../../errors/normalize";
|
|
16
|
+
import { serverLogger } from "../../../monitoring";
|
|
17
|
+
import { jobsRepository } from "../../../repositories";
|
|
18
|
+
import { JobStatusValues } from "../schemas/firestore";
|
|
19
|
+
import { getAdminAuth, getAdminRealtimeDb } from "../../../providers/db-firebase";
|
|
20
|
+
import { RTDB_PATHS } from "../../../providers/db-firebase/rtdb-paths";
|
|
21
|
+
export async function enqueueJob(input) {
|
|
22
|
+
const job = await jobsRepository.create({
|
|
23
|
+
jobType: input.jobType,
|
|
24
|
+
status: JobStatusValues.PENDING,
|
|
25
|
+
payload: input.payload,
|
|
26
|
+
requestedBy: input.requestedBy,
|
|
27
|
+
});
|
|
28
|
+
try {
|
|
29
|
+
await getAdminRealtimeDb()
|
|
30
|
+
.ref(`${RTDB_PATHS.BULK_EVENTS}/${job.id}`)
|
|
31
|
+
.set({ status: "pending", createdAt: Date.now() });
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
void normalizeError(err);
|
|
35
|
+
serverLogger.warn("enqueueJob: RTDB pending-ping failed (non-fatal)", {
|
|
36
|
+
jobId: job.id,
|
|
37
|
+
error: err instanceof Error ? err.message : String(err),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const customToken = await getAdminAuth().createCustomToken(`bulk_event_${job.id}`, {
|
|
41
|
+
bulkJobId: job.id,
|
|
42
|
+
});
|
|
43
|
+
return { jobId: job.id, customToken };
|
|
44
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JobsRepository — thin wrapper over the `jobs` collection.
|
|
3
|
+
*
|
|
4
|
+
* See `appkit/src/features/jobs/schemas/firestore.ts` for the doc shape and
|
|
5
|
+
* the async-job primitive's overall flow.
|
|
6
|
+
*/
|
|
7
|
+
import type { DocumentReference } from "firebase-admin/firestore";
|
|
8
|
+
import { BaseRepository } from "../../../providers/db-firebase";
|
|
9
|
+
import { type JobDocument, type JobResult } from "../schemas/firestore";
|
|
10
|
+
export declare class JobsRepository extends BaseRepository<JobDocument> {
|
|
11
|
+
constructor();
|
|
12
|
+
markProcessing(id: string): Promise<JobDocument>;
|
|
13
|
+
markDone(id: string, result: JobResult): Promise<JobDocument>;
|
|
14
|
+
markFailed(id: string, error: string): Promise<JobDocument>;
|
|
15
|
+
/**
|
|
16
|
+
* Refs for finished (done/failed) jobs older than `ttlDays` — used by the
|
|
17
|
+
* `cleanupRtdbEvents` sweep to prune the `jobs` collection.
|
|
18
|
+
*/
|
|
19
|
+
getStaleFinishedRefs(ttlDays?: number): Promise<DocumentReference[]>;
|
|
20
|
+
}
|
|
21
|
+
export declare const jobsRepository: JobsRepository;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JobsRepository — thin wrapper over the `jobs` collection.
|
|
3
|
+
*
|
|
4
|
+
* See `appkit/src/features/jobs/schemas/firestore.ts` for the doc shape and
|
|
5
|
+
* the async-job primitive's overall flow.
|
|
6
|
+
*/
|
|
7
|
+
import { BaseRepository } from "../../../providers/db-firebase";
|
|
8
|
+
import { JOBS_COLLECTION, JobStatusValues, } from "../schemas/firestore";
|
|
9
|
+
export class JobsRepository extends BaseRepository {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(JOBS_COLLECTION);
|
|
12
|
+
}
|
|
13
|
+
markProcessing(id) {
|
|
14
|
+
return this.update(id, {
|
|
15
|
+
status: JobStatusValues.PROCESSING,
|
|
16
|
+
startedAt: new Date(),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
markDone(id, result) {
|
|
20
|
+
return this.update(id, {
|
|
21
|
+
status: JobStatusValues.DONE,
|
|
22
|
+
result,
|
|
23
|
+
finishedAt: new Date(),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
markFailed(id, error) {
|
|
27
|
+
return this.update(id, {
|
|
28
|
+
status: JobStatusValues.FAILED,
|
|
29
|
+
error,
|
|
30
|
+
finishedAt: new Date(),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Refs for finished (done/failed) jobs older than `ttlDays` — used by the
|
|
35
|
+
* `cleanupRtdbEvents` sweep to prune the `jobs` collection.
|
|
36
|
+
*/
|
|
37
|
+
async getStaleFinishedRefs(ttlDays = 30) {
|
|
38
|
+
const cutoff = new Date();
|
|
39
|
+
cutoff.setDate(cutoff.getDate() - ttlDays);
|
|
40
|
+
const snap = await this.getCollection()
|
|
41
|
+
.where("status", "in", [JobStatusValues.DONE, JobStatusValues.FAILED])
|
|
42
|
+
.where("updatedAt", "<", cutoff)
|
|
43
|
+
.limit(500)
|
|
44
|
+
.get();
|
|
45
|
+
return snap.docs.map((d) => d.ref);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export const jobsRepository = new JobsRepository();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async Job Firestore Document Types & Constants
|
|
3
|
+
*
|
|
4
|
+
* Collection: jobs
|
|
5
|
+
* Document ID: Firestore auto-ID (no slug prefix — see CLAUDE.md "True
|
|
6
|
+
* Firestore auto-IDs" list).
|
|
7
|
+
*
|
|
8
|
+
* A `jobs/{jobId}` document is a lightweight trigger record. Vercel API
|
|
9
|
+
* routes write ONE of these and return immediately (`{ jobId, customToken }`).
|
|
10
|
+
* All actual processing happens inside the `onJobCreated` Firebase Function,
|
|
11
|
+
* which reads `jobType` to pick a runner from `JOB_RUNNERS`, executes it, and
|
|
12
|
+
* writes the final status back to both this document and the
|
|
13
|
+
* `bulk_events/{jobId}` RTDB ping channel the client is subscribed to.
|
|
14
|
+
*
|
|
15
|
+
* This is the async-job primitive for any admin action that would otherwise
|
|
16
|
+
* exceed the Vercel Hobby 10s sync-function ceiling (CLAUDE.md Rule #6).
|
|
17
|
+
*/
|
|
18
|
+
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
19
|
+
export declare const JOBS_COLLECTION = "jobs";
|
|
20
|
+
export declare const JobStatusValues: {
|
|
21
|
+
readonly PENDING: "pending";
|
|
22
|
+
readonly PROCESSING: "processing";
|
|
23
|
+
readonly DONE: "done";
|
|
24
|
+
readonly FAILED: "failed";
|
|
25
|
+
};
|
|
26
|
+
export type JobStatus = (typeof JobStatusValues)[keyof typeof JobStatusValues];
|
|
27
|
+
export interface JobResult {
|
|
28
|
+
summary: {
|
|
29
|
+
total: number;
|
|
30
|
+
succeeded: number;
|
|
31
|
+
skipped: number;
|
|
32
|
+
failed: number;
|
|
33
|
+
};
|
|
34
|
+
succeeded: string[];
|
|
35
|
+
skipped: string[];
|
|
36
|
+
failed: {
|
|
37
|
+
id: string;
|
|
38
|
+
reason: string;
|
|
39
|
+
}[];
|
|
40
|
+
data?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
export interface JobDocument extends BaseDocument {
|
|
43
|
+
/** Key into JOB_RUNNERS — e.g. "payoutsWeekly", "hardBanCascade". */
|
|
44
|
+
jobType: string;
|
|
45
|
+
status: JobStatus;
|
|
46
|
+
/** Opaque input the runner receives — shape is jobType-specific. */
|
|
47
|
+
payload: Record<string, unknown>;
|
|
48
|
+
/** uid of the admin/user who enqueued this job. */
|
|
49
|
+
requestedBy: string;
|
|
50
|
+
result?: JobResult;
|
|
51
|
+
error?: string;
|
|
52
|
+
startedAt?: Date;
|
|
53
|
+
finishedAt?: Date;
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async Job Firestore Document Types & Constants
|
|
3
|
+
*
|
|
4
|
+
* Collection: jobs
|
|
5
|
+
* Document ID: Firestore auto-ID (no slug prefix — see CLAUDE.md "True
|
|
6
|
+
* Firestore auto-IDs" list).
|
|
7
|
+
*
|
|
8
|
+
* A `jobs/{jobId}` document is a lightweight trigger record. Vercel API
|
|
9
|
+
* routes write ONE of these and return immediately (`{ jobId, customToken }`).
|
|
10
|
+
* All actual processing happens inside the `onJobCreated` Firebase Function,
|
|
11
|
+
* which reads `jobType` to pick a runner from `JOB_RUNNERS`, executes it, and
|
|
12
|
+
* writes the final status back to both this document and the
|
|
13
|
+
* `bulk_events/{jobId}` RTDB ping channel the client is subscribed to.
|
|
14
|
+
*
|
|
15
|
+
* This is the async-job primitive for any admin action that would otherwise
|
|
16
|
+
* exceed the Vercel Hobby 10s sync-function ceiling (CLAUDE.md Rule #6).
|
|
17
|
+
*/
|
|
18
|
+
export const JOBS_COLLECTION = "jobs";
|
|
19
|
+
export const JobStatusValues = {
|
|
20
|
+
PENDING: "pending",
|
|
21
|
+
PROCESSING: "processing",
|
|
22
|
+
DONE: "done",
|
|
23
|
+
FAILED: "failed",
|
|
24
|
+
};
|
|
@@ -15,6 +15,7 @@ const ROLE_DOT_BG_CLASS = {
|
|
|
15
15
|
};
|
|
16
16
|
import { useTheme } from "../../_internal/client/theme";
|
|
17
17
|
import { useAuth } from "../../react/contexts/SessionContext";
|
|
18
|
+
import { useVisualViewportInset } from "../../react/hooks/useVisualViewportInset";
|
|
18
19
|
import { isBuyerUser } from "../auth/role-predicates";
|
|
19
20
|
import { NavbarWithSettings } from "./NavbarWithSettings";
|
|
20
21
|
import { useBottomActionsContext } from "./BottomActionsContext";
|
|
@@ -28,13 +29,13 @@ import { BackToTop } from "./BackToTop";
|
|
|
28
29
|
import { useDashboardNav } from "./DashboardNavContext";
|
|
29
30
|
import { usePathname } from "next/navigation";
|
|
30
31
|
import { OVERLAY_FALLBACK_COLOR, SEED_DARK_BG as DEFAULT_DARK_BG, SEED_LIGHT_BG as DEFAULT_LIGHT_BG } from "./background-seed-defaults";
|
|
31
|
-
const CLS_STAT_BOX = "flex flex-col items-center gap-1 p-2 bg-[var(--appkit-color-surface)] rounded-lg text-center";
|
|
32
|
-
const CLS_STAT_LABEL = "text-xs text-[var(--appkit-color-text-muted)]";
|
|
33
|
-
const CLS_LOGOUT_BTN = "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm text-error transition-colors hover:bg-error-surface hover:text-error dark:text-error dark:hover:bg-error-surface dark:hover:text-error";
|
|
32
|
+
const CLS_STAT_BOX = "flex flex-col items-center gap-[var(--appkit-space-1)] p-[var(--appkit-space-2)] bg-[var(--appkit-color-surface)] rounded-lg text-center";
|
|
33
|
+
const CLS_STAT_LABEL = "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)]";
|
|
34
|
+
const CLS_LOGOUT_BTN = "flex w-full items-center justify-end gap-[var(--appkit-space-3)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-error transition-colors hover:bg-error-surface hover:text-error dark:text-error dark:hover:bg-error-surface dark:hover:text-error";
|
|
34
35
|
/** Collapsible accordion section for the public sidebar. */
|
|
35
36
|
function CollapsibleNavGroup({ title, children, }) {
|
|
36
37
|
const [open, setOpen] = useState(false);
|
|
37
|
-
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-1 py-1 text-xs font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && _jsx(Stack, { gap: "none", className: "", children: children })] }));
|
|
38
|
+
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && _jsx(Stack, { gap: "none", className: "", children: children })] }));
|
|
38
39
|
}
|
|
39
40
|
function CollapsibleSidebarSection({ section, navItemClass, }) {
|
|
40
41
|
const [open, setOpen] = useState(section.defaultOpen ?? false);
|
|
@@ -42,7 +43,7 @@ function CollapsibleSidebarSection({ section, navItemClass, }) {
|
|
|
42
43
|
if (!hasTitle) {
|
|
43
44
|
return (_jsx(Ul, { spacing: "2xs", children: section.items.map((item) => (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: navItemClass, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`))) }));
|
|
44
45
|
}
|
|
45
|
-
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-1 py-1 text-xs font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(Ul, { spacing: "2xs", children: section.items.map((item) => (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: navItemClass, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`))) }))] }));
|
|
46
|
+
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(Ul, { spacing: "2xs", children: section.items.map((item) => (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: navItemClass, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`))) }))] }));
|
|
46
47
|
}
|
|
47
48
|
/** Sidebar header when a user is logged in — avatar + display name + close button. */
|
|
48
49
|
function SidebarUserHeader({ user, onClose, }) {
|
|
@@ -54,11 +55,11 @@ function SidebarUserHeader({ user, onClose, }) {
|
|
|
54
55
|
}
|
|
55
56
|
: user.photoURL
|
|
56
57
|
? { url: user.photoURL, position: { x: 50, y: 50 }, zoom: 1 }
|
|
57
|
-
: null, size: "md", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && !isBuyerUser(user) && (_jsx(Row, { textWeight: "bold", className: `absolute -bottom-0.5 -right-0.5 w-4 h-4 border-2 border-white text-white text-[9px] leading-none select-none ${ROLE_DOT_BG_CLASS[user.role] ?? "bg-[var(--appkit-color-text-muted)]"}`, align: "center", justify: "center", rounded: "full", title: user.role.charAt(0).toUpperCase() + user.role.slice(1), "aria-label": user.role, children: user.role.charAt(0).toUpperCase() }))] }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "truncate", color: "primary", size: "sm", weight: "medium", children: user.displayName || "User" }), _jsx(Text, { className: "truncate", color: "muted", size: "xs", children: user.email || "" })] })] }), _jsx("button", { type: "button", "aria-label": "Close menu", onClick: onClose, className: "flex-shrink-0 rounded-full p-2 text-[var(--appkit-color-text-muted)] hover:bg-zinc-200 hover:text-zinc-900 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-zinc-100 transition-all hover:rotate-90", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }));
|
|
58
|
+
: null, size: "md", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && !isBuyerUser(user) && (_jsx(Row, { textWeight: "bold", className: `absolute -bottom-0.5 -right-0.5 w-4 h-4 border-2 border-white text-white text-[9px] leading-none select-none ${ROLE_DOT_BG_CLASS[user.role] ?? "bg-[var(--appkit-color-text-muted)]"}`, align: "center", justify: "center", rounded: "full", title: user.role.charAt(0).toUpperCase() + user.role.slice(1), "aria-label": user.role, children: user.role.charAt(0).toUpperCase() }))] }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "truncate", color: "primary", size: "sm", weight: "medium", children: user.displayName || "User" }), _jsx(Text, { className: "truncate", color: "muted", size: "xs", children: user.email || "" })] })] }), _jsx("button", { type: "button", "aria-label": "Close menu", onClick: onClose, className: "flex-shrink-0 rounded-full p-[var(--appkit-space-2)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-200 hover:text-zinc-900 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-zinc-100 transition-all hover:rotate-90", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }));
|
|
58
59
|
}
|
|
59
60
|
/** Sidebar header when no user is logged in — title + close button. */
|
|
60
61
|
function SidebarGuestHeader({ sidebarTitle, onClose, }) {
|
|
61
|
-
return (_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Div, { textWeight: "semibold", textSize: "sm", color: "primary", children: sidebarTitle }), _jsx("button", { type: "button", "aria-label": "Close menu", onClick: onClose, className: "rounded-full p-2 text-[var(--appkit-color-text-muted)] hover:bg-zinc-200 hover:text-zinc-900 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-zinc-100 transition-all hover:rotate-90", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }));
|
|
62
|
+
return (_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Div, { textWeight: "semibold", textSize: "sm", color: "primary", children: sidebarTitle }), _jsx("button", { type: "button", "aria-label": "Close menu", onClick: onClose, className: "rounded-full p-[var(--appkit-space-2)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-200 hover:text-zinc-900 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-zinc-100 transition-all hover:rotate-90", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }));
|
|
62
63
|
}
|
|
63
64
|
function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions, user, profileHref, userOrdersHref, userWishlistHref, userSettingsHref, adminHref, storeHref, sellerHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, theme, activeThemeName, toggleTheme, onLogout, onAfterLogout, }) {
|
|
64
65
|
const hasLegacyItems = sidebarItems.length > 0;
|
|
@@ -78,18 +79,18 @@ function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions,
|
|
|
78
79
|
storeDashboard: sidebarProfileLabels?.storeDashboard ?? sidebarProfileLabels?.sellerDashboard ?? "Store Dashboard",
|
|
79
80
|
logout: sidebarProfileLabels?.logout ?? "Logout",
|
|
80
81
|
};
|
|
81
|
-
const navItemClass = "flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-zinc-700 transition-colors hover:bg-primary-50 hover:text-primary-800 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-secondary-300";
|
|
82
|
+
const navItemClass = "flex w-full items-center justify-end gap-[var(--appkit-space-2)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-zinc-700 transition-colors hover:bg-primary-50 hover:text-primary-800 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-secondary-300";
|
|
82
83
|
const normalizedSections = hasSections
|
|
83
84
|
? sidebarSections
|
|
84
85
|
: hasLegacyItems
|
|
85
86
|
? [{ items: sidebarItems }]
|
|
86
87
|
: [];
|
|
87
88
|
return (_jsxs(Stack, { gap: "lg", children: [!isAuthenticated && sidebarPrimaryActions && sidebarPrimaryActions.length > 0 && (_jsx(Stack, { gap: "sm", children: sidebarPrimaryActions.map((action) => (_jsx(TextLink, { href: action.href, variant: "none", className: [
|
|
88
|
-
"block w-full rounded-lg px-3 py-2
|
|
89
|
+
"block w-full rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2-5)] text-center text-[length:var(--appkit-text-sm)] font-semibold transition-all duration-200 hover:scale-[1.02] shadow-sm",
|
|
89
90
|
action.variant === "outline"
|
|
90
91
|
? "border border-zinc-300 text-zinc-700 hover:bg-zinc-50 border-[var(--appkit-color-border)] text-[var(--appkit-color-text)] hover:bg-[var(--appkit-color-surface-elevated)]"
|
|
91
92
|
: "bg-primary text-white hover:bg-primary-600 dark:bg-primary dark:hover:bg-primary-600 btn-glow",
|
|
92
|
-
].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsx(CollapsibleNavGroup, { title: labels.sectionTitle, children: _jsxs(Ul, { spacing: "2xs", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: navItemClass, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: navItemClass, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: navItemClass, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: navItemClass, children: labels.settings }) }))] }) })), isAuthenticated && user?.stats && (_jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2", children: [user.stats.totalOrders != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.totalOrders }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Orders" })] })), user.stats.reviewsCount != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.reviewsCount }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Reviews" })] })), user.stats.auctionsWon != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.auctionsWon }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Auctions Won" })] })), user.stats.itemsSold != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.itemsSold }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Items Sold" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || resolvedStoreHref) && (_jsx(CollapsibleNavGroup, { title: labels.dashboardSectionTitle, children: _jsxs(Ul, { spacing: "2xs", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: navItemClass, children: labels.adminDashboard }) })), resolvedStoreHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: resolvedStoreHref, variant: "none", className: navItemClass, children: labels.storeDashboard }) }))] }) })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot || showThemeToggleInSidebar || (isAuthenticated && onLogout)) && (_jsxs(Stack, { border: "default", className: "border-t border-[var(--appkit-color-border-subtle)]", padding: "t-md", gap: "3", children: [sidebarLocaleSlot, showThemeToggleInSidebar && (_jsxs("button", { type: "button", onClick: toggleTheme, className: "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm text-[var(--appkit-color-text-muted)] transition-colors hover:bg-primary-50 hover:text-primary-800", children: [_jsx(Span, { "aria-hidden": "true", children: theme === "dark" ? "☀️" : "🌙" }), activeThemeName] })), isAuthenticated && onLogout && (_jsx("button", { type: "button", onClick: () => { onLogout(); onAfterLogout(); }, className: CLS_LOGOUT_BTN, children: labels.logout }))] }))] }));
|
|
93
|
+
].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsx(CollapsibleNavGroup, { title: labels.sectionTitle, children: _jsxs(Ul, { spacing: "2xs", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: navItemClass, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: navItemClass, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: navItemClass, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: navItemClass, children: labels.settings }) }))] }) })), isAuthenticated && user?.stats && (_jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2", children: [user.stats.totalOrders != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.totalOrders }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Orders" })] })), user.stats.reviewsCount != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.reviewsCount }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Reviews" })] })), user.stats.auctionsWon != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.auctionsWon }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Auctions Won" })] })), user.stats.itemsSold != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.itemsSold }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Items Sold" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || resolvedStoreHref) && (_jsx(CollapsibleNavGroup, { title: labels.dashboardSectionTitle, children: _jsxs(Ul, { spacing: "2xs", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: navItemClass, children: labels.adminDashboard }) })), resolvedStoreHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: resolvedStoreHref, variant: "none", className: navItemClass, children: labels.storeDashboard }) }))] }) })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot || showThemeToggleInSidebar || (isAuthenticated && onLogout)) && (_jsxs(Stack, { border: "default", className: "border-t border-[var(--appkit-color-border-subtle)]", padding: "t-md", gap: "3", children: [sidebarLocaleSlot, showThemeToggleInSidebar && (_jsxs("button", { type: "button", onClick: toggleTheme, className: "flex w-full items-center justify-end gap-[var(--appkit-space-3)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)] transition-colors hover:bg-primary-50 hover:text-primary-800", children: [_jsx(Span, { "aria-hidden": "true", children: theme === "dark" ? "☀️" : "🌙" }), activeThemeName] })), isAuthenticated && onLogout && (_jsx("button", { type: "button", onClick: () => { onLogout(); onAfterLogout(); }, className: CLS_LOGOUT_BTN, children: labels.logout }))] }))] }));
|
|
93
94
|
}
|
|
94
95
|
// ─── Main component ────────────────────────────────────────────────────────────
|
|
95
96
|
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, onTourStart, contentClassName, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
@@ -119,6 +120,11 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
119
120
|
document.documentElement.style.setProperty("--header-height", `${el.offsetHeight}px`);
|
|
120
121
|
return () => observer.disconnect();
|
|
121
122
|
}, []);
|
|
123
|
+
// Writes --keyboard-inset-height to :root so any fixed/sticky element
|
|
124
|
+
// (BottomActions, BackToTop, FormShell/FormActionBar footers) can offset
|
|
125
|
+
// itself above the on-screen keyboard via CSS calc() alone. BottomNavLayout
|
|
126
|
+
// additionally calls this hook itself for its own hide-on-keyboard branch.
|
|
127
|
+
useVisualViewportInset();
|
|
122
128
|
const handleTogglePublicSidebar = useCallback(() => {
|
|
123
129
|
setSidebarOpen((prev) => {
|
|
124
130
|
const next = !prev;
|
|
@@ -152,5 +158,5 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
152
158
|
opacity: darkBackground.overlay?.opacity ?? 0,
|
|
153
159
|
},
|
|
154
160
|
};
|
|
155
|
-
return (_jsx(_Fragment, { children: _jsxs(Stack, { className: "min-h-screen w-full overflow-x-clip transition-colors duration-300", children: [_jsx(BackgroundRenderer, { mode: theme === "dark" ? "dark" : "light", lightMode: normalizedLightBackground, darkMode: normalizedDarkBackground }), _jsxs(Div, { ref: headerRef, className: "sticky top-0 z-50 w-full", children: [_jsx(TitleBar, { onToggleSidebar: handleTogglePublicSidebar, sidebarOpen: sidebarOpen, onSearchToggle: () => setSearchOpen((prev) => !prev), searchOpen: searchOpen, brandName: brandName, brandShortName: brandShortName, siteLogoUrl: siteLogoUrl, logoHref: logoHref, promotionsHref: promotionsHref, cartHref: cartHref, wishlistHref: wishlistHref, userId: userId, profileHref: profileHref, loginHref: loginHref, registerHref: registerHref, user: user, navSlot: titleBarNavSlot, notificationSlot: titleBarNotificationSlot, devSlot: titleBarDevSlot, promoStripText: titleBarPromoStripText, isDark: theme === "dark", onToggleTheme: showThemeToggle ? toggleTheme : undefined, onTourStart: onTourStart, onBeforeToggleDashboardNav: handleBeforeDashboardNavToggle, suppressDashboardNav: suppressDashboardNav, hideSidebarToggle: hideSidebarToggle }), _jsx(NavbarWithSettings, { navItems: navItems, hiddenNavItems: hiddenNavItems, permissions: authUser?.permissions }), searchOpen && (searchSlotRenderer ? searchSlotRenderer(() => setSearchOpen(false)) : searchSlot)] }),
|
|
161
|
+
return (_jsx(_Fragment, { children: _jsxs(Stack, { className: "min-h-screen w-full overflow-x-clip transition-colors duration-300", children: [_jsx(BackgroundRenderer, { mode: theme === "dark" ? "dark" : "light", lightMode: normalizedLightBackground, darkMode: normalizedDarkBackground }), _jsxs(Div, { ref: headerRef, className: "sticky top-0 z-50 w-full", children: [_jsx(TitleBar, { onToggleSidebar: handleTogglePublicSidebar, sidebarOpen: sidebarOpen, onSearchToggle: () => setSearchOpen((prev) => !prev), searchOpen: searchOpen, brandName: brandName, brandShortName: brandShortName, siteLogoUrl: siteLogoUrl, logoHref: logoHref, promotionsHref: promotionsHref, cartHref: cartHref, wishlistHref: wishlistHref, userId: userId, profileHref: profileHref, loginHref: loginHref, registerHref: registerHref, user: user, navSlot: titleBarNavSlot, notificationSlot: titleBarNotificationSlot, devSlot: titleBarDevSlot, promoStripText: titleBarPromoStripText, isDark: theme === "dark", onToggleTheme: showThemeToggle ? toggleTheme : undefined, onTourStart: onTourStart, onBeforeToggleDashboardNav: handleBeforeDashboardNavToggle, suppressDashboardNav: suppressDashboardNav, hideSidebarToggle: hideSidebarToggle }), _jsx(NavbarWithSettings, { navItems: navItems, hiddenNavItems: hiddenNavItems, permissions: authUser?.permissions }), searchOpen && (searchSlotRenderer ? searchSlotRenderer(() => setSearchOpen(false)) : searchSlot), eventBannerSlot] }), _jsx(AutoBreadcrumbs, {}), _jsxs(Div, { layout: "flex", className: "relative w-full flex-1 overflow-x-clip", children: [_jsx(SidebarLayout, { isOpen: sidebarOpen, ariaLabel: "Secondary navigation", header: user ? (_jsx(SidebarUserHeader, { user: user, onClose: () => setSidebarOpen(false) })) : (_jsx(SidebarGuestHeader, { sidebarTitle: sidebarTitle, onClose: () => setSidebarOpen(false) })), onClose: () => setSidebarOpen(false), children: sidebarContent }), _jsx(Main, { id: "main-content", className: `w-full flex-1 flex flex-col ${hasBottomActions ? "mb-28" : "mb-16"} lg:mb-0`, children: _jsx(Div, { padding: "y-lg", className: `flex-1 ${contentClassName ?? "mx-auto w-full max-w-screen-xl px-[var(--appkit-space-5)] md:px-[var(--appkit-space-6)] lg:px-[var(--appkit-space-8)]"}`, children: children }) })] }), _jsx(BackToTop, {}), _jsx(FooterLayout, { ...footer }), _jsx(BottomActions, {}), _jsx(BottomNavbar, { user: user, userId: userId, homeHref: homeHref, shopHref: shopHref, cartHref: cartHref, wishlistHref: wishlistHref, profileHref: profileHref, loginHref: loginHref, onSearchToggle: () => setSearchOpen((prev) => !prev), navItems: navItems, onMoreToggle: hasDashboardNav ? toggleDashboardNav : handleTogglePublicSidebar }), _jsx(UnsavedChangesModal, {})] }) }));
|
|
156
162
|
}
|
|
@@ -77,7 +77,7 @@ export function AutoBreadcrumbs({ pathLabels, separator = "/", className = "", r
|
|
|
77
77
|
href: `${localePrefix}/${segments.slice(0, i + 1).join("/")}`,
|
|
78
78
|
}));
|
|
79
79
|
const allCrumbs = [{ label: "Home", href: localePrefix || "/" }, ...crumbs];
|
|
80
|
-
return (_jsx(Div, { border: "default", className: "border-y /80 backdrop-blur supports-[backdrop-filter]:bg-white/90 dark:supports-[backdrop-filter]:bg-slate-950/85", surface: "default", children: _jsx(Div, { className: "container mx-auto max-w-[1920px] px-5 sm:px-6 lg:px-8", padding: "y-xs", children: _jsx(Nav, { "aria-label": "Breadcrumb", className: className, children: _jsx(Ol, { className: "flex items-center gap-2 flex-wrap", size: "sm", children: allCrumbs.map((crumb, index) => {
|
|
80
|
+
return (_jsx(Div, { border: "default", className: "border-y /80 backdrop-blur supports-[backdrop-filter]:bg-white/90 dark:supports-[backdrop-filter]:bg-slate-950/85", surface: "default", children: _jsx(Div, { className: "container mx-auto max-w-[1920px] px-[var(--appkit-space-5)] sm:px-[var(--appkit-space-6)] lg:px-[var(--appkit-space-8)]", padding: "y-xs", children: _jsx(Nav, { "aria-label": "Breadcrumb", className: className, children: _jsx(Ol, { className: "flex items-center gap-[var(--appkit-space-2)] flex-wrap", size: "sm", children: allCrumbs.map((crumb, index) => {
|
|
81
81
|
const isLast = index === allCrumbs.length - 1;
|
|
82
82
|
return (_jsxs(Li, { layout: "flex-center", gap: "2", children: [isLast ? (_jsx(Span, { "aria-current": "page", color: "primary", children: crumb.label })) : renderLink ? (renderLink(crumb.href, crumb.label)) : (_jsx(Link, { href: crumb.href, className: "text-[var(--appkit-color-text-muted)] hover:text-zinc-950 dark:hover:text-white transition-colors", children: crumb.label })), !isLast && (_jsx(Span, { className: "text-[var(--appkit-color-text-muted)]/90 select-none", color: "muted", "aria-hidden": "true", children: separator }))] }, crumb.href));
|
|
83
83
|
}) }) }) }) }));
|
|
@@ -17,9 +17,9 @@ export function BackToTop({ threshold = 400, ariaLabel = "Back to top", classNam
|
|
|
17
17
|
}, [threshold]);
|
|
18
18
|
if (!visible)
|
|
19
19
|
return null;
|
|
20
|
-
return (_jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-label": ariaLabel, onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }), className: `fixed bottom-
|
|
20
|
+
return (_jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-label": ariaLabel, onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }), className: `fixed bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem))] right-4 z-[var(--appkit-z-bottom-nav)] flex items-center justify-center w-10 h-10 rounded-full bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] shadow-md text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface)] dark:hover:bg-slate-700 transition-colors ${className}`, children: _jsx(ChevronUp, { className: "w-5 h-5", "aria-hidden": "true" }) }));
|
|
21
21
|
}
|
|
22
22
|
/** SkipToMain — visually hidden link that becomes visible on focus for keyboard users. */
|
|
23
23
|
export function SkipToMain({ mainId = "main-content" }) {
|
|
24
|
-
return (_jsx(Link, { href: `#${mainId}`, className: "sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-[var(--appkit-z-toast)] focus:px-4 focus:py-2 focus:rounded-lg focus:bg-[var(--appkit-color-surface)] dark:focus:bg-slate-900 focus:text-zinc-900 dark:focus:text-zinc-100 focus:shadow-lg focus:ring-2 focus:ring-primary-500 focus:outline-none text-sm font-medium", children: "Skip to main content" }));
|
|
24
|
+
return (_jsx(Link, { href: `#${mainId}`, className: "sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-[var(--appkit-z-toast)] focus:px-[var(--appkit-space-4)] focus:py-[var(--appkit-space-2)] focus:rounded-lg focus:bg-[var(--appkit-color-surface)] dark:focus:bg-slate-900 focus:text-zinc-900 dark:focus:text-zinc-100 focus:shadow-lg focus:ring-2 focus:ring-primary-500 focus:outline-none text-[length:var(--appkit-text-sm)] font-medium", children: "Skip to main content" }));
|
|
25
25
|
}
|
|
@@ -34,7 +34,7 @@ import { useClickOutside } from "../../react";
|
|
|
34
34
|
import { Div, Row, Span, Text, Button } from "../../ui";
|
|
35
35
|
// Token values inlined from @mohasinac/appkit/tokens
|
|
36
36
|
const BOTTOM_NAV_BG = "bg-[color-mix(in_srgb,var(--appkit-color-bg)_90%,transparent)] backdrop-blur-md border-t border-[var(--appkit-color-border)]";
|
|
37
|
-
const Z_BOTTOM_ACTIONS = "z-
|
|
37
|
+
const Z_BOTTOM_ACTIONS = "z-[var(--appkit-z-bottom-nav)]";
|
|
38
38
|
const BOTTOM_NAV_HEIGHT = "h-14";
|
|
39
39
|
const FLEX_CENTER = "flex items-center justify-center";
|
|
40
40
|
const CLS_COUNT_BADGE = "bg-error-surface text-white";
|
|
@@ -52,7 +52,7 @@ function BulkPickerPanel({ bulkActions, selectedActionId, pickerOpen, onSelect,
|
|
|
52
52
|
.join(" "), children: bulkActions.map((action, i) => {
|
|
53
53
|
const isSelected = action.id === selectedActionId;
|
|
54
54
|
return (_jsxs(Button, { role: "option", "aria-selected": isSelected, type: "button", variant: "ghost", disabled: action.disabled || action.loading, onClick: () => onSelect(action.id), className: [
|
|
55
|
-
"w-full flex items-center gap-3 px-5 py-3
|
|
55
|
+
"w-full flex items-center gap-[var(--appkit-space-3)] px-[var(--appkit-space-5)] py-[var(--appkit-space-3-5)] text-left text-[length:var(--appkit-text-sm)] font-medium transition-colors rounded-none",
|
|
56
56
|
i > 0 ? "border-t border-zinc-100/80 border-[var(--appkit-color-border-subtle)]" : "",
|
|
57
57
|
isSelected ? "bg-zinc-50 bg-[var(--appkit-color-surface-input)]" : "",
|
|
58
58
|
action.variant === "danger"
|
|
@@ -73,10 +73,10 @@ function PageActionsRow({ pageActions, dispatchAction, }) {
|
|
|
73
73
|
return (_jsxs(Button, { type: "button", variant: action.variant ?? "primary", size: "sm", isLoading: action.loading, disabled: action.disabled, onClick: () => dispatchAction(action.id), className: [
|
|
74
74
|
"h-10 relative",
|
|
75
75
|
growClass,
|
|
76
|
-
isIconOnly ? "px-0 justify-center" : "",
|
|
76
|
+
isIconOnly ? "px-[var(--appkit-space-0)] justify-center" : "",
|
|
77
77
|
]
|
|
78
78
|
.filter(Boolean)
|
|
79
|
-
.join(" "), children: [action.icon && (_jsx(Span, { className: ["flex-shrink-0", action.label ? "mr-1.5" : ""].join(" "), "aria-hidden": "true", children: action.icon })), action.label && _jsx(Span, { className: "truncate leading-none", children: action.label }), action.badge !== undefined && (_jsx(Span, { className: `absolute -top-1.5 -right-1.5 min-w-[18px] h-[18px] ${CLS_COUNT_BADGE} text-[10px] ${FLEX_CENTER} px-1 pointer-events-none select-none`, rounded: "full", weight: "bold", "aria-hidden": "true", children: action.badge }))] }, action.id));
|
|
79
|
+
.join(" "), children: [action.icon && (_jsx(Span, { className: ["flex-shrink-0", action.label ? "mr-1.5" : ""].join(" "), "aria-hidden": "true", children: action.icon })), action.label && _jsx(Span, { className: "truncate leading-none", children: action.label }), action.badge !== undefined && (_jsx(Span, { className: `absolute -top-1.5 -right-1.5 min-w-[18px] h-[18px] ${CLS_COUNT_BADGE} text-[10px] ${FLEX_CENTER} px-[var(--appkit-space-1)] pointer-events-none select-none`, rounded: "full", weight: "bold", "aria-hidden": "true", children: action.badge }))] }, action.id));
|
|
80
80
|
}) }));
|
|
81
81
|
}
|
|
82
82
|
export default function BottomActions() {
|
|
@@ -119,7 +119,7 @@ export default function BottomActions() {
|
|
|
119
119
|
setPickerOpen(false);
|
|
120
120
|
};
|
|
121
121
|
return (_jsxs(Div, { ref: containerRef, role: "toolbar", "aria-label": isBulkMode ? "Bulk actions" : "Page actions", "aria-hidden": !isVisible, className: [
|
|
122
|
-
"fixed bottom-
|
|
122
|
+
"fixed bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem))] left-0 right-0 lg:hidden",
|
|
123
123
|
Z_BOTTOM_ACTIONS,
|
|
124
124
|
BOTTOM_NAV_BG,
|
|
125
125
|
"shadow-[0_-4px_20px_rgba(0,0,0,0.08)] dark:shadow-[0_-4px_20px_rgba(0,0,0,0.30)]",
|
|
@@ -127,10 +127,10 @@ export default function BottomActions() {
|
|
|
127
127
|
isVisible ? "translate-y-0" : "translate-y-full pointer-events-none",
|
|
128
128
|
]
|
|
129
129
|
.filter(Boolean)
|
|
130
|
-
.join(" "), "data-section": "bottomactions-div-401", children: [isBulkMode && (_jsx(BulkPickerPanel, { bulkActions: bulkActions, selectedActionId: selectedActionId, pickerOpen: pickerOpen, onSelect: (id) => { setSelectedActionId(id); setPickerOpen(false); } })), isBulkMode && (_jsx(Div, { className: "h-[3px] w-full bg-[image:var(--appkit-gradient-brand-tri)]" })), infoLabel && !isBulkMode && (_jsx(Div, { border: "subtle", className: "pt-2 pb-0 border-b /80", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }) })), _jsx(Row, { className: `${BOTTOM_NAV_HEIGHT}`, gap: "sm", padding: "x-sm", children: isBulkMode && bulk ? (_jsxs(_Fragment, { children: [_jsxs(Button, { rounded: "full", gap: "xs", type: "button", variant: "ghost", onClick: dispatchBulkClear, className: "inline- flex-shrink-0 bg-primary-50 hover:bg-primary-100 active:bg-primary-200 dark:bg-primary-950/30 dark:hover:bg-primary-900/50 text-primary-700 dark:text-primary-300 pl-2 pr-3 h-8 border border-primary-200/70 dark:border-primary-800/50 transition-colors min-h-0", "aria-label": "Clear selection", children: [_jsx(X, { className: "w-3.5 h-3.5 flex-shrink-0", "aria-hidden": "true" }), _jsx(Span, { size: "xs", weight: "semibold", className: "tabular-nums whitespace-nowrap leading-none", children: bulk.noun
|
|
130
|
+
.join(" "), "data-section": "bottomactions-div-401", children: [isBulkMode && (_jsx(BulkPickerPanel, { bulkActions: bulkActions, selectedActionId: selectedActionId, pickerOpen: pickerOpen, onSelect: (id) => { setSelectedActionId(id); setPickerOpen(false); } })), isBulkMode && (_jsx(Div, { className: "h-[3px] w-full bg-[image:var(--appkit-gradient-brand-tri)]" })), infoLabel && !isBulkMode && (_jsx(Div, { border: "subtle", className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b /80", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }) })), _jsx(Row, { className: `${BOTTOM_NAV_HEIGHT}`, gap: "sm", padding: "x-sm", children: isBulkMode && bulk ? (_jsxs(_Fragment, { children: [_jsxs(Button, { rounded: "full", gap: "xs", type: "button", variant: "ghost", onClick: dispatchBulkClear, className: "inline- flex-shrink-0 bg-primary-50 hover:bg-primary-100 active:bg-primary-200 dark:bg-primary-950/30 dark:hover:bg-primary-900/50 text-primary-700 dark:text-primary-300 pl-2 pr-3 h-8 border border-primary-200/70 dark:border-primary-800/50 transition-colors min-h-0", "aria-label": "Clear selection", children: [_jsx(X, { className: "w-3.5 h-3.5 flex-shrink-0", "aria-hidden": "true" }), _jsx(Span, { size: "xs", weight: "semibold", className: "tabular-nums whitespace-nowrap leading-none", children: bulk.noun
|
|
131
131
|
? `${bulk.selectedCount} ${bulk.noun}`
|
|
132
132
|
: `${bulk.selectedCount} selected` })] }), bulkActions.length > 0 && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setPickerOpen((o) => !o), "aria-haspopup": "listbox", "aria-expanded": pickerOpen, className: [
|
|
133
|
-
"flex-1 min-w-0 h-10 flex items-center gap-2 px-3 rounded-lg border text-sm font-medium transition-colors",
|
|
133
|
+
"flex-1 min-w-0 h-10 flex items-center gap-[var(--appkit-space-2)] px-[var(--appkit-space-3)] rounded-lg border text-[length:var(--appkit-text-sm)] font-medium transition-colors",
|
|
134
134
|
"bg-zinc-50 hover:bg-[var(--appkit-color-surface)] active:bg-zinc-200 bg-[var(--appkit-color-surface-input)] dark:hover:bg-slate-700/60",
|
|
135
135
|
"border-[var(--appkit-color-border)]",
|
|
136
136
|
selectedAction?.variant === "danger"
|
|
@@ -21,7 +21,7 @@ import { Li, Span } from "../../ui";
|
|
|
21
21
|
export function BottomNavItem({ href, label, icon, badge, isActive = false, className, }) {
|
|
22
22
|
// Use a plain anchor so this component stays framework-agnostic.
|
|
23
23
|
// Consumer projects may swap for next/link if preferred.
|
|
24
|
-
return (_jsx(Li, { className: "flex-1", children: _jsxs("a", { href: href, "aria-label": label, "aria-current": isActive ? "page" : undefined, className: `relative flex flex-col items-center justify-center gap-0
|
|
24
|
+
return (_jsx(Li, { className: "flex-1", children: _jsxs("a", { href: href, "aria-label": label, "aria-current": isActive ? "page" : undefined, className: `relative flex flex-col items-center justify-center gap-[var(--appkit-space-0-5)] h-full w-full min-h-[44px] transition-colors ${isActive
|
|
25
25
|
? "text-primary-600 dark:text-secondary-400"
|
|
26
|
-
: "text-[var(--appkit-color-text-muted)] hover:text-zinc-700 hover:text-[var(--appkit-color-text-muted)]"}${className ? ` ${className}` : ""}`, children: [_jsxs(Span, { className: "relative", children: [icon, badge != null && badge > 0 && (_jsx(Span, { layout: "flex-center", color: "inverse", "aria-label": `${badge} items`, className: "absolute -top-1.5 -right-1.5 min-w-[16px] h-4 px-1 text-[10px] bg-secondary-500 leading-none", rounded: "full", weight: "bold", children: badge > 99 ? "99+" : badge }))] }), _jsx(Span, { weight: "medium", className: "text-[10px] leading-none", children: label })] }) }));
|
|
26
|
+
: "text-[var(--appkit-color-text-muted)] hover:text-zinc-700 hover:text-[var(--appkit-color-text-muted)]"}${className ? ` ${className}` : ""}`, children: [_jsxs(Span, { className: "relative", children: [icon, badge != null && badge > 0 && (_jsx(Span, { layout: "flex-center", color: "inverse", "aria-label": `${badge} items`, className: "absolute -top-1.5 -right-1.5 min-w-[16px] h-4 px-[var(--appkit-space-1)] text-[10px] bg-secondary-500 leading-none", rounded: "full", weight: "bold", children: badge > 99 ? "99+" : badge }))] }), _jsx(Span, { weight: "medium", className: "text-[10px] leading-none", children: label })] }) }));
|
|
27
27
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect } from "react";
|
|
4
4
|
import { Nav, Ul } from "../../ui";
|
|
5
|
+
import { useVisualViewportInset } from "../../react/hooks/useVisualViewportInset";
|
|
5
6
|
/**
|
|
6
7
|
* BottomNavLayout — generic fixed-bottom navigation shell.
|
|
7
8
|
*
|
|
@@ -14,12 +15,20 @@ import { Nav, Ul } from "../../ui";
|
|
|
14
15
|
* themselves above the navbar so they don't get clipped.
|
|
15
16
|
*/
|
|
16
17
|
export function BottomNavLayout({ ariaLabel, children, id = "bottom-navbar", className, }) {
|
|
18
|
+
const isKeyboardOpen = useVisualViewportInset();
|
|
17
19
|
useEffect(() => {
|
|
18
20
|
const root = document.documentElement;
|
|
19
21
|
const mql = typeof window !== "undefined" ? window.matchMedia("(max-width: 1023.98px)") : null;
|
|
20
22
|
const sync = () => {
|
|
21
|
-
// Only expose the var when the navbar is actually rendered (below lg
|
|
22
|
-
|
|
23
|
+
// Only expose the var when the navbar is actually rendered (below lg
|
|
24
|
+
// and the on-screen keyboard isn't covering it — the bar hides itself
|
|
25
|
+
// in that case, see className below).
|
|
26
|
+
// Includes the safe-area inset since the nav's own padding-bottom
|
|
27
|
+
// (env(safe-area-inset-bottom) below) adds to its real rendered
|
|
28
|
+
// height — omitting it here would under-report the reserved space to
|
|
29
|
+
// every sibling that reads this var (BottomActions, ListingLayout's
|
|
30
|
+
// mobile pagination bar, SideDrawer/Drawer footers, etc.).
|
|
31
|
+
root.style.setProperty("--bottom-nav-height", mql && mql.matches && !isKeyboardOpen ? "calc(4rem + env(safe-area-inset-bottom, 0px))" : "0px");
|
|
23
32
|
};
|
|
24
33
|
sync();
|
|
25
34
|
mql?.addEventListener("change", sync);
|
|
@@ -27,6 +36,6 @@ export function BottomNavLayout({ ariaLabel, children, id = "bottom-navbar", cla
|
|
|
27
36
|
mql?.removeEventListener("change", sync);
|
|
28
37
|
root.style.setProperty("--bottom-nav-height", "0px");
|
|
29
38
|
};
|
|
30
|
-
}, []);
|
|
31
|
-
return (_jsx(Nav, { id: id, "aria-label": ariaLabel, className: `fixed bottom-0 left-0 right-0 lg:hidden z-
|
|
39
|
+
}, [isKeyboardOpen]);
|
|
40
|
+
return (_jsx(Nav, { id: id, "aria-label": ariaLabel, "aria-hidden": isKeyboardOpen, className: `fixed bottom-0 left-0 right-0 lg:hidden z-[var(--appkit-z-bottom-nav)] bg-[color-mix(in_srgb,var(--appkit-color-bg)_90%,transparent)] backdrop-blur-md border-t border-[var(--appkit-color-border)] shadow-2xl pb-[env(safe-area-inset-bottom,0px)] transition-transform duration-200 ease-out ${isKeyboardOpen ? "translate-y-full pointer-events-none" : "translate-y-0"}${className ? ` ${className}` : ""}`, children: _jsx(Ul, { className: "flex items-stretch h-16", children: children }) }));
|
|
32
41
|
}
|
|
@@ -29,12 +29,20 @@ export interface BottomNavbarUser {
|
|
|
29
29
|
export interface BottomNavbarProps {
|
|
30
30
|
/** Authenticated user � if provided, shows profile slot with avatar */
|
|
31
31
|
user?: BottomNavbarUser | null;
|
|
32
|
+
/** Authenticated user's uid — drives the wishlist count badge on the wishlist slot. */
|
|
33
|
+
userId?: string | null;
|
|
32
34
|
/** href for the Home nav item */
|
|
33
35
|
homeHref: string;
|
|
34
36
|
/** href for the Shop/Products nav item */
|
|
35
37
|
shopHref: string;
|
|
36
38
|
/** href for the Cart link */
|
|
37
39
|
cartHref: string;
|
|
40
|
+
/**
|
|
41
|
+
* href for the Wishlist page. When provided (and a `user` is signed in),
|
|
42
|
+
* renders a 6th slot between Cart and Profile so mobile has the same
|
|
43
|
+
* 1-click wishlist access desktop's TitleBar already has.
|
|
44
|
+
*/
|
|
45
|
+
wishlistHref?: string;
|
|
38
46
|
/** href for authenticated user Profile page */
|
|
39
47
|
profileHref: string;
|
|
40
48
|
/** href for the Login page (shown when unauthenticated) */
|
|
@@ -64,4 +72,4 @@ export interface BottomNavbarProps {
|
|
|
64
72
|
*
|
|
65
73
|
* Pass `user` from your auth context to render the profile slot with avatar.
|
|
66
74
|
*/
|
|
67
|
-
export declare function BottomNavbar({ user, homeHref, shopHref, cartHref, profileHref, loginHref, onSearchToggle, navItems, onMoreToggle, activeClassName, inactiveClassName, iconClassName, labelClassName, getRoleBadgeClass, }: BottomNavbarProps): React.JSX.Element;
|
|
75
|
+
export declare function BottomNavbar({ user, userId, homeHref, shopHref, cartHref, wishlistHref, profileHref, loginHref, onSearchToggle, navItems, onMoreToggle, activeClassName, inactiveClassName, iconClassName, labelClassName, getRoleBadgeClass, }: BottomNavbarProps): React.JSX.Element;
|
|
@@ -4,28 +4,35 @@ import { usePathname } from "next/navigation";
|
|
|
4
4
|
import { AvatarDisplay, Li, Span, TextLink } from "../../ui";
|
|
5
5
|
import { BottomNavLayout } from "./BottomNavLayout";
|
|
6
6
|
import { NavItem } from "./NavItem";
|
|
7
|
+
import { useWishlistCount } from "../wishlist/hooks/useWishlistCount";
|
|
7
8
|
/**
|
|
8
9
|
* BottomNavbar � mobile bottom navigation bar (5 slots: home, shop, search, cart, profile).
|
|
9
10
|
*
|
|
10
11
|
* Pass `user` from your auth context to render the profile slot with avatar.
|
|
11
12
|
*/
|
|
12
|
-
export function BottomNavbar({ user, homeHref, shopHref, cartHref, profileHref, loginHref, onSearchToggle, navItems, onMoreToggle, activeClassName = "text-primary-600 dark:text-primary-400", inactiveClassName = "text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]", iconClassName, labelClassName = "text-[11px] leading-tight font-medium", getRoleBadgeClass, }) {
|
|
13
|
+
export function BottomNavbar({ user, userId, homeHref, shopHref, cartHref, wishlistHref, profileHref, loginHref, onSearchToggle, navItems, onMoreToggle, activeClassName = "text-primary-600 dark:text-primary-400", inactiveClassName = "text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]", iconClassName, labelClassName = "text-[11px] leading-tight font-medium", getRoleBadgeClass, }) {
|
|
13
14
|
const pathname = usePathname();
|
|
15
|
+
const wishlistCount = useWishlistCount(userId);
|
|
14
16
|
const labels = {
|
|
15
17
|
mobileNav: "Mobile navigation",
|
|
16
18
|
home: "Home",
|
|
17
19
|
products: "Products",
|
|
18
20
|
search: "Search",
|
|
21
|
+
wishlist: "Wishlist",
|
|
19
22
|
cart: "Cart",
|
|
20
23
|
profile: "Profile",
|
|
21
24
|
more: "More",
|
|
22
25
|
};
|
|
23
|
-
const slotClassName = "relative flex h-full w-full flex-col items-
|
|
26
|
+
const slotClassName = "relative flex h-full w-full flex-col items-end justify-center gap-[var(--appkit-space-1)] pr-[var(--appkit-space-1)] text-right transition-colors duration-200";
|
|
24
27
|
const defaultIcon = (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h16" }) }));
|
|
25
28
|
if (navItems && navItems.length > 0) {
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
// When a wishlist href is available, trade one nav-item slot for a
|
|
30
|
+
// dedicated wishlist slot so mobile has the same 1-click wishlist
|
|
31
|
+
// access desktop's TitleBar already has (previously: More → sidebar →
|
|
32
|
+
// Shopping group, 2 taps).
|
|
33
|
+
const visibleItems = navItems.slice(0, wishlistHref ? 3 : 4);
|
|
34
|
+
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [visibleItems.map((item) => (_jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: item.href, label: item.label, icon: item.icon ?? defaultIcon, isActive: pathname === item.href || (item.href !== "/" && pathname.startsWith(item.href + "/")), variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }, item.key))), wishlistHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: wishlistHref, variant: "none", className: `${slotClassName} ${pathname === wishlistHref ? activeClassName : inactiveClassName}`, "aria-label": wishlistCount > 0 ? `${labels.wishlist}, ${wishlistCount} items` : labels.wishlist, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4.318 6.318a4.5 4.5 0 0 0 0 6.364L12 20.364l7.682-7.682a4.5 4.5 0 0 0-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 0 0-6.364 0z" }) }), wishlistCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: "absolute -right-2 -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-[var(--appkit-color-error)] px-[var(--appkit-space-1)] leading-none text-[var(--appkit-color-text-on-primary)]", children: wishlistCount > 9 ? "9+" : wishlistCount }))] }), _jsx(Span, { className: labelClassName, children: labels.wishlist })] }) })), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onMoreToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.more, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h16" }) }), _jsx(Span, { className: labelClassName, children: labels.more })] }) })] }));
|
|
28
35
|
}
|
|
29
|
-
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [_jsx(Li, { className: "flex-1 w-1/5", children: _jsx(NavItem, { href: homeHref, label: labels.home, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), isActive: pathname === homeHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: shopHref, label: labels.products, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) }), isActive: pathname === shopHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onSearchToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.search, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.search })] }) }), _jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": labels.cart, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) }), _jsx(Li, { className: "flex-1", children: user ? (_jsxs(TextLink, { href: profileHref, variant: "none", className: `${slotClassName} ${pathname === profileHref ? activeClassName : inactiveClassName}`, "aria-label": labels.profile, children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata ||
|
|
36
|
+
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [_jsx(Li, { className: "flex-1 w-1/5", children: _jsx(NavItem, { href: homeHref, label: labels.home, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), isActive: pathname === homeHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: shopHref, label: labels.products, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) }), isActive: pathname === shopHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onSearchToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.search, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.search })] }) }), _jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": labels.cart, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) }), wishlistHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: wishlistHref, variant: "none", className: `${slotClassName} ${pathname === wishlistHref ? activeClassName : inactiveClassName}`, "aria-label": wishlistCount > 0 ? `${labels.wishlist}, ${wishlistCount} items` : labels.wishlist, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4.318 6.318a4.5 4.5 0 0 0 0 6.364L12 20.364l7.682-7.682a4.5 4.5 0 0 0-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 0 0-6.364 0z" }) }), wishlistCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: "absolute -right-2 -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-[var(--appkit-color-error)] px-[var(--appkit-space-1)] leading-none text-[var(--appkit-color-text-on-primary)]", children: wishlistCount > 9 ? "9+" : wishlistCount }))] }), _jsx(Span, { className: labelClassName, children: labels.wishlist })] }) })), _jsx(Li, { className: "flex-1", children: user ? (_jsxs(TextLink, { href: profileHref, variant: "none", className: `${slotClassName} ${pathname === profileHref ? activeClassName : inactiveClassName}`, "aria-label": labels.profile, children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata ||
|
|
30
37
|
(user.photoURL ? { url: user.photoURL, position: { x: 50, y: 50 }, zoom: 1 } : null), size: "sm", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && (_jsx(Span, { className: `text-[7px] uppercase leading-none ${getRoleBadgeClass ? getRoleBadgeClass(user.role) : "text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]"}`, weight: "semibold", children: user.role }))] })) : (_jsx(NavItem, { href: loginHref, label: labels.profile, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }), isActive: pathname === loginHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName })) })] }));
|
|
31
38
|
}
|