@mohasinac/appkit 2.7.27 → 2.7.31
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/filters/filter-load-options.d.ts +29 -0
- package/dist/_internal/client/features/filters/filter-load-options.js +98 -0
- 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 +504 -9
- 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 +4 -0
- package/dist/client.js +2 -0
- package/dist/configs/next.js +4 -0
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/constants/field-names.d.ts +1 -0
- package/dist/constants/field-names.js +1 -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 +46 -16
- 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/AdminAddressEditorView.d.ts +7 -0
- package/dist/features/admin/components/AdminAddressEditorView.js +117 -0
- 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 +12 -9
- 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/AdminFeatureFlagsView.js +58 -13
- 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/AdminProductEditorView.d.ts +3 -1
- package/dist/features/admin/components/AdminProductEditorView.js +12 -9
- package/dist/features/admin/components/AdminProductsView.js +18 -12
- package/dist/features/admin/components/AdminReturnRequestsView.js +5 -4
- package/dist/features/admin/components/AdminReviewsView.js +36 -26
- 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/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- 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/AuctionBidsTable.d.ts +13 -0
- package/dist/features/auctions/components/AuctionBidsTable.js +62 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +15 -21
- package/dist/features/auctions/components/AuctionFilters.d.ts +7 -1
- package/dist/features/auctions/components/AuctionFilters.js +3 -2
- 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 +2 -1
- package/dist/features/auctions/components/index.js +1 -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/components/CartView.js +2 -1
- package/dist/features/cart/components/CheckoutView.js +2 -1
- 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.d.ts +5 -1
- package/dist/features/categories/components/BrandDetailTabs.js +30 -16
- 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.d.ts +5 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +31 -16
- 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 +12 -0
- package/dist/features/classified/components/ClassifiedFilters.js +20 -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 +12 -0
- package/dist/features/digital-codes/components/DigitalCodeFilters.js +17 -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/repository/event-entry.repository.d.ts +1 -0
- package/dist/features/events/repository/event-entry.repository.js +5 -0
- 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/filters/index.d.ts +2 -0
- package/dist/features/filters/index.js +1 -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 +12 -0
- package/dist/features/live/components/LiveItemFilters.js +24 -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 +29 -22
- package/dist/features/pre-orders/components/PreOrderFilters.d.ts +7 -1
- package/dist/features/pre-orders/components/PreOrderFilters.js +3 -2
- 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 +3 -3
- package/dist/features/products/components/AuctionsIndexListing.js +15 -51
- package/dist/features/products/components/BidHistory.js +2 -2
- package/dist/features/products/components/CompareOverlay.d.ts +2 -0
- 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.d.ts +7 -1
- package/dist/features/products/components/ProductFilters.js +11 -8
- package/dist/features/products/components/ProductForm.js +18 -16
- package/dist/features/products/components/ProductGalleryClient.js +2 -1
- package/dist/features/products/components/ProductGrid.js +12 -15
- package/dist/features/products/components/ProductsIndexListing.js +16 -52
- package/dist/features/products/components/ProductsIndexPageView.js +2 -2
- package/dist/features/products/repository/products.repository.d.ts +4 -0
- package/dist/features/products/repository/products.repository.js +22 -3
- package/dist/features/products/schemas/firestore.d.ts +7 -1
- 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/products/types/index.d.ts +4 -0
- 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 +18 -4
- 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 +27 -3
- package/dist/index.js +1743 -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 +41 -0
- package/dist/next/routing/route-map.js +17 -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/products-auctions-seed-data.js +40 -40
- package/dist/seed/products-preorders-seed-data.js +16 -16
- package/dist/seed/products-prize-draws-seed-data.js +4 -4
- package/dist/seed/products-standard-seed-data.js +202 -202
- 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
|
@@ -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
|
}
|
|
@@ -31,14 +31,14 @@ export declare const blogPostSchema: 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;
|
|
@@ -48,7 +48,7 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
48
48
|
}, string>]>>>, {
|
|
49
49
|
type: "video" | "image" | "file";
|
|
50
50
|
url: string;
|
|
51
|
-
source?: "
|
|
51
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
52
52
|
alt?: string | undefined;
|
|
53
53
|
thumbnailUrl?: string | undefined;
|
|
54
54
|
youtubeId?: string | undefined;
|
|
@@ -58,7 +58,7 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
58
58
|
} | null, string | {
|
|
59
59
|
type: "video" | "image" | "file";
|
|
60
60
|
url: string;
|
|
61
|
-
source?: "
|
|
61
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
62
62
|
alt?: string | undefined;
|
|
63
63
|
thumbnailUrl?: string | undefined;
|
|
64
64
|
youtubeId?: string | undefined;
|
|
@@ -73,14 +73,14 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
73
73
|
}, "strip", z.ZodTypeAny, {
|
|
74
74
|
type: "video" | "image" | "file";
|
|
75
75
|
url: string;
|
|
76
|
-
source?: "
|
|
76
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
77
77
|
alt?: string | undefined;
|
|
78
78
|
thumbnailUrl?: string | undefined;
|
|
79
79
|
youtubeId?: string | undefined;
|
|
80
80
|
}, {
|
|
81
81
|
type: "video" | "image" | "file";
|
|
82
82
|
url: string;
|
|
83
|
-
source?: "
|
|
83
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
84
84
|
alt?: string | undefined;
|
|
85
85
|
thumbnailUrl?: string | undefined;
|
|
86
86
|
youtubeId?: string | undefined;
|
|
@@ -95,14 +95,14 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
95
95
|
}, "strip", z.ZodTypeAny, {
|
|
96
96
|
type: "video" | "image" | "file";
|
|
97
97
|
url: string;
|
|
98
|
-
source?: "
|
|
98
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
99
99
|
alt?: string | undefined;
|
|
100
100
|
thumbnailUrl?: string | undefined;
|
|
101
101
|
youtubeId?: string | undefined;
|
|
102
102
|
}, {
|
|
103
103
|
type: "video" | "image" | "file";
|
|
104
104
|
url: string;
|
|
105
|
-
source?: "
|
|
105
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
106
106
|
alt?: string | undefined;
|
|
107
107
|
thumbnailUrl?: string | undefined;
|
|
108
108
|
youtubeId?: string | undefined;
|
|
@@ -130,7 +130,7 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
130
130
|
coverImage: {
|
|
131
131
|
type: "video" | "image" | "file";
|
|
132
132
|
url: string;
|
|
133
|
-
source?: "
|
|
133
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
134
134
|
alt?: string | undefined;
|
|
135
135
|
thumbnailUrl?: string | undefined;
|
|
136
136
|
youtubeId?: string | undefined;
|
|
@@ -141,7 +141,7 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
141
141
|
contentImages: {
|
|
142
142
|
type: "video" | "image" | "file";
|
|
143
143
|
url: string;
|
|
144
|
-
source?: "
|
|
144
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
145
145
|
alt?: string | undefined;
|
|
146
146
|
thumbnailUrl?: string | undefined;
|
|
147
147
|
youtubeId?: string | undefined;
|
|
@@ -149,7 +149,7 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
149
149
|
additionalImages: {
|
|
150
150
|
type: "video" | "image" | "file";
|
|
151
151
|
url: string;
|
|
152
|
-
source?: "
|
|
152
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
153
153
|
alt?: string | undefined;
|
|
154
154
|
thumbnailUrl?: string | undefined;
|
|
155
155
|
youtubeId?: string | undefined;
|
|
@@ -183,7 +183,7 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
183
183
|
coverImage?: string | {
|
|
184
184
|
type: "video" | "image" | "file";
|
|
185
185
|
url: string;
|
|
186
|
-
source?: "
|
|
186
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
187
187
|
alt?: string | undefined;
|
|
188
188
|
thumbnailUrl?: string | undefined;
|
|
189
189
|
youtubeId?: string | undefined;
|
|
@@ -196,7 +196,7 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
196
196
|
contentImages?: {
|
|
197
197
|
type: "video" | "image" | "file";
|
|
198
198
|
url: string;
|
|
199
|
-
source?: "
|
|
199
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
200
200
|
alt?: string | undefined;
|
|
201
201
|
thumbnailUrl?: string | undefined;
|
|
202
202
|
youtubeId?: string | undefined;
|
|
@@ -204,7 +204,7 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
204
204
|
additionalImages?: {
|
|
205
205
|
type: "video" | "image" | "file";
|
|
206
206
|
url: string;
|
|
207
|
-
source?: "
|
|
207
|
+
source?: "upload" | "youtube" | "external" | undefined;
|
|
208
208
|
alt?: string | undefined;
|
|
209
209
|
thumbnailUrl?: string | undefined;
|
|
210
210
|
youtubeId?: string | undefined;
|
|
@@ -226,16 +226,16 @@ export declare const blogListParamsSchema: z.ZodObject<{
|
|
|
226
226
|
sort?: string | undefined;
|
|
227
227
|
perPage?: number | undefined;
|
|
228
228
|
q?: string | undefined;
|
|
229
|
-
page?: number | undefined;
|
|
230
229
|
category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
|
|
231
|
-
tags?: string | undefined;
|
|
232
230
|
featured?: boolean | undefined;
|
|
231
|
+
tags?: string | undefined;
|
|
232
|
+
page?: number | undefined;
|
|
233
233
|
}, {
|
|
234
234
|
sort?: string | undefined;
|
|
235
235
|
perPage?: number | undefined;
|
|
236
236
|
q?: string | undefined;
|
|
237
|
-
page?: number | undefined;
|
|
238
237
|
category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
|
|
239
|
-
tags?: string | undefined;
|
|
240
238
|
featured?: boolean | undefined;
|
|
239
|
+
tags?: string | undefined;
|
|
240
|
+
page?: number | undefined;
|
|
241
241
|
}>;
|
|
@@ -2,14 +2,15 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Aside, Button, Div, Heading, Row, Span, Text } from "../../../ui";
|
|
3
3
|
import { formatCurrency } from "../../../utils/number.formatter";
|
|
4
4
|
import { THEME_CONSTANTS } from "../../../tokens";
|
|
5
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
5
6
|
export function CartItemRow({ item, onQtyChange, onRemove, href, isOutOfStock = false }) {
|
|
6
7
|
return (_jsxs(Div, { className: `flex gap-4 rounded-xl border bg-white dark:bg-slate-900 p-4 transition-opacity ${isOutOfStock ? "border-neutral-200 dark:border-slate-700 opacity-60" : "border-neutral-200 dark:border-slate-700"}`, children: [_jsx(Div, { className: "h-20 w-20 flex-shrink-0 overflow-hidden rounded-lg bg-neutral-100 dark:bg-slate-800", children: item.meta.image && (_jsx(Div, { role: "img", "aria-label": item.meta.title, className: "h-full w-full bg-center bg-cover", style: { backgroundImage: `url(${item.meta.image})` } })) }), _jsxs(Div, { className: "flex flex-1 flex-col justify-between", children: [_jsxs(Div, { className: "flex items-start gap-1.5", children: [href ? (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: `font-medium text-neutral-900 dark:text-zinc-100 hover:underline underline-offset-2 ${THEME_CONSTANTS.utilities.textClamp2}`, children: item.meta.title })) : (_jsx(Text, { className: `font-medium text-neutral-900 dark:text-zinc-100 ${THEME_CONSTANTS.utilities.textClamp2}`, children: item.meta.title })), isOutOfStock && (_jsx(Span, { className: "flex-shrink-0 rounded bg-red-100 dark:bg-red-900/30 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--appkit-color-error)]", children: "Out of Stock" }))] }), item.meta.attributes &&
|
|
7
8
|
Object.keys(item.meta.attributes).length > 0 && (_jsx(Text, { className: "text-xs text-neutral-500 dark:text-zinc-400", children: Object.entries(item.meta.attributes)
|
|
8
9
|
.map(([k, v]) => `${k}: ${v}`)
|
|
9
|
-
.join(", ") })), _jsxs(Row, { justify: "between", children: [_jsx(Text, { className: "font-semibold text-neutral-900 dark:text-zinc-100", children: formatCurrency(item.meta.price * item.quantity, item.meta.currency) }), onQtyChange && !isOutOfStock && (_jsxs(Row, { className: "gap-2", children: [_jsx(Button, { onClick: () => onQtyChange(item.id, item.quantity - 1), disabled: item.quantity <= 1, variant: "outline", size: "sm", className: "flex h-7 w-7 items-center justify-center rounded-full border border-neutral-200 dark:border-slate-600 text-sm disabled:opacity-40", children: "\u2212" }), _jsx(Span, { className: "min-w-[1.5rem] text-center text-sm", children: item.quantity }), _jsx(Button, { onClick: () => onQtyChange(item.id, item.quantity + 1), variant: "outline", size: "sm", className: "flex h-7 w-7 items-center justify-center rounded-full border border-neutral-200 dark:border-slate-600 text-sm", children: "+" })] })), isOutOfStock && (_jsxs(Span, { className: "text-xs text-neutral-400 dark:text-zinc-500", children: ["Qty: ", item.quantity] }))] })] }), onRemove && (_jsx(Button, { onClick: () => onRemove(item.id), variant: "ghost", size: "sm", "aria-label": "
|
|
10
|
+
.join(", ") })), _jsxs(Row, { justify: "between", children: [_jsx(Text, { className: "font-semibold text-neutral-900 dark:text-zinc-100", children: formatCurrency(item.meta.price * item.quantity, item.meta.currency) }), onQtyChange && !isOutOfStock && (_jsxs(Row, { className: "gap-2", children: [_jsx(Button, { onClick: () => onQtyChange(item.id, item.quantity - 1), disabled: item.quantity <= 1, variant: "outline", size: "sm", className: "flex h-7 w-7 items-center justify-center rounded-full border border-neutral-200 dark:border-slate-600 text-sm disabled:opacity-40", children: "\u2212" }), _jsx(Span, { className: "min-w-[1.5rem] text-center text-sm", children: item.quantity }), _jsx(Button, { onClick: () => onQtyChange(item.id, item.quantity + 1), variant: "outline", size: "sm", className: "flex h-7 w-7 items-center justify-center rounded-full border border-neutral-200 dark:border-slate-600 text-sm", children: "+" })] })), isOutOfStock && (_jsxs(Span, { className: "text-xs text-neutral-400 dark:text-zinc-500", children: ["Qty: ", item.quantity] }))] })] }), onRemove && (_jsx(Button, { onClick: () => onRemove(item.id), variant: "ghost", size: "sm", "aria-label": ACTIONS.CART["remove-item"].ariaLabel, className: "self-start text-neutral-400 dark:text-zinc-500 transition hover:text-red-500", children: "\u2715" }))] }));
|
|
10
11
|
}
|
|
11
12
|
export function CartDrawer({ isOpen, onClose, items, subtotal = 0, currency, isLoading, onQtyChange, onRemove, onCheckout, labels = {}, }) {
|
|
12
13
|
if (!isOpen)
|
|
13
14
|
return null;
|
|
14
|
-
return (_jsxs(_Fragment, { children: [_jsx(Div, { role: "presentation", className: "fixed inset-0 z-40 bg-black/40", onClick: onClose }), _jsxs(Aside, { className: "fixed inset-y-0 right-0 z-50 flex w-full max-w-sm flex-col bg-white dark:bg-slate-900 shadow-xl", children: [_jsxs(Row, { justify: "between", className: "border-b border-neutral-200 dark:border-slate-700 p-4", children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold", children: labels.title ?? "Cart" }), _jsx(Button, { onClick: onClose, variant: "ghost", size: "sm", "aria-label": "Close cart", className: "text-neutral-500 dark:text-zinc-400 hover:text-neutral-900 dark:hover:text-zinc-100", children: "\u2715" })] }), _jsx(Div, { className: "flex-1 overflow-y-auto p-4 space-y-3", children: isLoading ? (_jsx(Div, { className: "flex justify-center py-12", children: _jsx(Div, { className: "h-8 w-8 animate-spin rounded-full border-2 border-neutral-300 dark:border-slate-600 border-t-neutral-800 dark:border-t-zinc-200" }) })) : items.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-neutral-500 dark:text-zinc-400", children: labels.empty ?? "Your cart is empty" })) : (items.map((item) => (_jsx(CartItemRow, { item: item, onQtyChange: onQtyChange, onRemove: onRemove }, item.id)))) }), items.length > 0 && (_jsxs(Div, { className: "border-t border-neutral-200 dark:border-slate-700 p-4 space-y-4", children: [_jsxs(Row, { justify: "between", className: "text-sm", children: [_jsx(Span, { className: "text-neutral-600 dark:text-zinc-300", children: labels.subtotal ?? "Subtotal" }), _jsx(Span, { className: "font-semibold", children: formatCurrency(subtotal, currency) })] }), onCheckout && (_jsx(Button, { onClick: onCheckout, variant: "primary", className: "w-full rounded-xl bg-neutral-900 py-3 text-sm font-semibold text-white transition hover:bg-neutral-800", children: labels.checkout ?? "
|
|
15
|
+
return (_jsxs(_Fragment, { children: [_jsx(Div, { role: "presentation", className: "fixed inset-0 z-40 bg-black/40", onClick: onClose }), _jsxs(Aside, { className: "fixed inset-y-0 right-0 z-50 flex w-full max-w-sm flex-col bg-white dark:bg-slate-900 shadow-xl", children: [_jsxs(Row, { justify: "between", className: "border-b border-neutral-200 dark:border-slate-700 p-4", children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold", children: labels.title ?? "Cart" }), _jsx(Button, { onClick: onClose, variant: "ghost", size: "sm", "aria-label": "Close cart", className: "text-neutral-500 dark:text-zinc-400 hover:text-neutral-900 dark:hover:text-zinc-100", children: "\u2715" })] }), _jsx(Div, { className: "flex-1 overflow-y-auto p-4 space-y-3", children: isLoading ? (_jsx(Div, { className: "flex justify-center py-12", children: _jsx(Div, { className: "h-8 w-8 animate-spin rounded-full border-2 border-neutral-300 dark:border-slate-600 border-t-neutral-800 dark:border-t-zinc-200" }) })) : items.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-neutral-500 dark:text-zinc-400", children: labels.empty ?? "Your cart is empty" })) : (items.map((item) => (_jsx(CartItemRow, { item: item, onQtyChange: onQtyChange, onRemove: onRemove }, item.id)))) }), items.length > 0 && (_jsxs(Div, { className: "border-t border-neutral-200 dark:border-slate-700 p-4 space-y-4", children: [_jsxs(Row, { justify: "between", className: "text-sm", children: [_jsx(Span, { className: "text-neutral-600 dark:text-zinc-300", children: labels.subtotal ?? "Subtotal" }), _jsx(Span, { className: "font-semibold", children: formatCurrency(subtotal, currency) })] }), onCheckout && (_jsx(Button, { onClick: onCheckout, variant: "primary", className: "w-full rounded-xl bg-neutral-900 py-3 text-sm font-semibold text-white transition hover:bg-neutral-800", children: labels.checkout ?? ACTIONS.CART["checkout"].label }))] }))] })] }));
|
|
15
16
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Div, Heading } from "../../../ui";
|
|
3
|
+
import { AdSlot } from "../../homepage/components/AdSlot";
|
|
3
4
|
export function CartView({ labels = {}, renderItems, renderGroups, groups = [], renderSummary, renderPromoCode, renderCheckoutButton, renderEmpty, isEmpty = false, isLoading = false, className = "", }) {
|
|
4
5
|
if (isEmpty && renderEmpty)
|
|
5
6
|
return _jsx(_Fragment, { children: renderEmpty() });
|
|
6
|
-
return (_jsxs(Div, { className: className, children: [labels.title && (_jsx(Heading, { level: 1, className: "text-2xl font-bold mb-6", children: labels.title })), _jsxs(Div, { className: "flex flex-col gap-6 lg:flex-row lg:gap-8", children: [_jsxs(Div, { className: "flex-1", children: [renderGroups ? renderGroups(groups, isLoading) : renderItems(isLoading), !renderGroups && renderPromoCode?.(), renderGroups && renderPromoCode?.()] }), _jsxs(Div, { className: "w-full lg:w-80", children: [renderSummary?.(), renderCheckoutButton?.(() => { }, isLoading)] })] })] }));
|
|
7
|
+
return (_jsxs(Div, { className: className, children: [labels.title && (_jsx(Heading, { level: 1, className: "text-2xl font-bold mb-6", children: labels.title })), _jsxs(Div, { className: "flex flex-col gap-6 lg:flex-row lg:gap-8", children: [_jsxs(Div, { className: "flex-1", children: [renderGroups ? renderGroups(groups, isLoading) : renderItems(isLoading), !renderGroups && renderPromoCode?.(), renderGroups && renderPromoCode?.(), _jsx(AdSlot, { id: "cart-upsell", className: "mt-6" })] }), _jsxs(Div, { className: "w-full lg:w-80", children: [renderSummary?.(), renderCheckoutButton?.(() => { }, isLoading)] })] })] }));
|
|
7
8
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { Div, Heading } from "../../../ui";
|
|
5
|
+
import { AdSlot } from "../../homepage/components/AdSlot";
|
|
5
6
|
export function CheckoutView({ labels = {}, renderStep, renderStepIndicator, renderOrderSummary, totalSteps = 3, initialStep = 0, activeStep, onStepChange, className = "", }) {
|
|
6
7
|
const [internalStep, setInternalStep] = React.useState(initialStep);
|
|
7
8
|
const step = activeStep ?? internalStep;
|
|
@@ -14,5 +15,5 @@ export function CheckoutView({ labels = {}, renderStep, renderStepIndicator, ren
|
|
|
14
15
|
}
|
|
15
16
|
onStepChange?.(nextStep);
|
|
16
17
|
}, [activeStep, onStepChange, step]);
|
|
17
|
-
return (_jsxs(Div, { className: className, children: [labels.title && (_jsx(Heading, { level: 1, className: "text-2xl font-bold mb-6", children: labels.title })), renderStepIndicator?.(step, totalSteps, setStep), _jsxs(Div, { className: "flex flex-col gap-6 lg:flex-row lg:gap-8", children: [
|
|
18
|
+
return (_jsxs(Div, { className: className, children: [labels.title && (_jsx(Heading, { level: 1, className: "text-2xl font-bold mb-6", children: labels.title })), renderStepIndicator?.(step, totalSteps, setStep), _jsxs(Div, { className: "flex flex-col gap-6 lg:flex-row lg:gap-8", children: [_jsxs(Div, { className: "flex-1", children: [renderStep(step, setStep), _jsx(AdSlot, { id: "checkout-upsell", className: "mt-6" })] }), renderOrderSummary && (_jsx(Div, { className: "w-full lg:w-80", children: renderOrderSummary() }))] })] }));
|
|
18
19
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
2
|
import { DatabaseError, NotFoundError } from "../../../errors";
|
|
3
|
+
const ERR_CART_ITEM_NOT_FOUND = "Cart item not found";
|
|
3
4
|
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
4
5
|
import { decryptPii } from "../../../security";
|
|
5
6
|
import { CART_COLLECTION, } from "../schemas";
|
|
@@ -120,7 +121,7 @@ export class CartRepository extends BaseRepository {
|
|
|
120
121
|
throw new NotFoundError("Cart not found");
|
|
121
122
|
const itemIndex = cart.items.findIndex((item) => item.itemId === itemId);
|
|
122
123
|
if (itemIndex < 0)
|
|
123
|
-
throw new NotFoundError(
|
|
124
|
+
throw new NotFoundError(ERR_CART_ITEM_NOT_FOUND);
|
|
124
125
|
const items = [...cart.items];
|
|
125
126
|
items[itemIndex] = {
|
|
126
127
|
...items[itemIndex],
|
|
@@ -152,7 +153,7 @@ export class CartRepository extends BaseRepository {
|
|
|
152
153
|
throw new NotFoundError("Cart not found");
|
|
153
154
|
const itemExists = cart.items.some((item) => item.itemId === itemId);
|
|
154
155
|
if (!itemExists)
|
|
155
|
-
throw new NotFoundError(
|
|
156
|
+
throw new NotFoundError(ERR_CART_ITEM_NOT_FOUND);
|
|
156
157
|
const items = cart.items.filter((item) => item.itemId !== itemId);
|
|
157
158
|
const updatedCart = {
|
|
158
159
|
...cart,
|
|
@@ -223,7 +224,7 @@ export class CartRepository extends BaseRepository {
|
|
|
223
224
|
throw new NotFoundError("Cart not found");
|
|
224
225
|
const itemIndex = cart.items.findIndex((item) => item.itemId === itemId);
|
|
225
226
|
if (itemIndex < 0)
|
|
226
|
-
throw new NotFoundError(
|
|
227
|
+
throw new NotFoundError(ERR_CART_ITEM_NOT_FOUND);
|
|
227
228
|
const items = [...cart.items];
|
|
228
229
|
items[itemIndex] = {
|
|
229
230
|
...items[itemIndex],
|
|
@@ -20,7 +20,7 @@ export interface CartOp {
|
|
|
20
20
|
export interface WishlistOp {
|
|
21
21
|
op: WishlistOpKind;
|
|
22
22
|
itemId: string;
|
|
23
|
-
type: "product" | "auction" | "preorder" | "category" | "store";
|
|
23
|
+
type: "product" | "auction" | "preorder" | "category" | "store" | "classified" | "digital-code" | "live";
|
|
24
24
|
title?: string;
|
|
25
25
|
image?: string;
|
|
26
26
|
ts: number;
|
|
@@ -12,6 +12,7 @@ import { NextResponse } from "next/server.js";
|
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { getProviders } from "../../../../contracts";
|
|
14
14
|
import { createRouteHandler } from "../../../../next";
|
|
15
|
+
const ERR_CATEGORY_NOT_FOUND = "Category not found";
|
|
15
16
|
// ---- GET /api/categories/[id] ------------------------------------------------
|
|
16
17
|
export async function GET(_request, context) {
|
|
17
18
|
try {
|
|
@@ -23,7 +24,7 @@ export async function GET(_request, context) {
|
|
|
23
24
|
const repo = db.getRepository("categories");
|
|
24
25
|
const category = await repo.findById(id);
|
|
25
26
|
if (!category) {
|
|
26
|
-
return NextResponse.json({ success: false, error:
|
|
27
|
+
return NextResponse.json({ success: false, error: ERR_CATEGORY_NOT_FOUND }, { status: 404 });
|
|
27
28
|
}
|
|
28
29
|
return NextResponse.json({ success: true, data: category });
|
|
29
30
|
}
|
|
@@ -65,7 +66,7 @@ export const categoryItemPATCH = createRouteHandler({
|
|
|
65
66
|
const repo = db.getRepository("categories");
|
|
66
67
|
const category = await repo.findById(id);
|
|
67
68
|
if (!category)
|
|
68
|
-
return NextResponse.json({ success: false, error:
|
|
69
|
+
return NextResponse.json({ success: false, error: ERR_CATEGORY_NOT_FOUND }, { status: 404 });
|
|
69
70
|
const updated = await repo.update(id, {
|
|
70
71
|
...body,
|
|
71
72
|
updatedAt: new Date().toISOString(),
|
|
@@ -87,7 +88,7 @@ export const categoryItemDELETE = createRouteHandler({
|
|
|
87
88
|
const repo = db.getRepository("categories");
|
|
88
89
|
const category = await repo.findById(id);
|
|
89
90
|
if (!category)
|
|
90
|
-
return NextResponse.json({ success: false, error:
|
|
91
|
+
return NextResponse.json({ success: false, error: ERR_CATEGORY_NOT_FOUND }, { status: 404 });
|
|
91
92
|
// Guard: refuse to delete if it has children
|
|
92
93
|
if (category.childrenIds?.length) {
|
|
93
94
|
return NextResponse.json({ success: false, error: "Cannot delete a category that has children" }, { status: 409 });
|
|
@@ -16,6 +16,7 @@ import { NextResponse } from "next/server.js";
|
|
|
16
16
|
import { z } from "zod";
|
|
17
17
|
import { getProviders } from "../../../contracts";
|
|
18
18
|
import { createRouteHandler } from "../../../next";
|
|
19
|
+
const CACHE_CONTROL_PUBLIC = "public, max-age=3600, s-maxage=3600, stale-while-revalidate=86400";
|
|
19
20
|
function buildTreeFromFlat(items, rootId) {
|
|
20
21
|
const byId = new Map(items.map((i) => [i.id, i]));
|
|
21
22
|
// Roots: tier === 0 or no parentIds
|
|
@@ -130,7 +131,7 @@ export async function GET(request) {
|
|
|
130
131
|
});
|
|
131
132
|
const items = result.data.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
132
133
|
const res = NextResponse.json({ success: true, data: items });
|
|
133
|
-
res.headers.set("Cache-Control",
|
|
134
|
+
res.headers.set("Cache-Control", CACHE_CONTROL_PUBLIC);
|
|
134
135
|
return res;
|
|
135
136
|
}
|
|
136
137
|
// -- Tree mode (default or explicit ?tree=true) ----------------------------
|
|
@@ -143,7 +144,7 @@ export async function GET(request) {
|
|
|
143
144
|
if (tree === "true" || !flat) {
|
|
144
145
|
const treeNodes = buildTreeFromFlat(allResult.data, rootId);
|
|
145
146
|
const res = NextResponse.json({ success: true, data: treeNodes });
|
|
146
|
-
res.headers.set("Cache-Control",
|
|
147
|
+
res.headers.set("Cache-Control", CACHE_CONTROL_PUBLIC);
|
|
147
148
|
return res;
|
|
148
149
|
}
|
|
149
150
|
// Explicit ?flat=true on full set (non-filtered path, shouldn't reach here
|
|
@@ -152,7 +153,7 @@ export async function GET(request) {
|
|
|
152
153
|
success: true,
|
|
153
154
|
data: allResult.data.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)),
|
|
154
155
|
});
|
|
155
|
-
res.headers.set("Cache-Control",
|
|
156
|
+
res.headers.set("Cache-Control", CACHE_CONTROL_PUBLIC);
|
|
156
157
|
return res;
|
|
157
158
|
}
|
|
158
159
|
catch (error) {
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import { categoriesRepository, productRepository, } from "../../../repositories";
|
|
4
4
|
import { ROUTES } from "../../../next";
|
|
5
|
-
import { Container, Main, Section } from "../../../ui";
|
|
5
|
+
import { Container, Heading, Main, Section, Text } from "../../../ui";
|
|
6
6
|
import { BrandDetailTabs } from "./BrandDetailTabs";
|
|
7
7
|
export async function BrandDetailPageView({ slug, initialBrand }) {
|
|
8
8
|
const brand = (initialBrand ?? (await categoriesRepository
|
|
@@ -81,5 +81,5 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
|
|
|
81
81
|
counts.preOrders +
|
|
82
82
|
counts.prizeDraws +
|
|
83
83
|
counts.bundles;
|
|
84
|
-
return (_jsxs(Main, { children: [_jsxs(
|
|
84
|
+
return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative overflow-hidden ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover", style: { backgroundImage: `url(${coverImage})` } }), _jsx("div", { className: "absolute inset-0 bg-black/55" })] })), !hasCover && (_jsx("div", { className: "absolute inset-0 opacity-10", style: { backgroundColor: brandColor } })), _jsxs("div", { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs("nav", { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Home" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.BRANDS ?? "/brands"), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Brands" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx("span", { className: hasCover ? "text-white font-medium" : "text-zinc-900 dark:text-zinc-100 font-medium", children: brand?.name ?? slug })] }), _jsxs("div", { className: "flex items-center gap-4 mb-3", children: [brand?.display?.icon && (_jsx("span", { className: `text-5xl leading-none ${!hasCover ? "" : ""}`, children: brand.display.icon })), _jsxs(_Fragment, { children: [_jsx(Heading, { level: 1, className: `text-3xl md:text-4xl font-bold ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: brand?.name ?? slug }), brand?.description && typeof brand.description === "string" && !brand.description.startsWith("{") && (_jsx(Text, { className: `text-base max-w-2xl mt-1 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: brand.description }))] })] }), _jsxs("div", { className: "flex flex-wrap gap-2 mt-3", children: [counts.products > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [counts.products.toLocaleString(), " ", counts.products === 1 ? "product" : "products"] })), counts.auctions > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [counts.auctions.toLocaleString(), " ", counts.auctions === 1 ? "auction" : "auctions"] })), counts.preOrders > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [counts.preOrders.toLocaleString(), " ", counts.preOrders === 1 ? "pre-order" : "pre-orders"] })), totalItems === 0 && (_jsx("span", { className: `text-sm ${hasCover ? "text-white/60" : "text-zinc-400"}`, children: "No items listed yet" }))] })] })] }), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: brandName ? (_jsx(BrandDetailTabs, { brandName: brandName, initialProductsData: productsResult ?? undefined, initialBundles: brandBundles, counts: counts })) : (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "Brand not found." })) }) })] }));
|
|
85
85
|
}
|