@mohasinac/appkit 2.7.26 → 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/classified/ClassifiedDetailView.js +2 -2
- package/dist/_internal/client/features/digital-code/CodeRevealPanel.js +2 -2
- 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 +55 -84
- package/dist/features/layout/BottomActions.js +43 -48
- package/dist/features/layout/NavbarWithSettings.d.ts +6 -0
- package/dist/features/layout/NavbarWithSettings.js +11 -0
- 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 +2 -2
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { AuctionCard, AuctionCountdown } from "./AuctionCard";
|
|
2
1
|
export { MarketplaceAuctionCard, type MarketplaceAuctionCardData, type MarketplaceAuctionCardLabels, type MarketplaceAuctionCardProps, } from "./MarketplaceAuctionCard";
|
|
3
2
|
export { MarketplaceAuctionGrid, type MarketplaceAuctionGridLabels, type MarketplaceAuctionGridProps, } from "./MarketplaceAuctionGrid";
|
|
4
3
|
export { AuctionsListView } from "./AuctionsListView";
|
|
@@ -31,14 +31,14 @@ export declare const auctionItemSchema: z.ZodObject<{
|
|
|
31
31
|
}, "strip", z.ZodTypeAny, {
|
|
32
32
|
type: "video" | "image" | "file";
|
|
33
33
|
url: string;
|
|
34
|
-
source?: "
|
|
34
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
35
35
|
alt?: string | undefined;
|
|
36
36
|
thumbnailUrl?: string | undefined;
|
|
37
37
|
youtubeId?: string | undefined;
|
|
38
38
|
}, {
|
|
39
39
|
type: "video" | "image" | "file";
|
|
40
40
|
url: string;
|
|
41
|
-
source?: "
|
|
41
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
42
42
|
alt?: string | undefined;
|
|
43
43
|
thumbnailUrl?: string | undefined;
|
|
44
44
|
youtubeId?: string | undefined;
|
|
@@ -64,27 +64,27 @@ export declare const auctionItemSchema: z.ZodObject<{
|
|
|
64
64
|
updatedAt: string;
|
|
65
65
|
status: "draft" | "published" | "archived" | "sold";
|
|
66
66
|
title: string;
|
|
67
|
-
price: number;
|
|
68
67
|
featured: boolean;
|
|
69
68
|
listingType: "auction";
|
|
70
69
|
slug: string;
|
|
70
|
+
price: number;
|
|
71
71
|
auctionEndDate: string;
|
|
72
72
|
startingBid: number;
|
|
73
73
|
bidCount: number;
|
|
74
74
|
sellerId: string;
|
|
75
75
|
storeId?: string | undefined;
|
|
76
|
+
description?: string | undefined;
|
|
77
|
+
mainImage?: string | undefined;
|
|
78
|
+
images?: string[] | undefined;
|
|
79
|
+
currentBid?: number | undefined;
|
|
76
80
|
media?: {
|
|
77
81
|
type: "video" | "image" | "file";
|
|
78
82
|
url: string;
|
|
79
|
-
source?: "
|
|
83
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
80
84
|
alt?: string | undefined;
|
|
81
85
|
thumbnailUrl?: string | undefined;
|
|
82
86
|
youtubeId?: string | undefined;
|
|
83
87
|
}[] | undefined;
|
|
84
|
-
images?: string[] | undefined;
|
|
85
|
-
description?: string | undefined;
|
|
86
|
-
mainImage?: string | undefined;
|
|
87
|
-
currentBid?: number | undefined;
|
|
88
88
|
storeSlug?: string | undefined;
|
|
89
89
|
}, {
|
|
90
90
|
currency: string;
|
|
@@ -93,27 +93,27 @@ export declare const auctionItemSchema: z.ZodObject<{
|
|
|
93
93
|
updatedAt: string;
|
|
94
94
|
status: "draft" | "published" | "archived" | "sold";
|
|
95
95
|
title: string;
|
|
96
|
-
price: number;
|
|
97
96
|
featured: boolean;
|
|
98
97
|
listingType: "auction";
|
|
99
98
|
slug: string;
|
|
99
|
+
price: number;
|
|
100
100
|
auctionEndDate: string;
|
|
101
101
|
startingBid: number;
|
|
102
102
|
bidCount: number;
|
|
103
103
|
sellerId: string;
|
|
104
104
|
storeId?: string | undefined;
|
|
105
|
+
description?: string | undefined;
|
|
106
|
+
mainImage?: string | undefined;
|
|
107
|
+
images?: string[] | undefined;
|
|
108
|
+
currentBid?: number | undefined;
|
|
105
109
|
media?: {
|
|
106
110
|
type: "video" | "image" | "file";
|
|
107
111
|
url: string;
|
|
108
|
-
source?: "
|
|
112
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
109
113
|
alt?: string | undefined;
|
|
110
114
|
thumbnailUrl?: string | undefined;
|
|
111
115
|
youtubeId?: string | undefined;
|
|
112
116
|
}[] | undefined;
|
|
113
|
-
images?: string[] | undefined;
|
|
114
|
-
description?: string | undefined;
|
|
115
|
-
mainImage?: string | undefined;
|
|
116
|
-
currentBid?: number | undefined;
|
|
117
117
|
storeSlug?: string | undefined;
|
|
118
118
|
}>;
|
|
119
119
|
export declare const bidRecordSchema: z.ZodObject<{
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { getAdminAuth } from "../../../providers/db-firebase/index";
|
|
8
8
|
import { chatRepository } from "../../admin/repository/chat.repository";
|
|
9
|
+
import { conversationsRepository } from "../../messages/repository/conversations.repository";
|
|
10
|
+
import { storeRepository } from "../../stores/repository/store.repository";
|
|
9
11
|
import { serverLogger } from "../../../monitoring/index";
|
|
10
12
|
export async function issueRealtimeToken(userId, userRole) {
|
|
11
13
|
let chatIds = {};
|
|
@@ -14,14 +16,27 @@ export async function issueRealtimeToken(userId, userRole) {
|
|
|
14
16
|
chatIds = Object.fromEntries(userChatIds.map((id) => [id, true]));
|
|
15
17
|
}
|
|
16
18
|
catch (err) {
|
|
17
|
-
serverLogger.warn("Could not resolve chatIds for realtime token", {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
serverLogger.warn("Could not resolve chatIds for realtime token", { userId, err });
|
|
20
|
+
}
|
|
21
|
+
let conversationIds = {};
|
|
22
|
+
try {
|
|
23
|
+
const buyerConvs = await conversationsRepository.listByBuyer(userId);
|
|
24
|
+
buyerConvs.forEach((c) => { conversationIds[c.id] = true; });
|
|
25
|
+
if (userRole === "seller") {
|
|
26
|
+
const store = await storeRepository.findByOwnerId(userId);
|
|
27
|
+
if (store?.id) {
|
|
28
|
+
const sellerConvs = await conversationsRepository.listByStore(store.id);
|
|
29
|
+
sellerConvs.forEach((c) => { conversationIds[c.id] = true; });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
serverLogger.warn("Could not resolve conversationIds for realtime token", { userId, err });
|
|
21
35
|
}
|
|
22
36
|
const customToken = await getAdminAuth().createCustomToken(userId, {
|
|
23
37
|
role: userRole,
|
|
24
38
|
chatIds,
|
|
39
|
+
conversationIds,
|
|
25
40
|
});
|
|
26
41
|
serverLogger.info("issueRealtimeToken: token issued", {
|
|
27
42
|
userId,
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useState } from "react";
|
|
4
4
|
import { Alert, Div, Heading, Label, Text } from "../../../ui";
|
|
5
|
+
function renderRegisterFormFields(props) {
|
|
6
|
+
const { values, setValues, labels, renderTermsLink, renderPasswordStrength } = props;
|
|
7
|
+
const CLS_INPUT = "w-full rounded-lg border border-neutral-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-zinc-900 dark:text-zinc-100 placeholder:text-neutral-400 dark:placeholder:text-slate-500 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary";
|
|
8
|
+
const CLS_LABEL = "block text-sm font-medium mb-1";
|
|
9
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Div, { children: [_jsx(Label, { htmlFor: "reg-name", className: CLS_LABEL, children: labels?.displayNameLabel ?? "Full name" }), _jsx("input", { id: "reg-name", name: "displayName", type: "text", autoComplete: "name", required: true, placeholder: labels?.displayNamePlaceholder ?? "Your name", value: values.displayName, onChange: (e) => setValues({ ...values, displayName: e.target.value }), className: "w-full rounded-lg border border-neutral-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary" })] }), _jsxs(Div, { children: [_jsx(Label, { htmlFor: "reg-email", className: CLS_LABEL, children: labels?.emailLabel ?? "Email address" }), _jsx("input", { id: "reg-email", name: "email", type: "email", autoComplete: "username", required: true, placeholder: labels?.emailPlaceholder ?? "you@example.com", value: values.email, onChange: (e) => setValues({ ...values, email: e.target.value }), className: CLS_INPUT })] }), _jsxs(Div, { children: [_jsx(Label, { htmlFor: "reg-password", className: CLS_LABEL, children: labels?.passwordLabel ?? "Password" }), _jsx("input", { id: "reg-password", name: "password", type: "password", autoComplete: "new-password", required: true, placeholder: labels?.passwordPlaceholder ?? "••••••••", value: values.password, onChange: (e) => setValues({ ...values, password: e.target.value }), className: CLS_INPUT }), renderPasswordStrength?.(values.password)] }), _jsxs(Div, { children: [_jsx(Label, { htmlFor: "reg-confirm", className: CLS_LABEL, children: labels?.confirmPasswordLabel ?? "Confirm password" }), _jsx("input", { id: "reg-confirm", name: "confirmPassword", type: "password", autoComplete: "new-password", required: true, placeholder: labels?.confirmPasswordPlaceholder ?? "••••••••", value: values.confirmPassword, onChange: (e) => setValues({ ...values, confirmPassword: e.target.value }), className: CLS_INPUT })] }), _jsxs(Div, { className: "flex items-start gap-2", children: [_jsx("input", { id: "reg-terms", type: "checkbox", required: true, checked: values.acceptTerms, onChange: (e) => setValues({ ...values, acceptTerms: e.target.checked }), className: "mt-0.5 h-4 w-4 rounded border-neutral-300 dark:border-slate-600 accent-primary" }), _jsxs(Label, { htmlFor: "reg-terms", className: "text-sm leading-snug", children: [labels?.acceptTermsLabel ?? "I accept the", " ", renderTermsLink?.()] })] })] }));
|
|
10
|
+
}
|
|
5
11
|
export function RegisterForm({ onSubmit, isLoading = false, error, success, labels = {}, renderSocialButtons, renderLoginLink, renderTermsLink, renderPasswordStrength, className = "", }) {
|
|
6
12
|
const [values, setValues] = useState({
|
|
7
13
|
email: "",
|
|
@@ -21,7 +27,5 @@ export function RegisterForm({ onSubmit, isLoading = false, error, success, labe
|
|
|
21
27
|
await onSubmit(values);
|
|
22
28
|
}, [onSubmit, values, labels.passwordMismatch]);
|
|
23
29
|
const displayError = error ?? validationError;
|
|
24
|
-
return (_jsx(Div, { className: `flex items-center justify-center min-h-[60vh] px-4 ${className}`, children: _jsxs(Div, { className: "max-w-md w-full space-y-6", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Heading, { level: 1, className: "text-3xl font-extrabold", children: labels.title ?? "Create Account" }), (labels.subtitle || renderLoginLink) && (_jsxs(Text, { className: "mt-2 text-sm text-neutral-600 dark:text-zinc-400", children: [labels.subtitle ?? "Already have an account?", " ", renderLoginLink?.()] }))] }), success && (_jsx(Alert, { variant: "success", compact: true, children: success })), displayError && (_jsx(Alert, { variant: "error", compact: true, children: displayError })), _jsxs("form", { className: "space-y-4", onSubmit: handleSubmit, noValidate: true, children: [
|
|
25
|
-
? (labels.submittingLabel ?? "Creating account…")
|
|
26
|
-
: (labels.submitLabel ?? "Create account") })] }), renderSocialButtons?.()] }) }));
|
|
30
|
+
return (_jsx(Div, { className: `flex items-center justify-center min-h-[60vh] px-4 ${className}`, children: _jsxs(Div, { className: "max-w-md w-full space-y-6", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Heading, { level: 1, className: "text-3xl font-extrabold", children: labels.title ?? "Create Account" }), (labels.subtitle || renderLoginLink) && (_jsxs(Text, { className: "mt-2 text-sm text-neutral-600 dark:text-zinc-400", children: [labels.subtitle ?? "Already have an account?", " ", renderLoginLink?.()] }))] }), success && (_jsx(Alert, { variant: "success", compact: true, children: success })), displayError && (_jsx(Alert, { variant: "error", compact: true, children: displayError })), _jsxs("form", { className: "space-y-4", onSubmit: handleSubmit, noValidate: true, children: [renderRegisterFormFields({ values, setValues, labels, renderTermsLink, renderPasswordStrength }), _jsx("button", { type: "submit", disabled: isLoading, className: "w-full rounded-lg bg-zinc-900 px-4 py-2.5 text-sm font-semibold text-white disabled:opacity-60 hover:bg-zinc-800 dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-zinc-200 transition-colors", children: isLoading ? (labels.submittingLabel ?? "Creating account…") : (labels.submitLabel ?? "Create account") })] }), renderSocialButtons?.()] }) }));
|
|
27
31
|
}
|
|
@@ -46,6 +46,8 @@ export declare function saveConsentOtp(uid: string, addressId: string, data: Rec
|
|
|
46
46
|
export declare function readConsentOtp(uid: string, addressId: string): Promise<ConsentOtpDoc | null>;
|
|
47
47
|
/** Partially update a consent OTP document. */
|
|
48
48
|
export declare function patchConsentOtp(uid: string, addressId: string, data: Record<string, unknown>): Promise<void>;
|
|
49
|
+
/** Build the consent OTP WhatsApp message body. */
|
|
50
|
+
export declare function buildConsentOtpWhatsApp(recipientName: string, code: string, siteName: string): string;
|
|
49
51
|
/** Build the consent OTP email subject and HTML body. */
|
|
50
52
|
export declare function buildConsentOtpEmail(recipientName: string, code: string, siteName: string): {
|
|
51
53
|
subject: string;
|
|
@@ -102,6 +102,14 @@ export async function patchConsentOtp(uid, addressId, data) {
|
|
|
102
102
|
const db = getAdminDb();
|
|
103
103
|
await consentOtpRef(db, uid, addressId).update(data);
|
|
104
104
|
}
|
|
105
|
+
/** Build the consent OTP WhatsApp message body. */
|
|
106
|
+
export function buildConsentOtpWhatsApp(recipientName, code, siteName) {
|
|
107
|
+
return (`*${siteName} — Consent OTP*\n\n` +
|
|
108
|
+
`You are shipping to *${recipientName}*.\n\n` +
|
|
109
|
+
`Your verification code is: *${code}*\n\n` +
|
|
110
|
+
`This code expires in ${CONSENT_OTP_EXPIRY_MINUTES} minutes. ` +
|
|
111
|
+
`If you did not request this, please ignore this message.`);
|
|
112
|
+
}
|
|
105
113
|
/** Build the consent OTP email subject and HTML body. */
|
|
106
114
|
export function buildConsentOtpEmail(recipientName, code, siteName) {
|
|
107
115
|
return {
|
|
@@ -98,6 +98,8 @@ export interface UserDocument {
|
|
|
98
98
|
hardBannedBy?: string;
|
|
99
99
|
/** Timestamp when user confirmed they read the scam awareness guide. Required before full platform access. */
|
|
100
100
|
scamAwarenessAcknowledgedAt?: Date;
|
|
101
|
+
/** User-controlled notification channel and type preferences. */
|
|
102
|
+
notificationPreferences?: import("../../account/types").NotificationPreferences;
|
|
101
103
|
}
|
|
102
104
|
export interface UserSoftBan {
|
|
103
105
|
action: BannedAction;
|
|
@@ -212,31 +212,24 @@ export const sessionQueryHelpers = {
|
|
|
212
212
|
};
|
|
213
213
|
export function parseUserAgent(userAgent) {
|
|
214
214
|
const ua = userAgent.toLowerCase();
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
else if (ua.includes("ios") || ua.includes("iphone") || ua.includes("ipad"))
|
|
234
|
-
os = "iOS";
|
|
235
|
-
let device = "Desktop";
|
|
236
|
-
if (ua.includes("mobile"))
|
|
237
|
-
device = "Mobile";
|
|
238
|
-
else if (ua.includes("tablet") || ua.includes("ipad"))
|
|
239
|
-
device = "Tablet";
|
|
215
|
+
const BROWSER_TOKENS = [
|
|
216
|
+
["chrome", "Chrome"],
|
|
217
|
+
["firefox", "Firefox"],
|
|
218
|
+
["safari", "Safari"],
|
|
219
|
+
["edge", "Edge"],
|
|
220
|
+
];
|
|
221
|
+
const browser = BROWSER_TOKENS.find(([tok]) => ua.includes(tok))?.[1] ?? "Unknown";
|
|
222
|
+
const OS_TOKENS = [
|
|
223
|
+
["windows", "Windows"],
|
|
224
|
+
["mac", "macOS"],
|
|
225
|
+
["linux", "Linux"],
|
|
226
|
+
["android", "Android"],
|
|
227
|
+
[["ios", "iphone", "ipad"], "iOS"],
|
|
228
|
+
];
|
|
229
|
+
const os = OS_TOKENS.find(([tok]) => Array.isArray(tok) ? tok.some((t) => ua.includes(t)) : ua.includes(tok))?.[1] ?? "Unknown";
|
|
230
|
+
const device = ua.includes("mobile") ? "Mobile"
|
|
231
|
+
: ua.includes("tablet") || ua.includes("ipad") ? "Tablet"
|
|
232
|
+
: "Desktop";
|
|
240
233
|
return { userAgent, browser, os, device };
|
|
241
234
|
}
|
|
242
235
|
export const SESSION_FIELDS = {
|
|
@@ -58,14 +58,14 @@ export declare const authUserSchema: z.ZodObject<{
|
|
|
58
58
|
}, "strip", z.ZodTypeAny, {
|
|
59
59
|
id: string;
|
|
60
60
|
displayName?: string | undefined;
|
|
61
|
-
role?: "
|
|
61
|
+
role?: "user" | "seller" | "moderator" | "employee" | "admin" | undefined;
|
|
62
62
|
email?: string | undefined;
|
|
63
63
|
photoURL?: string | undefined;
|
|
64
64
|
isEmailVerified?: boolean | undefined;
|
|
65
65
|
}, {
|
|
66
66
|
id: string;
|
|
67
67
|
displayName?: string | undefined;
|
|
68
|
-
role?: "
|
|
68
|
+
role?: "user" | "seller" | "moderator" | "employee" | "admin" | undefined;
|
|
69
69
|
email?: string | undefined;
|
|
70
70
|
photoURL?: string | undefined;
|
|
71
71
|
isEmailVerified?: boolean | undefined;
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { NextResponse } from "next/server.js";
|
|
12
12
|
import { getProviders } from "../../../../contracts";
|
|
13
|
+
const ERR_DB_NOT_CONFIGURED = "DB not configured";
|
|
14
|
+
const ERR_INTERNAL_SERVER_ERROR = "Internal server error";
|
|
13
15
|
export async function GET(request, { params }) {
|
|
14
16
|
try {
|
|
15
17
|
const { id } = await params;
|
|
@@ -18,7 +20,7 @@ export async function GET(request, { params }) {
|
|
|
18
20
|
}
|
|
19
21
|
const { db } = getProviders();
|
|
20
22
|
if (!db)
|
|
21
|
-
return NextResponse.json({ success: false, error:
|
|
23
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_CONFIGURED }, { status: 503 });
|
|
22
24
|
const repo = db.getRepository("before_after");
|
|
23
25
|
const item = await repo.findById(id);
|
|
24
26
|
if (!item) {
|
|
@@ -32,7 +34,7 @@ export async function GET(request, { params }) {
|
|
|
32
34
|
catch (error) {
|
|
33
35
|
return NextResponse.json({
|
|
34
36
|
success: false,
|
|
35
|
-
error: error instanceof Error ? error.message :
|
|
37
|
+
error: error instanceof Error ? error.message : ERR_INTERNAL_SERVER_ERROR,
|
|
36
38
|
}, { status: 500 });
|
|
37
39
|
}
|
|
38
40
|
}
|
|
@@ -45,7 +47,7 @@ export async function PATCH(request, { params }) {
|
|
|
45
47
|
const body = await request.json();
|
|
46
48
|
const { db } = getProviders();
|
|
47
49
|
if (!db)
|
|
48
|
-
return NextResponse.json({ success: false, error:
|
|
50
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_CONFIGURED }, { status: 503 });
|
|
49
51
|
const repo = db.getRepository("before_after");
|
|
50
52
|
const updated = await repo.update(id, body);
|
|
51
53
|
if (!updated) {
|
|
@@ -59,7 +61,7 @@ export async function PATCH(request, { params }) {
|
|
|
59
61
|
catch (error) {
|
|
60
62
|
return NextResponse.json({
|
|
61
63
|
success: false,
|
|
62
|
-
error: error instanceof Error ? error.message :
|
|
64
|
+
error: error instanceof Error ? error.message : ERR_INTERNAL_SERVER_ERROR,
|
|
63
65
|
}, { status: 500 });
|
|
64
66
|
}
|
|
65
67
|
}
|
|
@@ -71,7 +73,7 @@ export async function DELETE(request, { params }) {
|
|
|
71
73
|
}
|
|
72
74
|
const { db } = getProviders();
|
|
73
75
|
if (!db)
|
|
74
|
-
return NextResponse.json({ success: false, error:
|
|
76
|
+
return NextResponse.json({ success: false, error: ERR_DB_NOT_CONFIGURED }, { status: 503 });
|
|
75
77
|
const repo = db.getRepository("before_after");
|
|
76
78
|
await repo.delete(id);
|
|
77
79
|
return NextResponse.json({
|
|
@@ -82,7 +84,7 @@ export async function DELETE(request, { params }) {
|
|
|
82
84
|
catch (error) {
|
|
83
85
|
return NextResponse.json({
|
|
84
86
|
success: false,
|
|
85
|
-
error: error instanceof Error ? error.message :
|
|
87
|
+
error: error instanceof Error ? error.message : ERR_INTERNAL_SERVER_ERROR,
|
|
86
88
|
}, { status: 500 });
|
|
87
89
|
}
|
|
88
90
|
}
|
|
@@ -27,8 +27,8 @@ export declare const beforeAfterItemSchema: z.ZodObject<{
|
|
|
27
27
|
title: string;
|
|
28
28
|
isActive: boolean;
|
|
29
29
|
createdAt?: string | undefined;
|
|
30
|
-
productId?: string | undefined;
|
|
31
30
|
description?: string | undefined;
|
|
31
|
+
productId?: string | undefined;
|
|
32
32
|
concern?: string | undefined;
|
|
33
33
|
beforeImageUrl?: string | undefined;
|
|
34
34
|
afterImageUrl?: string | undefined;
|
|
@@ -38,9 +38,9 @@ export declare const beforeAfterItemSchema: z.ZodObject<{
|
|
|
38
38
|
title: string;
|
|
39
39
|
createdAt?: string | undefined;
|
|
40
40
|
sortOrder?: number | undefined;
|
|
41
|
+
description?: string | undefined;
|
|
41
42
|
isActive?: boolean | undefined;
|
|
42
43
|
productId?: string | undefined;
|
|
43
|
-
description?: string | undefined;
|
|
44
44
|
concern?: string | undefined;
|
|
45
45
|
beforeImageUrl?: string | undefined;
|
|
46
46
|
afterImageUrl?: string | undefined;
|
|
@@ -73,9 +73,9 @@ export declare const createBlogPostSchema: z.ZodObject<{
|
|
|
73
73
|
title: string;
|
|
74
74
|
content: string;
|
|
75
75
|
category: "news" | "tips" | "guides" | "updates" | "community";
|
|
76
|
+
slug: string;
|
|
76
77
|
tags: string[];
|
|
77
78
|
isFeatured: boolean;
|
|
78
|
-
slug: string;
|
|
79
79
|
excerpt: string;
|
|
80
80
|
readTimeMinutes: number;
|
|
81
81
|
contentImages: {
|
|
@@ -212,9 +212,9 @@ export declare const updateBlogPostSchema: z.ZodObject<{
|
|
|
212
212
|
title?: string | undefined;
|
|
213
213
|
content?: string | undefined;
|
|
214
214
|
category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
|
|
215
|
+
slug?: string | undefined;
|
|
215
216
|
tags?: string[] | undefined;
|
|
216
217
|
isFeatured?: boolean | undefined;
|
|
217
|
-
slug?: string | undefined;
|
|
218
218
|
publishedAt?: string | undefined;
|
|
219
219
|
coverImage?: {
|
|
220
220
|
type: "video" | "image" | "file";
|
|
@@ -249,9 +249,9 @@ export declare const updateBlogPostSchema: z.ZodObject<{
|
|
|
249
249
|
title?: string | undefined;
|
|
250
250
|
content?: string | undefined;
|
|
251
251
|
category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
|
|
252
|
+
slug?: string | undefined;
|
|
252
253
|
tags?: string[] | undefined;
|
|
253
254
|
isFeatured?: boolean | undefined;
|
|
254
|
-
slug?: string | undefined;
|
|
255
255
|
publishedAt?: string | undefined;
|
|
256
256
|
coverImage?: string | {
|
|
257
257
|
type: "video" | "image" | "file";
|
|
@@ -4,13 +4,32 @@ import { useState, useCallback, useMemo } from "react";
|
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBlogPosts } from "../hooks/useBlog";
|
|
7
|
-
import { Pagination,
|
|
7
|
+
import { ListingToolbar, Pagination, Text } from "../../../ui";
|
|
8
8
|
import { ROUTES } from "../../../next";
|
|
9
9
|
import { BlogCard } from "./BlogListView";
|
|
10
10
|
import { BlogFilters, BLOG_PUBLIC_SORT_OPTIONS } from "./BlogFilters";
|
|
11
11
|
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
12
12
|
import { sortBy } from "../../../constants/sort";
|
|
13
13
|
import { BLOG_FIELDS } from "../../../constants/field-names";
|
|
14
|
+
function renderBlogGrid(props) {
|
|
15
|
+
const { isLoading, posts, view } = props;
|
|
16
|
+
if (isLoading) {
|
|
17
|
+
return (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-5 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] })] }, i))) }));
|
|
18
|
+
}
|
|
19
|
+
if (posts.length === 0) {
|
|
20
|
+
return _jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No posts found." });
|
|
21
|
+
}
|
|
22
|
+
if (view === "list") {
|
|
23
|
+
return (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: posts.map((post) => _jsx(BlogCard, { post: post, href: String(ROUTES.BLOG.ARTICLE(post.slug)) }, post.id)) }));
|
|
24
|
+
}
|
|
25
|
+
return (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: posts.map((post) => _jsx(BlogCard, { post: post, href: String(ROUTES.BLOG.ARTICLE(post.slug)) }, post.id)) }));
|
|
26
|
+
}
|
|
27
|
+
function renderBlogFilterDrawer(props) {
|
|
28
|
+
const { filterOpen, setFilterOpen, activeFilterCount, clearFilters, applyFilters, pendingTable } = props;
|
|
29
|
+
if (!filterOpen)
|
|
30
|
+
return null;
|
|
31
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(BlogFilters, { table: pendingTable, variant: "public" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
32
|
+
}
|
|
14
33
|
const PAGE_SIZE = 24;
|
|
15
34
|
const DEFAULT_SORT = sortBy(BLOG_FIELDS.PUBLISHED_AT);
|
|
16
35
|
const FILTER_KEYS = [TABLE_KEYS.CATEGORY, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO];
|
|
@@ -89,5 +108,5 @@ export function BlogIndexListing({ initialData }) {
|
|
|
89
108
|
const commitSearch = useCallback(() => {
|
|
90
109
|
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
91
110
|
}, [searchInput, table]);
|
|
92
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search posts...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: BLOG_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children:
|
|
111
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search posts...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: BLOG_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: renderBlogGrid({ isLoading, posts, view }) }), renderBlogFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, applyFilters, pendingTable })] }));
|
|
93
112
|
}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Div, Input, Label, RichTextEditor, Select, Stack, TagInput, Text, Textarea, } from "../../../ui";
|
|
3
3
|
import { Checkbox } from "../../forms/index";
|
|
4
4
|
import { MediaUploadField, MediaUploadList } from "../../media/index";
|
|
5
5
|
import { coerceMediaFieldArray, getMediaUrl, } from "../../media/types/index";
|
|
6
6
|
import { normalizeRichTextHtml, slugify } from "../../../utils/string.formatter";
|
|
7
|
+
function renderBlogPostMetaFields(props) {
|
|
8
|
+
const { value, update, isReadonly, categoryOptions, statusOptions, labels } = props;
|
|
9
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "grid gap-4 md:grid-cols-2", children: [_jsx(Select, { label: labels.category, value: value.category, onValueChange: (category) => update({ category }), options: categoryOptions, disabled: isReadonly }), _jsx(Select, { label: labels.status, value: value.status, onValueChange: (status) => update({ status }), options: statusOptions, disabled: isReadonly })] }), _jsxs(Div, { className: "grid gap-4 md:grid-cols-2", children: [_jsx(TagInput, { label: labels.tags, value: value.tags ?? [], onChange: (tags) => update({ tags }), disabled: isReadonly }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { htmlFor: "blog-post-read-time", children: labels.readTime }), _jsx(Input, { id: "blog-post-read-time", type: "number", value: String(value.readTimeMinutes ?? 5), onChange: (event) => update({ readTimeMinutes: parseInt(event.target.value, 10) || 5 }), disabled: isReadonly })] })] }), _jsx(Checkbox, { label: labels.featured, checked: value.isFeatured || false, onChange: (event) => update({ isFeatured: event.target.checked }), disabled: isReadonly })] }));
|
|
10
|
+
}
|
|
11
|
+
function renderBlogPostMediaFields(props) {
|
|
12
|
+
const { value, update, isReadonly, labels, onUploadCover, onUploadContentImage, onUploadAdditionalImage, onAbort, coverImageUrl, contentImages, additionalImages } = props;
|
|
13
|
+
return (_jsxs(_Fragment, { children: [_jsx(MediaUploadField, { label: labels.coverImage, value: coverImageUrl || "", onChange: (url) => update({ coverImage: url ? { url, type: "image" } : null }), onChangeField: (media) => update({ coverImage: media }), onUpload: onUploadCover, onAbort: onAbort, accept: "image/*", maxSizeMB: 10, disabled: isReadonly, helperText: labels.coverImageHelper }), _jsx(MediaUploadList, { label: labels.contentImages, value: contentImages, onChange: (media) => update({ contentImages: media }), onUpload: onUploadContentImage, onAbort: onAbort, accept: "image/*", maxItems: 10, maxSizeMB: 10, disabled: isReadonly, helperText: labels.contentImagesHelper }), _jsx(MediaUploadList, { label: labels.additionalImages, value: additionalImages, onChange: (media) => update({ additionalImages: media }), onUpload: onUploadAdditionalImage, onAbort: onAbort, accept: "image/*", maxItems: 5, maxSizeMB: 10, disabled: isReadonly, helperText: labels.additionalImagesHelper }), isReadonly && coverImageUrl && _jsx(Text, { size: "xs", variant: "secondary", children: coverImageUrl })] }));
|
|
14
|
+
}
|
|
7
15
|
export function BlogPostForm({ value, onChange, categoryOptions, statusOptions, labels, onUploadCover, onUploadContentImage, onUploadAdditionalImage, onAbort, isReadonly = false, renderContentField, renderContentReadonly, }) {
|
|
8
|
-
const coverImageUrl = getMediaUrl(value.coverImage);
|
|
16
|
+
const coverImageUrl = getMediaUrl(value.coverImage) ?? null;
|
|
9
17
|
const contentImages = coerceMediaFieldArray(value.contentImages);
|
|
10
18
|
const additionalImages = coerceMediaFieldArray(value.additionalImages);
|
|
11
19
|
const update = (partial) => {
|
|
@@ -22,7 +30,5 @@ export function BlogPostForm({ value, onChange, categoryOptions, statusOptions,
|
|
|
22
30
|
value: value.content || "",
|
|
23
31
|
onChange: (content) => update({ content }),
|
|
24
32
|
isReadonly,
|
|
25
|
-
})) : isReadonly && renderContentReadonly ? (renderContentReadonly(value.content || "")) : (_jsxs(Stack, { gap: "xs", children: [_jsx(RichTextEditor, { value: normalizeRichTextHtml(value.content || ""), onChange: (content) => update({ content }), disabled: isReadonly, minHeightClassName: "min-h-[240px]", placeholder: "Write blog content" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Rich text is supported for blog content." })] }))] }),
|
|
26
|
-
readTimeMinutes: parseInt(event.target.value, 10) || 5,
|
|
27
|
-
}), disabled: isReadonly })] })] }), _jsx(Checkbox, { label: labels.featured, checked: value.isFeatured || false, onChange: (event) => update({ isFeatured: event.target.checked }), disabled: isReadonly }), isReadonly && coverImageUrl && (_jsx(Text, { size: "xs", variant: "secondary", children: coverImageUrl }))] }));
|
|
33
|
+
})) : isReadonly && renderContentReadonly ? (renderContentReadonly(value.content || "")) : (_jsxs(Stack, { gap: "xs", children: [_jsx(RichTextEditor, { value: normalizeRichTextHtml(value.content || ""), onChange: (content) => update({ content }), disabled: isReadonly, minHeightClassName: "min-h-[240px]", placeholder: "Write blog content" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Rich text is supported for blog content." })] }))] }), renderBlogPostMediaFields({ value, update, isReadonly, labels, onUploadCover, onUploadContentImage, onUploadAdditionalImage, onAbort, coverImageUrl, contentImages, additionalImages }), renderBlogPostMetaFields({ value, update, isReadonly, categoryOptions, statusOptions, labels })] }));
|
|
28
34
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { Div, Heading, RichText, Row, Span, Text } from "../../../ui";
|
|
5
5
|
import { useBlogPost } from "../hooks/useBlog";
|
|
@@ -15,6 +15,14 @@ const CATEGORY_BADGE = {
|
|
|
15
15
|
updates: "bg-purple-100 text-purple-800",
|
|
16
16
|
community: "bg-orange-100 text-orange-800",
|
|
17
17
|
};
|
|
18
|
+
function renderBlogPostHeader(post, date, labels) {
|
|
19
|
+
return (_jsxs(Div, { className: "mb-8", children: [_jsxs(Row, { className: "gap-2 mb-4", children: [_jsx(Span, { className: `inline-block px-2 py-0.5 rounded-full text-xs font-medium capitalize ${CATEGORY_BADGE[post.category] ?? ""}`, children: post.category }), post.isFeatured && _jsx(Span, { className: "bg-yellow-100 text-yellow-800 px-2 py-0.5 rounded-full text-xs font-medium", children: labels?.featured ?? "Featured" })] }), _jsx(Heading, { level: 1, className: "text-3xl font-bold mb-4", children: post.title }), post.excerpt && _jsx(Text, { className: "text-lg text-neutral-500 mb-6", children: post.excerpt }), _jsxs(Row, { wrap: true, gap: "md", className: "text-sm text-neutral-400", children: [post.authorName && _jsxs(Span, { children: [labels?.author ?? "By", " ", _jsx(Span, { className: "font-medium text-neutral-700", children: post.authorName })] }), post.readTimeMinutes != null && _jsxs(Span, { children: [post.readTimeMinutes, " ", labels?.readTime ?? "min read"] }), date && _jsxs(Span, { children: [labels?.publishedOn ?? "Published", " ", date] }), post.views != null && _jsxs(Span, { children: [post.views, " ", labels?.viewsLabel ?? "views"] })] })] }));
|
|
20
|
+
}
|
|
21
|
+
function renderBlogPostRelated(related, labels, renderRelatedCard) {
|
|
22
|
+
if (related.length === 0)
|
|
23
|
+
return null;
|
|
24
|
+
return (_jsxs(Div, { children: [_jsx(Heading, { level: 2, className: "text-xl font-semibold mb-6", children: labels?.relatedTitle ?? "Related Posts" }), _jsx(Div, { className: "grid sm:grid-cols-3 gap-6", children: related.map((rel, i) => renderRelatedCard ? (_jsx(React.Fragment, { children: renderRelatedCard(rel, i) }, rel.id)) : (_jsx(BlogCard, { post: rel, href: String(ROUTES.BLOG.ARTICLE(rel.slug)) }, rel.id))) })] }));
|
|
25
|
+
}
|
|
18
26
|
export function BlogPostView({ slug, initialData, labels = {}, renderImage, renderContent, renderBackButton, renderLoading, renderError, renderRelatedCard, renderAuthorBio, className = "", }) {
|
|
19
27
|
const { post, related, isLoading, error } = useBlogPost(slug, {
|
|
20
28
|
initialData,
|
|
@@ -37,5 +45,5 @@ export function BlogPostView({ slug, initialData, labels = {}, renderImage, rend
|
|
|
37
45
|
})
|
|
38
46
|
: "";
|
|
39
47
|
const coverImageUrl = getMediaUrl(post.coverImage);
|
|
40
|
-
return (_jsxs(Div, { className: `min-h-screen ${className}`, children: [coverImageUrl && (_jsxs(Div, { className: "relative h-72 md:h-96 overflow-hidden", children: [renderImage ? (renderImage(post)) : (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-cover bg-center", style: { backgroundImage: `url(${coverImageUrl})` } })), _jsx(Div, { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" })] })), _jsxs(Div, { className: "max-w-3xl mx-auto px-4 py-12", children: [
|
|
48
|
+
return (_jsxs(Div, { className: `min-h-screen ${className}`, children: [coverImageUrl && (_jsxs(Div, { className: "relative h-72 md:h-96 overflow-hidden", children: [renderImage ? (renderImage(post)) : (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-cover bg-center", style: { backgroundImage: `url(${coverImageUrl})` } })), _jsx(Div, { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" })] })), _jsxs(Div, { className: "max-w-3xl mx-auto px-4 py-12", children: [renderBlogPostHeader(post, date, labels), post.tags && post.tags.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mb-8", children: post.tags.map((tag) => (_jsxs(Span, { className: "inline-block px-3 py-1 rounded-full bg-neutral-100 text-neutral-600 text-xs font-medium", children: ["#", tag] }, tag))) })), renderAuthorBio && _jsx(Div, { className: "mb-6", children: renderAuthorBio(post) }), _jsx(Div, { className: "bg-white dark:bg-slate-900 rounded-xl border border-neutral-200 dark:border-slate-700 p-8 mb-12", children: renderContent ? renderContent(post) : (_jsx(RichText, { html: normalizeRichTextHtml(post.content ?? ""), proseClass: "prose max-w-none dark:prose-invert", className: "text-neutral-800 dark:text-neutral-100" })) }), renderBlogPostRelated(related, labels, renderRelatedCard), renderBackButton && _jsx(Div, { className: "mt-10 pt-8 border-t border-neutral-200", children: renderBackButton() })] })] }));
|
|
41
49
|
}
|