@mohasinac/appkit 2.7.27 → 2.7.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
- package/dist/_internal/server/features/checkout/actions.js +42 -35
- package/dist/_internal/server/features/classified/adapters.d.ts +21 -0
- package/dist/_internal/server/features/classified/adapters.js +24 -0
- package/dist/_internal/server/features/classified/index.d.ts +3 -0
- package/dist/_internal/server/features/classified/index.js +3 -0
- package/dist/_internal/server/features/classified/metadata.d.ts +8 -0
- package/dist/_internal/server/features/classified/metadata.js +43 -0
- package/dist/_internal/server/features/classified/og.d.ts +30 -0
- package/dist/_internal/server/features/classified/og.js +76 -0
- package/dist/_internal/server/features/digital-code/adapters.d.ts +21 -0
- package/dist/_internal/server/features/digital-code/adapters.js +29 -0
- package/dist/_internal/server/features/digital-code/index.d.ts +3 -0
- package/dist/_internal/server/features/digital-code/index.js +3 -0
- package/dist/_internal/server/features/digital-code/metadata.d.ts +8 -0
- package/dist/_internal/server/features/digital-code/metadata.js +36 -0
- package/dist/_internal/server/features/digital-code/og.d.ts +27 -0
- package/dist/_internal/server/features/digital-code/og.js +77 -0
- package/dist/_internal/server/features/live/adapters.d.ts +21 -0
- package/dist/_internal/server/features/live/adapters.js +34 -0
- package/dist/_internal/server/features/live/index.d.ts +3 -0
- package/dist/_internal/server/features/live/index.js +3 -0
- package/dist/_internal/server/features/live/metadata.d.ts +8 -0
- package/dist/_internal/server/features/live/metadata.js +39 -0
- package/dist/_internal/server/features/live/og.d.ts +27 -0
- package/dist/_internal/server/features/live/og.js +72 -0
- package/dist/_internal/server/features/promotions/actions.js +4 -3
- package/dist/_internal/server/jobs/core/onOrderCreate.js +19 -16
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +25 -22
- package/dist/_internal/server/jobs/core/onProductWrite.js +48 -58
- package/dist/_internal/shared/actions/action-registry.js +310 -8
- package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
- package/dist/_internal/shared/features/brands/schema.d.ts +5 -5
- package/dist/_internal/shared/features/cart/schema.d.ts +18 -18
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +38 -38
- package/dist/_internal/shared/features/events/schema.d.ts +8 -8
- package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
- package/dist/_internal/shared/features/products/schema.d.ts +42 -42
- package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
- package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
- package/dist/_internal/shared/media/limits.js +4 -3
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/configs/next.js +4 -0
- package/dist/constants/table-keys.d.ts +6 -0
- package/dist/constants/table-keys.js +9 -0
- package/dist/core/hooks/useSyncManager.js +9 -8
- package/dist/features/about/components/FeesView.js +10 -7
- package/dist/features/about/components/HelpPageView.js +10 -1
- package/dist/features/about/components/HowAuctionsWorkView.js +10 -1
- package/dist/features/about/components/HowCheckoutWorksView.js +10 -1
- package/dist/features/about/components/HowOffersWorkView.js +10 -1
- package/dist/features/about/components/HowOrdersWorkView.js +10 -1
- package/dist/features/about/components/HowPayoutsWorkView.js +10 -1
- package/dist/features/about/components/HowPreOrdersWorkView.js +10 -1
- package/dist/features/about/components/HowReviewsWorkView.js +10 -1
- package/dist/features/about/components/PublicProfileView.js +44 -14
- package/dist/features/about/components/SecurityPrivacyView.js +7 -1
- package/dist/features/account/components/AddressesIndexListing.js +2 -2
- package/dist/features/account/components/NotificationBell.js +27 -16
- package/dist/features/account/components/NotificationPreferencesPanel.d.ts +9 -0
- package/dist/features/account/components/NotificationPreferencesPanel.js +91 -0
- package/dist/features/account/components/UserSidebar.js +4 -4
- package/dist/features/account/components/UserSupportView.js +16 -6
- package/dist/features/account/components/index.d.ts +2 -0
- package/dist/features/account/components/index.js +1 -0
- package/dist/features/account/schemas/index.d.ts +4 -4
- package/dist/features/account/types/index.d.ts +34 -0
- package/dist/features/admin/actions/admin-actions.js +4 -3
- package/dist/features/admin/actions/notification-actions.d.ts +24 -22
- package/dist/features/admin/actions/notification-actions.js +122 -22
- package/dist/features/admin/components/AdminAdEditorView.js +29 -15
- package/dist/features/admin/components/AdminAdsView.js +22 -28
- package/dist/features/admin/components/AdminAllEventEntriesView.js +6 -1
- package/dist/features/admin/components/AdminBidsView.js +10 -3
- package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
- package/dist/features/admin/components/AdminBlogView.js +8 -3
- package/dist/features/admin/components/AdminBrandsView.js +7 -2
- package/dist/features/admin/components/AdminBundleEditorView.js +5 -2
- package/dist/features/admin/components/AdminCarouselEditorView.js +6 -3
- package/dist/features/admin/components/AdminCarouselView.js +13 -7
- package/dist/features/admin/components/AdminCategoriesView.js +8 -3
- package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
- package/dist/features/admin/components/AdminContactView.js +6 -1
- package/dist/features/admin/components/AdminCouponEditorView.js +8 -2
- package/dist/features/admin/components/AdminCouponsView.js +6 -1
- package/dist/features/admin/components/AdminEmployeeEditorView.js +12 -9
- package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
- package/dist/features/admin/components/AdminFaqsView.js +7 -2
- package/dist/features/admin/components/AdminFeaturesView.js +4 -3
- package/dist/features/admin/components/AdminMediaView.js +9 -8
- package/dist/features/admin/components/AdminNavigationView.js +10 -17
- package/dist/features/admin/components/AdminNewsletterView.js +6 -1
- package/dist/features/admin/components/AdminNotificationsView.js +11 -3
- package/dist/features/admin/components/AdminOrdersView.js +6 -1
- package/dist/features/admin/components/AdminPayoutsView.js +10 -3
- package/dist/features/admin/components/AdminPrizeDrawsView.js +10 -3
- package/dist/features/admin/components/AdminProductsView.js +18 -12
- package/dist/features/admin/components/AdminReviewsView.js +33 -24
- package/dist/features/admin/components/AdminScammerEditorView.js +3 -2
- package/dist/features/admin/components/AdminScammersView.js +28 -26
- package/dist/features/admin/components/AdminSectionsView.js +112 -99
- package/dist/features/admin/components/AdminSessionsView.js +10 -3
- package/dist/features/admin/components/AdminSidebar.js +4 -4
- package/dist/features/admin/components/AdminSiteSettingsView.js +45 -4
- package/dist/features/admin/components/AdminStoresView.js +6 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +0 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +32 -34
- package/dist/features/admin/components/AdminTeamView.js +6 -1
- package/dist/features/admin/components/AdminUserEditorView.js +8 -15
- package/dist/features/admin/components/AdminUsersView.js +6 -1
- package/dist/features/admin/components/AdminViewCards.js +4 -3
- package/dist/features/admin/components/DashboardStats.js +2 -2
- package/dist/features/admin/components/DataTable.js +2 -2
- package/dist/features/admin/components/sections/adminSectionsTypes.js +4 -3
- package/dist/features/admin/repository/chat.repository.js +8 -8
- package/dist/features/admin/repository/site-settings.repository.d.ts +1 -0
- package/dist/features/admin/repository/site-settings.repository.js +11 -9
- package/dist/features/admin/schemas/firestore.d.ts +38 -0
- package/dist/features/admin/schemas/firestore.js +15 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +13 -19
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +18 -9
- package/dist/features/auctions/components/MarketplaceAuctionGrid.js +3 -2
- package/dist/features/auctions/components/index.d.ts +0 -1
- package/dist/features/auctions/components/index.js +0 -1
- package/dist/features/auctions/schemas/index.d.ts +14 -14
- package/dist/features/auth/actions/realtime-token-actions.js +19 -4
- package/dist/features/auth/components/RegisterForm.js +8 -4
- package/dist/features/auth/consent-otp.d.ts +2 -0
- package/dist/features/auth/consent-otp.js +8 -0
- package/dist/features/auth/schemas/firestore.d.ts +2 -0
- package/dist/features/auth/schemas/firestore.js +18 -25
- package/dist/features/auth/schemas/index.d.ts +2 -2
- package/dist/features/before-after/api/[id]/route.js +8 -6
- package/dist/features/before-after/schemas/index.d.ts +2 -2
- package/dist/features/blog/actions/blog-actions.d.ts +3 -3
- package/dist/features/blog/components/BlogIndexListing.js +21 -2
- package/dist/features/blog/components/BlogPostForm.js +11 -5
- package/dist/features/blog/components/BlogPostView.js +10 -2
- package/dist/features/blog/schemas/index.d.ts +18 -18
- package/dist/features/cart/components/CartDrawer.js +3 -2
- package/dist/features/cart/repository/cart.repository.js +4 -3
- package/dist/features/cart/utils/pending-ops.d.ts +1 -1
- package/dist/features/categories/api/[id]/route.js +4 -3
- package/dist/features/categories/api/route.js +4 -3
- package/dist/features/categories/components/BrandDetailPageView.js +2 -2
- package/dist/features/categories/components/BrandDetailTabs.js +2 -2
- package/dist/features/categories/components/BundleCollage.d.ts +8 -0
- package/dist/features/categories/components/BundleCollage.js +50 -0
- package/dist/features/categories/components/BundleDetailView.js +2 -10
- package/dist/features/categories/components/BundleItemsPicker.js +20 -13
- package/dist/features/categories/components/CategoriesIndexListing.js +28 -9
- package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
- package/dist/features/categories/components/CategoryDetailTabs.js +2 -2
- package/dist/features/categories/components/CategoryProductsListing.js +52 -13
- package/dist/features/categories/components/index.d.ts +2 -0
- package/dist/features/categories/components/index.js +1 -0
- package/dist/features/categories/schemas/index.d.ts +6 -6
- package/dist/features/checkout/actions/checkout-actions.js +4 -3
- package/dist/features/checkout/hooks/usePaymentCheckout.js +31 -22
- package/dist/features/classified/components/ClassifiedFilters.d.ts +8 -0
- package/dist/features/classified/components/ClassifiedFilters.js +19 -0
- package/dist/features/classified/components/ClassifiedIndexListing.d.ts +4 -0
- package/dist/features/classified/components/ClassifiedIndexListing.js +110 -0
- package/dist/features/classified/components/ClassifiedListView.d.ts +6 -0
- package/dist/features/classified/components/ClassifiedListView.js +43 -0
- package/dist/features/classified/components/index.d.ts +6 -0
- package/dist/features/classified/components/index.js +3 -0
- package/dist/features/collections/api/[slug]/route.js +16 -12
- package/dist/features/collections/schemas/index.d.ts +5 -5
- package/dist/features/consultation/api/[id]/route.js +12 -9
- package/dist/features/consultation/components/ConsultationForm.js +3 -2
- package/dist/features/consultation/schemas/index.d.ts +6 -6
- package/dist/features/contact/email.js +77 -116
- package/dist/features/copilot/components/AdminCopilotView.js +12 -2
- package/dist/features/corporate/api/[id]/route.js +12 -9
- package/dist/features/corporate/components/CorporateInquiryForm.js +2 -1
- package/dist/features/digital-codes/components/DigitalCodeFilters.d.ts +8 -0
- package/dist/features/digital-codes/components/DigitalCodeFilters.js +16 -0
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +4 -0
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +107 -0
- package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +6 -0
- package/dist/features/digital-codes/components/DigitalCodesListView.js +37 -0
- package/dist/features/digital-codes/components/index.d.ts +6 -0
- package/dist/features/digital-codes/components/index.js +3 -0
- package/dist/features/events/actions/event-actions.js +4 -3
- package/dist/features/events/components/AdminEventEditorView.js +3 -2
- package/dist/features/events/components/AdminEventsView.js +4 -2
- package/dist/features/events/components/EventPollWidget.js +5 -5
- package/dist/features/events/components/EventsIndexListing.js +2 -2
- package/dist/features/events/schemas/index.d.ts +20 -20
- package/dist/features/faq/actions/faq-actions.d.ts +8 -8
- package/dist/features/faq/components/ContactCTA.js +3 -1
- package/dist/features/faq/components/FAQAccordion.js +1 -1
- package/dist/features/faq/schemas/index.d.ts +4 -4
- package/dist/features/filters/AsyncFacetSection.d.ts +18 -0
- package/dist/features/filters/AsyncFacetSection.js +65 -0
- package/dist/features/forms/Checkbox.js +2 -2
- package/dist/features/forms/Input.js +2 -2
- package/dist/features/forms/Radio.js +5 -5
- package/dist/features/forms/Select.js +2 -2
- package/dist/features/forms/Slider.js +2 -2
- package/dist/features/forms/Textarea.js +2 -2
- package/dist/features/homepage/api/[id]/route.js +4 -3
- package/dist/features/homepage/api/sections/[id]/route.js +12 -9
- package/dist/features/homepage/components/AdSlot.js +2 -1
- package/dist/features/homepage/components/AdvertisementBanner.js +5 -3
- package/dist/features/homepage/components/BrandsSection.js +2 -2
- package/dist/features/homepage/components/CTABannerSection.js +2 -2
- package/dist/features/homepage/components/CharacterHotspot.d.ts +2 -2
- package/dist/features/homepage/components/CharacterHotspot.js +119 -157
- package/dist/features/homepage/components/CharacterHotspotForm.js +25 -20
- package/dist/features/homepage/components/CustomCardsSection.js +8 -6
- package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
- package/dist/features/homepage/components/GoogleReviewsSection.js +3 -3
- package/dist/features/homepage/components/HeroBanner.js +5 -4
- package/dist/features/homepage/components/HeroCarousel.js +13 -26
- package/dist/features/homepage/components/HowItWorksSection.js +4 -2
- package/dist/features/homepage/components/SocialFeedSection.js +2 -2
- package/dist/features/homepage/components/SocialPostCard.js +3 -2
- package/dist/features/homepage/lib/social-feed-fetcher.js +6 -3
- package/dist/features/layout/AppLayoutShell.d.ts +8 -2
- package/dist/features/layout/AppLayoutShell.js +54 -78
- package/dist/features/layout/BottomActions.js +43 -48
- package/dist/features/live/components/LiveItemFilters.d.ts +8 -0
- package/dist/features/live/components/LiveItemFilters.js +23 -0
- package/dist/features/live/components/LiveItemsIndexListing.d.ts +4 -0
- package/dist/features/live/components/LiveItemsIndexListing.js +108 -0
- package/dist/features/live/components/LiveItemsListView.d.ts +6 -0
- package/dist/features/live/components/LiveItemsListView.js +43 -0
- package/dist/features/live/components/index.d.ts +6 -0
- package/dist/features/live/components/index.js +3 -0
- package/dist/features/media/MediaLightbox.js +8 -14
- package/dist/features/media/types/index.d.ts +2 -2
- package/dist/features/media/upload/CameraCapture.js +2 -2
- package/dist/features/media/upload/ImageUpload.js +4 -2
- package/dist/features/media/upload/MediaUploadField.js +24 -8
- package/dist/features/media/upload/MediaUploadList.js +1 -1
- package/dist/features/orders/actions/order-actions.js +4 -3
- package/dist/features/orders/components/MarketplaceOrderCard.js +2 -1
- package/dist/features/orders/components/OrderFilters.js +12 -9
- package/dist/features/orders/schemas/index.d.ts +6 -6
- package/dist/features/pre-orders/api/[id]/route.js +12 -9
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +5 -4
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +27 -20
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +17 -52
- package/dist/features/pre-orders/components/PreorderCard.d.ts +0 -9
- package/dist/features/pre-orders/components/PreorderCard.js +2 -13
- package/dist/features/pre-orders/components/index.d.ts +1 -1
- package/dist/features/pre-orders/components/index.js +1 -1
- package/dist/features/pre-orders/schemas/index.d.ts +10 -10
- package/dist/features/products/api/[id]/route.js +9 -7
- package/dist/features/products/api/route.js +1 -1
- package/dist/features/products/columns/productTableColumns.js +2 -2
- package/dist/features/products/components/AuctionsIndexListing.js +15 -51
- package/dist/features/products/components/BidHistory.js +2 -2
- package/dist/features/products/components/CompareOverlay.js +3 -2
- package/dist/features/products/components/MakeOfferButton.js +20 -14
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -1
- package/dist/features/products/components/PrizeDrawCollage.d.ts +1 -15
- package/dist/features/products/components/PrizeDrawCollage.js +54 -14
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +9 -7
- package/dist/features/products/components/PrizeDrawsIndexListing.js +18 -51
- package/dist/features/products/components/PrizeRevealModal.js +24 -14
- package/dist/features/products/components/ProductDetailPageView.js +2 -1
- package/dist/features/products/components/ProductFilters.js +8 -6
- package/dist/features/products/components/ProductForm.js +12 -10
- package/dist/features/products/components/ProductGalleryClient.js +2 -1
- package/dist/features/products/components/ProductGrid.js +11 -14
- package/dist/features/products/components/ProductsIndexListing.js +16 -52
- package/dist/features/products/components/ProductsIndexPageView.js +2 -2
- package/dist/features/products/schemas/index.d.ts +56 -56
- package/dist/features/products/schemas/product-features.validators.d.ts +14 -14
- package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
- package/dist/features/promotions/schemas/index.d.ts +6 -6
- package/dist/features/reviews/actions/review-actions.js +6 -5
- package/dist/features/reviews/api/[id]/route.js +4 -3
- package/dist/features/reviews/components/ReviewDetailPageView.js +2 -2
- package/dist/features/reviews/components/ReviewDetailShell.js +6 -3
- package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
- package/dist/features/reviews/schemas/index.d.ts +6 -6
- package/dist/features/scams/components/ScamProfileView.js +19 -10
- package/dist/features/scams/components/ScamRegistryView.js +2 -1
- package/dist/features/search/components/Search.d.ts +1 -1
- package/dist/features/search/components/Search.js +56 -49
- package/dist/features/search/schemas/index.d.ts +5 -5
- package/dist/features/seller/actions/offer-actions.js +11 -9
- package/dist/features/seller/components/SellerAddressesView.js +6 -4
- package/dist/features/seller/components/SellerBidsView.js +2 -2
- package/dist/features/seller/components/SellerCouponsView.js +1 -1
- package/dist/features/seller/components/SellerOffersPanel.js +6 -1
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +3 -3
- package/dist/features/seller/components/SellerPreOrdersView.js +5 -4
- package/dist/features/seller/components/SellerPrizeDrawsView.js +5 -4
- package/dist/features/seller/components/SellerProductShell.d.ts +19 -1
- package/dist/features/seller/components/SellerProductShell.js +75 -9
- package/dist/features/seller/components/SellerProductsView.js +10 -11
- package/dist/features/seller/components/SellerShippingView.js +3 -3
- package/dist/features/seller/components/SellerSidebar.js +10 -7
- package/dist/features/seller/components/SellerStorefrontView.js +3 -3
- package/dist/features/seller/schemas/index.d.ts +4 -4
- package/dist/features/shell/FormShell.js +2 -1
- package/dist/features/shell/QuickFormDrawer.js +4 -3
- package/dist/features/shell/StepForm.js +4 -3
- package/dist/features/stores/actions/store-query-actions.js +4 -3
- package/dist/features/stores/components/StoreAboutView.js +2 -1
- package/dist/features/stores/components/StoreAuctionsListing.js +43 -64
- package/dist/features/stores/components/StoreDetailLayoutView.js +2 -2
- package/dist/features/stores/components/StorePreOrdersListing.js +33 -57
- package/dist/features/stores/components/StoreProductsListing.js +16 -49
- package/dist/features/stores/components/StoresIndexListing.js +2 -2
- package/dist/features/stores/schemas/index.d.ts +2 -2
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +8 -7
- package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +5 -5
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +6 -6
- package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -5
- package/dist/index.d.ts +21 -3
- package/dist/index.js +1737 -1725
- package/dist/monitoring/server-logger.js +5 -9
- package/dist/next/api/apiHandler.js +56 -37
- package/dist/next/components/GlobalError.js +2 -1
- package/dist/next/routing/route-map.d.ts +27 -0
- package/dist/next/routing/route-map.js +10 -0
- package/dist/react/ErrorBoundary.js +2 -1
- package/dist/react/contexts/SessionContext.js +38 -28
- package/dist/react/hooks/useCamera.d.ts +1 -1
- package/dist/react/hooks/useCamera.js +8 -7
- package/dist/seed/site-settings-seed-data.js +6 -0
- package/dist/server-entry.d.ts +3 -0
- package/dist/server-entry.js +3 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.js +15 -0
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/DataTable.js +28 -7
- package/dist/ui/components/Button.d.ts +8 -1
- package/dist/ui/components/Button.js +47 -8
- package/dist/ui/components/CollageLightbox.d.ts +18 -0
- package/dist/ui/components/CollageLightbox.js +41 -0
- package/dist/ui/components/Dropdown.js +7 -6
- package/dist/ui/components/ImageLightbox.d.ts +7 -1
- package/dist/ui/components/ImageLightbox.js +15 -5
- package/dist/ui/components/PageLoader.js +4 -3
- package/dist/ui/components/PaginatedMultiSelect.d.ts +28 -0
- package/dist/ui/components/PaginatedMultiSelect.js +141 -0
- package/dist/ui/components/PaginatedMultiSelect.style.css +272 -0
- package/dist/ui/components/Semantic.js +1 -1
- package/dist/ui/components/SideDrawer.js +17 -15
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/utils/number.formatter.js +5 -13
- package/dist/utils/string.formatter.js +21 -18
- package/dist/validation/schemas.d.ts +2 -2
- package/package.json +1 -1
|
@@ -60,22 +60,33 @@ export function NotificationBell({ limit = 10, viewAllHref, labels, icons, rende
|
|
|
60
60
|
markAllRead,
|
|
61
61
|
refetch,
|
|
62
62
|
]);
|
|
63
|
+
const handleMarkReadAndClose = useCallback((notification) => {
|
|
64
|
+
if (!notification.isRead)
|
|
65
|
+
void handleMarkRead(notification.id);
|
|
66
|
+
setIsOpen(false);
|
|
67
|
+
}, [handleMarkRead, setIsOpen]);
|
|
63
68
|
const renderActionLink = renderLink ??
|
|
64
69
|
((props) => (_jsx(TextLink, { href: props.href, onClick: props.onClick, className: props.className, children: props.children })));
|
|
65
|
-
return (_jsxs("div", { className: "relative", ref: dropdownRef, "data-section": "notificationbell-div-227", children: [
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
return (_jsxs("div", { className: "relative", ref: dropdownRef, "data-section": "notificationbell-div-227", children: [renderBellButton({ hideOnMobile, buttonClassName, labels, unreadCount, handleToggle, isOpen }), isOpen && renderNotificationDropdown({
|
|
71
|
+
dropdownClassName, labels, unreadCount, isMarkingAll, handleMarkAllRead,
|
|
72
|
+
isLoading, notifications: notifications, notificationIcons,
|
|
73
|
+
handleMarkRead, handleMarkReadAndClose, renderActionLink, viewAllHref,
|
|
74
|
+
setIsOpen,
|
|
75
|
+
})] }));
|
|
76
|
+
}
|
|
77
|
+
function renderBellButton(props) {
|
|
78
|
+
const { hideOnMobile, buttonClassName, labels, unreadCount, handleToggle, isOpen } = props;
|
|
79
|
+
return (_jsxs(Button, { onClick: handleToggle, className: `${hideOnMobile ? "hidden md:flex" : "flex"} p-2.5 md:p-3 rounded-xl transition-colors relative text-zinc-600 hover:bg-zinc-100 hover:text-zinc-900 dark:text-zinc-300 dark:hover:bg-slate-800 dark:hover:text-white ${buttonClassName}`, "aria-label": labels.title, "aria-expanded": isOpen, children: [_jsx("svg", { className: "w-6 h-6 md:w-7 md:h-7", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" }) }), unreadCount > 0 && (_jsx(Span, { className: "absolute -top-1 -right-1 bg-red-500 text-white text-xs min-w-[20px] h-5 px-1.5 flex items-center justify-center rounded-full font-semibold shadow-md", children: unreadCount > 99 ? "99+" : unreadCount }))] }));
|
|
80
|
+
}
|
|
81
|
+
function renderNotificationDropdown(props) {
|
|
82
|
+
const { dropdownClassName, labels, unreadCount, isMarkingAll, handleMarkAllRead, isLoading, notifications, notificationIcons, handleMarkRead, handleMarkReadAndClose, renderActionLink, viewAllHref, setIsOpen } = props;
|
|
83
|
+
return (_jsxs("div", { className: `absolute right-0 top-full mt-2 w-80 sm:w-96 rounded-xl shadow-2xl border border-zinc-200 bg-white dark:border-slate-800 dark:bg-slate-950 z-50 overflow-hidden ${dropdownClassName}`, "data-section": "notificationbell-div-228", children: [_jsxs(Row, { justify: "between", gap: "none", className: "px-4 py-3 border-b border-zinc-200 dark:border-slate-800", children: [_jsxs(Heading, { level: 3, className: "font-semibold text-zinc-900 dark:text-white", children: [labels.title, unreadCount > 0 && _jsxs(Span, { className: "ml-2 text-xs bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400 px-2 py-0.5 rounded-full font-medium", children: [unreadCount, " ", labels.unread] })] }), unreadCount > 0 && (_jsx(Button, { onClick: handleMarkAllRead, disabled: isMarkingAll, className: "text-xs text-primary hover:underline font-medium disabled:opacity-50", children: isMarkingAll ? labels.loading : labels.markAllRead }))] }), _jsx("div", { className: "max-h-96 overflow-y-auto", "data-section": "notificationbell-div-229", children: renderNotificationListContent({ isLoading, notifications, notificationIcons, labels, handleMarkRead, handleMarkReadAndClose, renderActionLink }) }), viewAllHref && (_jsx("div", { className: "px-4 py-3 border-t border-zinc-200 dark:border-slate-800 text-center", "data-section": "notificationbell-div-234", children: renderActionLink({ href: viewAllHref, onClick: () => setIsOpen(false), className: "text-sm text-primary hover:underline font-medium", children: labels.viewAll }) }))] }));
|
|
84
|
+
}
|
|
85
|
+
function renderNotificationListContent(props) {
|
|
86
|
+
const { isLoading, notifications, notificationIcons, labels, handleMarkRead, handleMarkReadAndClose, renderActionLink } = props;
|
|
87
|
+
if (isLoading)
|
|
88
|
+
return _jsx("div", { className: "flex items-center justify-center py-10", "data-section": "notificationbell-div-230", children: _jsx(Spinner, { size: "md" }) });
|
|
89
|
+
if (notifications.length === 0)
|
|
90
|
+
return (_jsxs("div", { className: "flex flex-col items-center justify-center py-10 px-4 text-center", "data-section": "notificationbell-div-231", children: [_jsx("svg", { className: "w-12 h-12 text-zinc-300 dark:text-zinc-600 mb-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" }) }), _jsx(Text, { className: "font-medium text-zinc-900 dark:text-white", children: labels.empty }), _jsx(Text, { size: "sm", className: "mt-1 text-zinc-500 dark:text-zinc-400", children: labels.emptyDesc })] }));
|
|
91
|
+
return (_jsx(Ul, { children: notifications.map((notification) => (_jsxs(Li, { className: `group flex items-start gap-3 px-4 py-3 border-b border-zinc-200 dark:border-slate-800 last:border-0 transition-colors hover:bg-zinc-50 dark:hover:bg-slate-900 ${!notification.isRead ? "bg-primary/5 dark:bg-primary/10" : ""}`, children: [_jsx(Span, { className: "text-xl flex-shrink-0 mt-0.5", children: notificationIcons[notification.type] ?? "🔔" }), _jsxs("div", { className: "flex-1 min-w-0", "data-section": "notificationbell-div-232", children: [_jsxs("div", { className: `flex items-start justify-between ${THEME_CONSTANTS.spacing.gap.xs}`, "data-section": "notificationbell-div-233", children: [_jsxs(Text, { size: "sm", className: "font-medium text-zinc-900 dark:text-white leading-tight", children: [notification.title, !notification.isRead && _jsx(Span, { className: "ml-1.5 inline-block w-2 h-2 rounded-full bg-primary flex-shrink-0 align-middle" })] }), _jsx(Span, { className: "text-xs text-zinc-500 dark:text-zinc-400 flex-shrink-0", children: formatRelativeTime(notification.createdAt) })] }), _jsx(Text, { size: "sm", className: `text-zinc-600 dark:text-zinc-300 mt-0.5 ${THEME_CONSTANTS.utilities.textClamp2}`, children: notification.message }), _jsxs(Row, { gap: "3", className: "mt-1.5", children: [notification.actionUrl && renderActionLink({ href: notification.actionUrl, onClick: () => { void handleMarkReadAndClose(notification); }, className: "text-xs text-primary hover:underline font-medium", children: notification.actionLabel ?? labels.viewAction }), !notification.isRead && _jsx(Button, { variant: "ghost", onClick: () => void handleMarkRead(notification.id), className: "text-xs text-zinc-500 dark:text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 hover:underline p-0 h-auto", children: labels.markRead })] })] })] }, notification.id))) }));
|
|
81
92
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NotificationPreferences } from "../types";
|
|
2
|
+
export interface NotificationPreferencesPanelProps {
|
|
3
|
+
/** Fetch URL — defaults to /api/user/notification-preferences */
|
|
4
|
+
fetchUrl?: string;
|
|
5
|
+
/** Save URL — defaults to /api/user/notification-preferences */
|
|
6
|
+
saveUrl?: string;
|
|
7
|
+
onSave?: (prefs: NotificationPreferences) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function NotificationPreferencesPanel({ fetchUrl, saveUrl, onSave, }: NotificationPreferencesPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect, useCallback } from "react";
|
|
4
|
+
import { Stack, Row, Text, Button, Div } from "../../../ui";
|
|
5
|
+
import { useSiteSettings } from "../../../core/hooks/useSiteSettings";
|
|
6
|
+
// ─── type-row config ────────────────────────────────────────────────────────
|
|
7
|
+
const TYPE_ROWS = [
|
|
8
|
+
{ key: "orderUpdates", label: "Order updates", description: "Placed, shipped, delivered, cancelled, refunded" },
|
|
9
|
+
{ key: "bids", label: "Auction bids", description: "Outbid alerts and auction win/loss" },
|
|
10
|
+
{ key: "offers", label: "Offers", description: "New offers received, counter-offers, responses" },
|
|
11
|
+
{ key: "reviews", label: "Reviews", description: "Review approved, seller reply" },
|
|
12
|
+
{ key: "messages", label: "Messages", description: "New buyer-seller chat messages" },
|
|
13
|
+
{ key: "promotions", label: "Promotions", description: "Coupons, sales and platform events" },
|
|
14
|
+
{ key: "system", label: "System & welcome", description: "Account alerts and platform announcements" },
|
|
15
|
+
];
|
|
16
|
+
// ─── Toggle row ─────────────────────────────────────────────────────────────
|
|
17
|
+
function ToggleRow({ label, description, checked, onChange, disabled, }) {
|
|
18
|
+
return (_jsxs(Row, { align: "center", justify: "between", gap: "md", className: "py-3 border-b last:border-0 border-[var(--appkit-color-border)]", children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: "text-sm font-medium text-[var(--appkit-color-text)]", children: label }), _jsx(Text, { variant: "secondary", className: "text-xs mt-0.5", children: description })] }), _jsx("button", { type: "button", role: "switch", "aria-checked": checked, "aria-label": label, disabled: disabled, onClick: () => onChange(!checked), className: [
|
|
19
|
+
"relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent",
|
|
20
|
+
"transition-colors duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2",
|
|
21
|
+
"focus-visible:ring-[var(--appkit-color-primary)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
22
|
+
checked ? "bg-[var(--appkit-color-primary)]" : "bg-[var(--appkit-color-border-strong,#a1a1aa)]",
|
|
23
|
+
].join(" "), children: _jsx("span", { className: [
|
|
24
|
+
"pointer-events-none inline-block h-4 w-4 transform rounded-full bg-white shadow-lg",
|
|
25
|
+
"ring-0 transition duration-200 ease-in-out",
|
|
26
|
+
checked ? "translate-x-4" : "translate-x-0",
|
|
27
|
+
].join(" ") }) })] }));
|
|
28
|
+
}
|
|
29
|
+
// ─── Section card ───────────────────────────────────────────────────────────
|
|
30
|
+
function SectionCard({ title, children }) {
|
|
31
|
+
return (_jsxs(Div, { className: "rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden", children: [_jsx(Div, { className: "px-4 py-3 border-b border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-alt,var(--appkit-color-surface))]", children: _jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]", children: title }) }), _jsx(Div, { className: "px-4", children: children })] }));
|
|
32
|
+
}
|
|
33
|
+
export function NotificationPreferencesPanel({ fetchUrl = "/api/user/notification-preferences", saveUrl = "/api/user/notification-preferences", onSave, }) {
|
|
34
|
+
const { data: settings } = useSiteSettings();
|
|
35
|
+
const adminChannels = settings?.notificationChannels;
|
|
36
|
+
const [channels, setChannels] = useState({ email: true, whatsapp: true, sms: true });
|
|
37
|
+
const [types, setTypes] = useState({
|
|
38
|
+
orderUpdates: true, bids: true, promotions: true,
|
|
39
|
+
system: true, reviews: true, messages: true, offers: true,
|
|
40
|
+
});
|
|
41
|
+
const [loading, setLoading] = useState(true);
|
|
42
|
+
const [saving, setSaving] = useState(false);
|
|
43
|
+
const [saved, setSaved] = useState(false);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
fetch(fetchUrl, { credentials: "include" })
|
|
46
|
+
.then((r) => r.json())
|
|
47
|
+
.then((json) => {
|
|
48
|
+
const prefs = json.data?.notificationPreferences;
|
|
49
|
+
if (prefs?.channels)
|
|
50
|
+
setChannels((prev) => ({ ...prev, ...prefs.channels }));
|
|
51
|
+
if (prefs?.types)
|
|
52
|
+
setTypes((prev) => ({ ...prev, ...prefs.types }));
|
|
53
|
+
})
|
|
54
|
+
.catch(() => { })
|
|
55
|
+
.finally(() => setLoading(false));
|
|
56
|
+
}, [fetchUrl]);
|
|
57
|
+
const setChannel = useCallback((key, val) => {
|
|
58
|
+
setChannels((prev) => ({ ...prev, [key]: val }));
|
|
59
|
+
setSaved(false);
|
|
60
|
+
}, []);
|
|
61
|
+
const setType = useCallback((key, val) => {
|
|
62
|
+
setTypes((prev) => ({ ...prev, [key]: val }));
|
|
63
|
+
setSaved(false);
|
|
64
|
+
}, []);
|
|
65
|
+
const handleSave = useCallback(async () => {
|
|
66
|
+
setSaving(true);
|
|
67
|
+
try {
|
|
68
|
+
const prefs = { channels, types };
|
|
69
|
+
await fetch(saveUrl, {
|
|
70
|
+
method: "PUT",
|
|
71
|
+
credentials: "include",
|
|
72
|
+
headers: { "Content-Type": "application/json" },
|
|
73
|
+
body: JSON.stringify(prefs),
|
|
74
|
+
});
|
|
75
|
+
setSaved(true);
|
|
76
|
+
onSave?.(prefs);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
setSaving(false);
|
|
80
|
+
}
|
|
81
|
+
}, [channels, types, saveUrl, onSave]);
|
|
82
|
+
if (loading) {
|
|
83
|
+
return (_jsx(Div, { className: "py-8 text-center", children: _jsx(Text, { variant: "secondary", className: "text-sm", children: "Loading preferences\u2026" }) }));
|
|
84
|
+
}
|
|
85
|
+
// Which channels are admin-enabled (only show those toggles to the user)
|
|
86
|
+
const emailAdminEnabled = adminChannels?.email?.enabled ?? false;
|
|
87
|
+
const whatsappAdminEnabled = adminChannels?.whatsapp?.enabled ?? false;
|
|
88
|
+
const smsAdminEnabled = adminChannels?.sms?.enabled ?? false;
|
|
89
|
+
const anyChannelEnabled = emailAdminEnabled || whatsappAdminEnabled || smsAdminEnabled;
|
|
90
|
+
return (_jsxs(Stack, { gap: "lg", children: [_jsx(SectionCard, { title: "In-app notifications", children: _jsxs(Row, { align: "center", justify: "between", gap: "md", className: "py-3", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-sm font-medium text-[var(--appkit-color-text)]", children: "In-app notifications" }), _jsx(Text, { variant: "secondary", className: "text-xs mt-0.5", children: "Always on \u2014 displayed in the notification bell and inbox" })] }), _jsx(Div, { className: "h-5 w-9 rounded-full bg-[var(--appkit-color-primary)] flex items-center justify-end px-0.5", "aria-label": "Always on", children: _jsx("span", { className: "h-4 w-4 rounded-full bg-white shadow" }) })] }) }), anyChannelEnabled && (_jsxs(SectionCard, { title: "Notification channels", children: [emailAdminEnabled && (_jsx(ToggleRow, { label: "Email", description: "Receive notifications by email", checked: channels.email !== false, onChange: (v) => setChannel("email", v) })), whatsappAdminEnabled && (_jsx(ToggleRow, { label: "WhatsApp", description: "Receive notifications on WhatsApp", checked: channels.whatsapp !== false, onChange: (v) => setChannel("whatsapp", v) })), smsAdminEnabled && (_jsx(ToggleRow, { label: "SMS", description: "Receive SMS text notifications", checked: channels.sms !== false, onChange: (v) => setChannel("sms", v) }))] })), _jsx(SectionCard, { title: "Notification types", children: TYPE_ROWS.map((row) => (_jsx(ToggleRow, { label: row.label, description: row.description, checked: types[row.key] !== false, onChange: (v) => setType(row.key, v) }, row.key))) }), _jsxs(Row, { justify: "end", gap: "sm", children: [saved && (_jsx(Text, { className: "text-sm text-[var(--appkit-color-success,#16a34a)] self-center", children: "Preferences saved \u2713" })), _jsx(Button, { onClick: handleSave, isLoading: saving, size: "sm", children: "Save preferences" })] })] }));
|
|
91
|
+
}
|
|
@@ -6,6 +6,9 @@ import Link from "next/link";
|
|
|
6
6
|
import { usePathname } from "next/navigation";
|
|
7
7
|
import { Li, Nav, Span, Ul } from "../../../ui";
|
|
8
8
|
import { BottomSheet } from "../../layout/BottomSheet";
|
|
9
|
+
function isNavItemActive(item, activeHref) {
|
|
10
|
+
return activeHref === item.href || activeHref.startsWith(item.href + "/");
|
|
11
|
+
}
|
|
9
12
|
function NavLink({ item, isActive, onClick }) {
|
|
10
13
|
return (_jsxs(Link, { href: item.href, onClick: onClick, className: `flex items-center gap-2.5 rounded-lg px-3 py-2 text-[0.8125rem] font-medium leading-tight transition-colors ${isActive
|
|
11
14
|
? "bg-primary-50 dark:bg-primary-900/25 text-primary-700 dark:text-primary-300"
|
|
@@ -35,10 +38,7 @@ function DrawerContent({ groups, items, activeHref, onItemClick, }) {
|
|
|
35
38
|
const hasActive = group.items.some((i) => activeHref === i.href || activeHref.startsWith(i.href + "/"));
|
|
36
39
|
return (_jsxs("div", { className: "mb-0.5", children: [_jsxs("button", { type: "button", onClick: () => toggle(group.title), className: `flex w-full items-center justify-between px-4 py-2 text-[0.6875rem] font-semibold uppercase tracking-widest transition-colors ${hasActive && !isOpen
|
|
37
40
|
? "text-primary-600 dark:text-primary-400"
|
|
38
|
-
: "text-zinc-400 dark:text-zinc-500 hover:text-zinc-600 dark:hover:text-zinc-300"}`, children: [_jsx("span", { children: group.title }), _jsx("svg", { className: `w-3 h-3 transition-transform duration-150 ${isOpen ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M19 9l-7 7-7-7" }) })] }), isOpen && (_jsx(Ul, { className: "space-y-0.5 px-3 pb-1", children: group.items.map((item) => {
|
|
39
|
-
const isActive = activeHref === item.href || activeHref.startsWith(item.href + "/");
|
|
40
|
-
return (_jsx(Li, { children: _jsx(NavLink, { item: item, isActive: isActive, onClick: onItemClick }) }, item.href));
|
|
41
|
-
}) }))] }, group.title));
|
|
41
|
+
: "text-zinc-400 dark:text-zinc-500 hover:text-zinc-600 dark:hover:text-zinc-300"}`, children: [_jsx("span", { children: group.title }), _jsx("svg", { className: `w-3 h-3 transition-transform duration-150 ${isOpen ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M19 9l-7 7-7-7" }) })] }), isOpen && (_jsx(Ul, { className: "space-y-0.5 px-3 pb-1", children: group.items.map((item) => (_jsx(Li, { children: _jsx(NavLink, { item: item, isActive: isNavItemActive(item, activeHref), onClick: onItemClick }) }, item.href))) }))] }, group.title));
|
|
42
42
|
}) }));
|
|
43
43
|
}
|
|
44
44
|
function DrawerPanel({ title, onClose, children, }) {
|
|
@@ -6,6 +6,7 @@ import { Button, Div, FormActions, Select, SideDrawer, Text, useToast, } from ".
|
|
|
6
6
|
import { apiClient } from "../../../http";
|
|
7
7
|
import { SUPPORT_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
8
|
// --- Constants ---------------------------------------------------------------
|
|
9
|
+
const CLS_INPUT = "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500";
|
|
9
10
|
const CATEGORY_OPTIONS = [
|
|
10
11
|
{ label: "Order Issue", value: "order_issue" },
|
|
11
12
|
{ label: "Billing / Payment", value: "billing_payment" },
|
|
@@ -81,10 +82,19 @@ export function UserSupportView(_props) {
|
|
|
81
82
|
showToast(err?.message ?? "Failed to send reply.", "error");
|
|
82
83
|
},
|
|
83
84
|
});
|
|
84
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "mx-auto max-w-2xl px-4 py-6", children: [_jsxs(Div, { className: "mb-4 flex items-center justify-between", children: [_jsx(Text, { className: "text-xl font-semibold text-zinc-900 dark:text-zinc-100", children: "Support Tickets" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => setNewTicketOpen(true), children: "New ticket" })] }),
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "mx-auto max-w-2xl px-4 py-6", children: [_jsxs(Div, { className: "mb-4 flex items-center justify-between", children: [_jsx(Text, { className: "text-xl font-semibold text-zinc-900 dark:text-zinc-100", children: "Support Tickets" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => setNewTicketOpen(true), children: "New ticket" })] }), renderTicketListArea({ isLoading, error, tickets, setSelectedTicket, setDetailOpen })] }), renderNewTicketDrawer({ newTicketOpen, setNewTicketOpen, newCategory, setNewCategory, newSubject, setNewSubject, newOrderId, setNewOrderId, newDescription, setNewDescription, createMutation }), renderTicketDetailDrawer({ detailOpen, setDetailOpen, selectedTicket, replyBody, setReplyBody, replyMutation })] }));
|
|
86
|
+
}
|
|
87
|
+
function renderTicketListArea(props) {
|
|
88
|
+
const { isLoading, error, tickets, setSelectedTicket, setDetailOpen } = props;
|
|
89
|
+
return (_jsxs(_Fragment, { children: [isLoading && (_jsx(Div, { className: "space-y-3", children: [1, 2, 3].map((i) => _jsx("div", { className: "h-16 animate-pulse rounded-lg bg-zinc-100 dark:bg-zinc-800" }, i)) })), error && (_jsx(Div, { className: "rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: "Failed to load support tickets." })), !isLoading && tickets.length === 0 && (_jsx(Div, { className: "rounded-xl border border-zinc-200 bg-zinc-50 px-6 py-10 text-center dark:border-zinc-700 dark:bg-zinc-900/40", children: _jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "You haven't opened any support tickets yet." }) })), _jsx("ul", { className: "space-y-3", children: tickets.map((ticket) => (_jsx("li", { children: _jsx("button", { type: "button", className: "w-full rounded-xl border border-zinc-200 bg-white p-4 text-left shadow-sm hover:border-primary-300 transition-colors dark:border-zinc-700 dark:bg-zinc-900", onClick: () => { setSelectedTicket(ticket); setDetailOpen(true); }, children: _jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100 truncate", children: ticket.subject }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: [ticket.category.replace(/_/g, " "), ticket.orderId ? ` · Order: ${ticket.orderId}` : ""] })] }), _jsx("span", { className: `shrink-0 inline-flex rounded-full px-2.5 py-0.5 text-xs font-medium ${STATUS_BADGE[ticket.status] ?? STATUS_BADGE.open}`, children: ticket.status.replace(/_/g, " ") })] }) }) }, ticket.id))) })] }));
|
|
90
|
+
}
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
+
function renderNewTicketDrawer(props) {
|
|
93
|
+
const { newTicketOpen, setNewTicketOpen, newCategory, setNewCategory, newSubject, setNewSubject, newOrderId, setNewOrderId, newDescription, setNewDescription, createMutation } = props;
|
|
94
|
+
return (_jsx(SideDrawer, { isOpen: newTicketOpen, onClose: () => setNewTicketOpen(false), title: "Open a support ticket", children: _jsxs(Div, { className: "flex flex-col gap-4 p-4", children: [_jsx(Select, { label: "Category", options: CATEGORY_OPTIONS, value: newCategory, onValueChange: setNewCategory }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Subject" }), _jsx("input", { type: "text", value: newSubject, onChange: (e) => setNewSubject(e.target.value), placeholder: "Brief description of the issue", className: CLS_INPUT })] }), newCategory === "order_issue" && (_jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Order ID" }), _jsx("input", { type: "text", value: newOrderId, onChange: (e) => setNewOrderId(e.target.value), placeholder: "e.g. order-3-20260508-a1b2c3", className: CLS_INPUT })] })), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Description" }), _jsx("textarea", { value: newDescription, onChange: (e) => setNewDescription(e.target.value), rows: 4, placeholder: "Describe the issue in detail\u2026", className: CLS_INPUT })] }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: () => setNewTicketOpen(false), children: "Cancel" }), _jsx(Button, { type: "button", isLoading: createMutation.isPending, disabled: !newSubject.trim() || !newDescription.trim() || createMutation.isPending, onClick: () => createMutation.mutate(), children: "Submit ticket" })] })] }) }));
|
|
95
|
+
}
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
97
|
+
function renderTicketDetailDrawer(props) {
|
|
98
|
+
const { detailOpen, setDetailOpen, selectedTicket, replyBody, setReplyBody, replyMutation } = props;
|
|
99
|
+
return (_jsx(SideDrawer, { isOpen: detailOpen, onClose: () => setDetailOpen(false), title: selectedTicket?.subject ?? "Ticket", children: selectedTicket && (_jsxs(Div, { className: "flex flex-col gap-4 p-4", children: [_jsxs(Div, { className: "flex flex-wrap gap-2", children: [_jsx("span", { className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${STATUS_BADGE[selectedTicket.status] ?? STATUS_BADGE.open}`, children: selectedTicket.status.replace(/_/g, " ") }), _jsx("span", { className: "rounded-full bg-zinc-100 px-2.5 py-1 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300", children: selectedTicket.category.replace(/_/g, " ") }), selectedTicket.orderId && _jsxs("span", { className: "rounded-full bg-zinc-100 px-2.5 py-1 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300", children: ["Order: ", selectedTicket.orderId] })] }), selectedTicket.description && (_jsxs(Div, { className: "rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsx(Text, { className: "mb-1 text-xs font-semibold text-zinc-500 uppercase tracking-wide", children: "Your description" }), _jsx(Text, { className: "whitespace-pre-wrap text-sm text-zinc-700 dark:text-zinc-200", children: selectedTicket.description })] })), (selectedTicket.messages ?? []).length > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold text-zinc-500 uppercase tracking-wide", children: "Messages" }), _jsx("div", { className: "space-y-2 max-h-72 overflow-y-auto", children: (selectedTicket.messages ?? []).map((msg, i) => (_jsxs("div", { className: `rounded-lg p-3 text-sm ${msg.authorRole === "user" ? "bg-zinc-50 border border-zinc-200 dark:bg-zinc-900/40 dark:border-zinc-700" : "bg-blue-50 border border-blue-200 dark:bg-blue-900/20 dark:border-blue-800"}`, children: [_jsxs("div", { className: "mb-1 flex items-center gap-2 text-xs text-zinc-400 dark:text-zinc-500", children: [_jsx("span", { className: "font-medium text-zinc-600 dark:text-zinc-300", children: ROLE_LABEL[msg.authorRole ?? "user"] ?? msg.authorRole }), msg.createdAt && _jsx("span", { children: new Date(msg.createdAt).toLocaleString() })] }), _jsx(Text, { className: "whitespace-pre-wrap text-zinc-700 dark:text-zinc-200", children: msg.body })] }, msg.id ?? i))) })] })), selectedTicket.status !== "closed" && selectedTicket.status !== "resolved" && (_jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Reply" }), _jsx("textarea", { value: replyBody, onChange: (e) => setReplyBody(e.target.value), rows: 3, placeholder: "Add a message to your ticket\u2026", className: CLS_INPUT }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: replyMutation.isPending, disabled: !replyBody.trim() || replyMutation.isPending, onClick: () => replyMutation.mutate(), children: "Send reply" })] }))] })) }));
|
|
90
100
|
}
|
|
@@ -40,3 +40,5 @@ export { UserReturnsView } from "./UserReturnsView";
|
|
|
40
40
|
export type { UserReturnsViewProps, UserReturnsViewLabels, } from "./UserReturnsView";
|
|
41
41
|
export { UserSupportView } from "./UserSupportView";
|
|
42
42
|
export type { UserSupportViewProps } from "./UserSupportView";
|
|
43
|
+
export { NotificationPreferencesPanel } from "./NotificationPreferencesPanel";
|
|
44
|
+
export type { NotificationPreferencesPanelProps } from "./NotificationPreferencesPanel";
|
|
@@ -19,3 +19,4 @@ export { BecomeSellerView } from "./BecomeSellerView";
|
|
|
19
19
|
export { UserOrderTrackView } from "./UserOrderTrackView";
|
|
20
20
|
export { UserReturnsView } from "./UserReturnsView";
|
|
21
21
|
export { UserSupportView } from "./UserSupportView";
|
|
22
|
+
export { NotificationPreferencesPanel } from "./NotificationPreferencesPanel";
|
|
@@ -132,6 +132,7 @@ export declare const userProfileSchema: z.ZodObject<{
|
|
|
132
132
|
updatedAt?: string | undefined;
|
|
133
133
|
displayName?: string | undefined;
|
|
134
134
|
email?: string | undefined;
|
|
135
|
+
phone?: string | undefined;
|
|
135
136
|
addresses?: {
|
|
136
137
|
country: string;
|
|
137
138
|
id: string;
|
|
@@ -145,8 +146,6 @@ export declare const userProfileSchema: z.ZodObject<{
|
|
|
145
146
|
line2?: string | undefined;
|
|
146
147
|
}[] | undefined;
|
|
147
148
|
photoURL?: string | undefined;
|
|
148
|
-
phone?: string | undefined;
|
|
149
|
-
bio?: string | undefined;
|
|
150
149
|
notificationPreferences?: {
|
|
151
150
|
push?: boolean | undefined;
|
|
152
151
|
promotions?: boolean | undefined;
|
|
@@ -154,12 +153,14 @@ export declare const userProfileSchema: z.ZodObject<{
|
|
|
154
153
|
orderUpdates?: boolean | undefined;
|
|
155
154
|
sms?: boolean | undefined;
|
|
156
155
|
} | undefined;
|
|
156
|
+
bio?: string | undefined;
|
|
157
157
|
}, {
|
|
158
158
|
id: string;
|
|
159
159
|
createdAt?: string | undefined;
|
|
160
160
|
updatedAt?: string | undefined;
|
|
161
161
|
displayName?: string | undefined;
|
|
162
162
|
email?: string | undefined;
|
|
163
|
+
phone?: string | undefined;
|
|
163
164
|
addresses?: {
|
|
164
165
|
country: string;
|
|
165
166
|
id: string;
|
|
@@ -173,8 +174,6 @@ export declare const userProfileSchema: z.ZodObject<{
|
|
|
173
174
|
line2?: string | undefined;
|
|
174
175
|
}[] | undefined;
|
|
175
176
|
photoURL?: string | undefined;
|
|
176
|
-
phone?: string | undefined;
|
|
177
|
-
bio?: string | undefined;
|
|
178
177
|
notificationPreferences?: {
|
|
179
178
|
push?: boolean | undefined;
|
|
180
179
|
promotions?: boolean | undefined;
|
|
@@ -182,6 +181,7 @@ export declare const userProfileSchema: z.ZodObject<{
|
|
|
182
181
|
orderUpdates?: boolean | undefined;
|
|
183
182
|
sms?: boolean | undefined;
|
|
184
183
|
} | undefined;
|
|
184
|
+
bio?: string | undefined;
|
|
185
185
|
}>;
|
|
186
186
|
export declare const updateProfileSchema: z.ZodObject<{
|
|
187
187
|
displayName: z.ZodOptional<z.ZodString>;
|
|
@@ -57,11 +57,45 @@ export interface UserAddress {
|
|
|
57
57
|
isDefault?: boolean;
|
|
58
58
|
phone?: string;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Per-channel on/off toggles at the user level.
|
|
62
|
+
* Channels are only surfaced in the UI when the admin has enabled them in
|
|
63
|
+
* siteSettings.notificationChannels — this is enforced on the server side.
|
|
64
|
+
*/
|
|
65
|
+
export interface NotificationChannelPrefs {
|
|
66
|
+
/** Disable all email notifications for this user (default: true = enabled). */
|
|
67
|
+
email?: boolean;
|
|
68
|
+
/** Disable all WhatsApp notifications for this user. */
|
|
69
|
+
whatsapp?: boolean;
|
|
70
|
+
/** Disable all SMS notifications for this user. */
|
|
71
|
+
sms?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Per notification-type on/off controls. `true` = enabled (default when absent).
|
|
75
|
+
*/
|
|
76
|
+
export interface NotificationTypePrefs {
|
|
77
|
+
orderUpdates?: boolean;
|
|
78
|
+
bids?: boolean;
|
|
79
|
+
promotions?: boolean;
|
|
80
|
+
system?: boolean;
|
|
81
|
+
reviews?: boolean;
|
|
82
|
+
messages?: boolean;
|
|
83
|
+
offers?: boolean;
|
|
84
|
+
}
|
|
60
85
|
export interface NotificationPreferences {
|
|
86
|
+
/** Per-channel global toggles (user can silence a whole channel). */
|
|
87
|
+
channels?: NotificationChannelPrefs;
|
|
88
|
+
/** Per notification-type toggles (user can silence specific event classes). */
|
|
89
|
+
types?: NotificationTypePrefs;
|
|
90
|
+
/** @deprecated Use types.orderUpdates */
|
|
61
91
|
orderUpdates?: boolean;
|
|
92
|
+
/** @deprecated Use types.promotions */
|
|
62
93
|
promotions?: boolean;
|
|
94
|
+
/** @deprecated Use channels.sms */
|
|
63
95
|
newsletter?: boolean;
|
|
96
|
+
/** @deprecated Use channels.sms */
|
|
64
97
|
sms?: boolean;
|
|
98
|
+
/** Push notifications reserved for future use. */
|
|
65
99
|
push?: boolean;
|
|
66
100
|
}
|
|
67
101
|
export interface UserProfile {
|
|
@@ -12,6 +12,7 @@ import { userRepository } from "../../auth/repository/user.repository";
|
|
|
12
12
|
import { productRepository } from "../../products/repository/products.repository";
|
|
13
13
|
import { NotFoundError, ValidationError } from "../../../errors";
|
|
14
14
|
import { getProviders } from "../../../contracts";
|
|
15
|
+
const ERR_UID_REQUIRED = "uid is required";
|
|
15
16
|
export async function revokeSession(adminId, sessionId) {
|
|
16
17
|
if (!sessionId?.trim()) {
|
|
17
18
|
throw new ValidationError("sessionId is required");
|
|
@@ -83,7 +84,7 @@ export async function adminUpdatePayout(adminId, id, input) {
|
|
|
83
84
|
}
|
|
84
85
|
export async function adminUpdateUser(adminId, uid, input) {
|
|
85
86
|
if (!uid?.trim()) {
|
|
86
|
-
throw new ValidationError(
|
|
87
|
+
throw new ValidationError(ERR_UID_REQUIRED);
|
|
87
88
|
}
|
|
88
89
|
const existing = await userRepository.findById(uid);
|
|
89
90
|
if (!existing) {
|
|
@@ -109,7 +110,7 @@ export async function adminUpdateUser(adminId, uid, input) {
|
|
|
109
110
|
}
|
|
110
111
|
export async function adminDeleteUser(adminId, uid) {
|
|
111
112
|
if (!uid?.trim()) {
|
|
112
|
-
throw new ValidationError(
|
|
113
|
+
throw new ValidationError(ERR_UID_REQUIRED);
|
|
113
114
|
}
|
|
114
115
|
if (uid === adminId) {
|
|
115
116
|
throw new ValidationError("Cannot delete your own account");
|
|
@@ -127,7 +128,7 @@ export async function adminDeleteUser(adminId, uid) {
|
|
|
127
128
|
export async function adminUpdateStoreStatus(adminId, input) {
|
|
128
129
|
const { uid, action } = input;
|
|
129
130
|
if (!uid?.trim()) {
|
|
130
|
-
throw new ValidationError(
|
|
131
|
+
throw new ValidationError(ERR_UID_REQUIRED);
|
|
131
132
|
}
|
|
132
133
|
const user = await userRepository.findById(uid);
|
|
133
134
|
if (!user) {
|
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
* Notification Domain Actions (appkit)
|
|
3
|
-
*
|
|
4
|
-
* Pure business functions — auth, rate-limiting, and Next.js specifics
|
|
5
|
-
* are handled by the calling server action in the consumer.
|
|
6
|
-
*/
|
|
7
|
-
import type { NotificationDocument } from "../schemas";
|
|
8
|
-
/**
|
|
9
|
-
* Mark a single notification as read.
|
|
10
|
-
*/
|
|
1
|
+
import type { NotificationDocument, NotificationCreateInput } from "../schemas";
|
|
11
2
|
export declare function markNotificationRead(id: string): Promise<void>;
|
|
12
|
-
/**
|
|
13
|
-
* Mark all notifications as read for a given user.
|
|
14
|
-
* Returns the number of notifications updated.
|
|
15
|
-
*/
|
|
16
3
|
export declare function markAllNotificationsRead(userId: string): Promise<number>;
|
|
17
|
-
/**
|
|
18
|
-
* Delete a single notification by id.
|
|
19
|
-
*/
|
|
20
4
|
export declare function deleteNotification(id: string): Promise<void>;
|
|
21
|
-
/**
|
|
22
|
-
* List notifications for a user with unread count.
|
|
23
|
-
*/
|
|
24
5
|
export declare function listNotifications(userId: string, limit?: number): Promise<{
|
|
25
6
|
notifications: NotificationDocument[];
|
|
26
7
|
unreadCount: number;
|
|
27
8
|
}>;
|
|
9
|
+
export declare function getUnreadNotificationCount(userId: string): Promise<number>;
|
|
10
|
+
export interface SendNotificationInput extends NotificationCreateInput {
|
|
11
|
+
/** User's email — used by the email channel when enabled. */
|
|
12
|
+
userEmail?: string;
|
|
13
|
+
/** User's phone with country code, digits only — used by WhatsApp channel. */
|
|
14
|
+
userPhone?: string;
|
|
15
|
+
/** Pre-rendered email HTML — falls back to a plain-text <p> when absent. */
|
|
16
|
+
emailHtml?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SendNotificationResult {
|
|
19
|
+
notification: NotificationDocument;
|
|
20
|
+
email: "sent" | "skipped" | "failed";
|
|
21
|
+
whatsapp: "sent" | "skipped" | "failed";
|
|
22
|
+
}
|
|
28
23
|
/**
|
|
29
|
-
*
|
|
24
|
+
* Create an in-app notification and fan out to enabled external channels.
|
|
25
|
+
*
|
|
26
|
+
* In-app is always written. Email / WhatsApp are sent only when:
|
|
27
|
+
* 1. The channel is enabled in siteSettings.notificationChannels
|
|
28
|
+
* 2. Notification priority meets the channel's minPriority threshold
|
|
29
|
+
* 3. The optional `types` filter includes this notification type (or is absent)
|
|
30
|
+
* 4. The contact field (userEmail / userPhone) is supplied by the caller
|
|
31
|
+
* 5. The required credentials are set in siteSettings.credentials
|
|
30
32
|
*/
|
|
31
|
-
export declare function
|
|
33
|
+
export declare function sendNotification(input: SendNotificationInput): Promise<SendNotificationResult>;
|