@mohasinac/appkit 2.7.55 → 2.7.57
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/shared/actions/action-registry.js +31 -5
- package/dist/client.d.ts +1 -3
- package/dist/client.js +1 -2
- package/dist/constants/field-names.d.ts +3 -0
- package/dist/constants/field-names.js +3 -0
- package/dist/features/about/components/FAQPageView.js +2 -2
- package/dist/features/about/components/FeesView.js +1 -1
- package/dist/features/about/components/PolicyPageView.js +1 -1
- package/dist/features/about/components/PublicProfileView.js +1 -1
- package/dist/features/account/components/AddressesIndexListing.js +1 -1
- package/dist/features/account/components/MessagesView.js +1 -1
- package/dist/features/account/components/NotificationPreferencesPanel.js +12 -3
- package/dist/features/account/components/UserOffersPanel.js +2 -2
- package/dist/features/account/components/UserSidebar.js +3 -3
- package/dist/features/account/components/UserSupportView.js +1 -1
- package/dist/features/admin/components/AdminAllEventEntriesView.js +1 -1
- package/dist/features/admin/components/AdminBidsView.js +5 -1
- package/dist/features/admin/components/AdminBlogView.js +6 -1
- package/dist/features/admin/components/AdminBrandsView.js +6 -1
- package/dist/features/admin/components/AdminBundleEditorView.js +13 -9
- package/dist/features/admin/components/AdminBundlesView.js +3 -1
- package/dist/features/admin/components/AdminCarouselEditorView.js +7 -8
- package/dist/features/admin/components/AdminCarouselView.js +4 -2
- package/dist/features/admin/components/AdminCartsView.js +1 -1
- package/dist/features/admin/components/AdminCategoriesView.js +6 -1
- package/dist/features/admin/components/AdminContactView.js +3 -1
- package/dist/features/admin/components/AdminCouponsView.js +4 -2
- package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -1
- package/dist/features/admin/components/AdminFaqsView.js +3 -1
- package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -1
- package/dist/features/admin/components/AdminFeaturesView.js +4 -0
- package/dist/features/admin/components/AdminMediaView.js +10 -5
- package/dist/features/admin/components/AdminNavigationView.js +1 -1
- package/dist/features/admin/components/AdminNewsletterView.js +3 -1
- package/dist/features/admin/components/AdminNotificationsView.js +6 -1
- package/dist/features/admin/components/AdminOrdersView.js +10 -1
- package/dist/features/admin/components/AdminPayoutsView.js +5 -1
- package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -0
- package/dist/features/admin/components/AdminProductsView.js +13 -1
- package/dist/features/admin/components/AdminReviewsView.js +6 -1
- package/dist/features/admin/components/AdminScammersView.js +1 -1
- package/dist/features/admin/components/AdminSectionsView.js +1 -1
- package/dist/features/admin/components/AdminSessionsView.js +5 -1
- package/dist/features/admin/components/AdminSidebar.js +3 -3
- package/dist/features/admin/components/AdminSiteSettingsView.js +1 -1
- package/dist/features/admin/components/AdminStoreEditorView.js +1 -1
- package/dist/features/admin/components/AdminStoresView.js +6 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +1 -1
- package/dist/features/admin/components/AdminTeamView.js +3 -1
- package/dist/features/admin/components/AdminUserEditorView.js +1 -1
- package/dist/features/admin/components/AdminUsersView.js +5 -1
- package/dist/features/admin/components/AdminViewCards.js +2 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +9 -0
- package/dist/features/admin/constants/filter-tabs.js +3 -0
- package/dist/features/auctions/components/AuctionBottomActions.d.ts +7 -0
- package/dist/features/auctions/components/AuctionBottomActions.js +24 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +7 -7
- package/dist/features/auctions/components/AuctionsListView.js +1 -1
- package/dist/features/auctions/components/MarketplaceAuctionGrid.js +1 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +1 -1
- package/dist/features/auth/components/LoginForm.js +1 -1
- package/dist/features/auth/components/VerifyEmailView.js +2 -2
- package/dist/features/before-after/components/BeforeAfterGallery.js +1 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +1 -1
- package/dist/features/blog/components/BlogIndexListing.js +1 -1
- package/dist/features/blog/components/BlogListView.js +2 -2
- package/dist/features/blog/components/BlogPostView.js +8 -8
- package/dist/features/cart/components/CartDrawer.js +1 -1
- package/dist/features/cart/repository/cart.repository.js +11 -4
- package/dist/features/cart/schemas/firestore.d.ts +3 -0
- package/dist/features/categories/components/BrandDetailPageView.js +1 -1
- package/dist/features/categories/components/CategoriesIndexListing.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +39 -3
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +4 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +7 -2
- package/dist/features/categories/components/CategoryGrid.js +1 -1
- package/dist/features/categories/components/CategoryProductsView.js +1 -1
- package/dist/features/categories/components/CategoryStoresListing.d.ts +5 -0
- package/dist/features/categories/components/CategoryStoresListing.js +11 -0
- package/dist/features/categories/components/CategoryTableColumns.js +2 -2
- package/dist/features/categories/components/CategoryTree.js +1 -1
- package/dist/features/categories/components/index.d.ts +2 -0
- package/dist/features/categories/components/index.js +1 -0
- package/dist/features/checkout/components/CheckoutStepper.js +1 -1
- package/dist/features/classified/components/ClassifiedIndexListing.js +2 -0
- package/dist/features/classified/components/ClassifiedListView.js +1 -1
- package/dist/features/contact/components/ContactInfoSidebar.js +1 -1
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +2 -0
- package/dist/features/digital-codes/components/DigitalCodesListView.js +1 -1
- package/dist/features/events/components/AdminEventsView.js +5 -1
- package/dist/features/events/components/EventCard.js +1 -1
- package/dist/features/events/components/EventPollWidget.js +1 -1
- package/dist/features/events/components/EventStatusBadge.js +5 -5
- package/dist/features/events/components/EventsIndexListing.js +1 -1
- package/dist/features/events/components/EventsListView.js +2 -2
- package/dist/features/events/components/SpinWheelView.js +4 -2
- package/dist/features/filters/AsyncFacetSection.js +3 -3
- package/dist/features/filters/FilterFacetSection.js +5 -5
- package/dist/features/filters/RangeFilter.js +1 -1
- package/dist/features/forms/Input.js +2 -2
- package/dist/features/forms/Select.js +1 -1
- package/dist/features/forms/Slider.js +1 -1
- package/dist/features/forms/Textarea.js +1 -1
- package/dist/features/forms/utils.d.ts +1 -1
- package/dist/features/forms/utils.js +1 -1
- package/dist/features/history/hooks/useHistory.js +23 -10
- package/dist/features/homepage/components/AdvertisementBanner.js +2 -2
- package/dist/features/homepage/components/FeaturedResultsSection.js +1 -1
- package/dist/features/homepage/components/HeroSection.js +1 -1
- package/dist/features/homepage/components/NewsletterSection.js +1 -1
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/live/components/LiveItemsIndexListing.js +2 -0
- package/dist/features/live/components/LiveItemsListView.js +1 -1
- package/dist/features/orders/components/OrdersList.js +12 -12
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
- package/dist/features/pre-orders/components/PreOrderBadge.js +5 -5
- package/dist/features/pre-orders/components/PreOrderBottomActions.d.ts +5 -0
- package/dist/features/pre-orders/components/PreOrderBottomActions.js +22 -0
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +5 -5
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +47 -0
- package/dist/features/pre-orders/components/PreOrdersListView.js +1 -1
- package/dist/features/pre-orders/components/PreorderCard.js +3 -3
- package/dist/features/products/components/AuctionsIndexListing.js +23 -0
- package/dist/features/products/components/BidHistory.js +2 -2
- package/dist/features/products/components/CompareOverlay.js +1 -1
- package/dist/features/products/components/CustomFieldsEditor.js +1 -1
- package/dist/features/products/components/CustomSectionTabContent.js +1 -1
- package/dist/features/products/components/CustomSectionsEditor.js +1 -1
- package/dist/features/products/components/GroupSettingsPanel.js +2 -2
- package/dist/features/products/components/MakeOfferButton.js +1 -1
- package/dist/features/products/components/PrizeDrawBottomActions.d.ts +6 -0
- package/dist/features/products/components/PrizeDrawBottomActions.js +24 -0
- package/dist/features/products/components/PrizeDrawDetailPageView.js +3 -5
- package/dist/features/products/components/PrizeDrawEntryActions.js +5 -18
- package/dist/features/products/components/PrizeRevealModal.js +5 -2
- package/dist/features/products/components/ProductDetailActions.d.ts +6 -1
- package/dist/features/products/components/ProductDetailActions.js +36 -6
- package/dist/features/products/components/ProductDetailPageView.js +17 -16
- package/dist/features/products/components/ProductGalleryClient.js +1 -1
- package/dist/features/products/components/ProductGrid.js +6 -6
- package/dist/features/products/components/ProductTabs.js +1 -1
- package/dist/features/products/components/ProductTabsShell.js +1 -1
- package/dist/features/products/components/ProductsIndexListing.js +47 -0
- package/dist/features/products/components/RelatedProducts.js +1 -1
- package/dist/features/products/components/ShowGroupSection.js +1 -1
- package/dist/features/products/components/SublistingCarouselSection.js +1 -1
- package/dist/features/products/components/SublistingCategorySelect.js +1 -1
- package/dist/features/products/components/index.d.ts +0 -2
- package/dist/features/products/components/index.js +0 -1
- package/dist/features/products/constants/action-defs.d.ts +7 -0
- package/dist/features/products/constants/action-defs.js +14 -0
- package/dist/features/products/constants/listing-tabs.d.ts +6 -0
- package/dist/features/products/constants/listing-tabs.js +1 -0
- package/dist/features/promotions/components/CouponCard.js +2 -2
- package/dist/features/promotions/components/CouponsIndexListing.js +2 -2
- package/dist/features/reviews/components/ReviewDetailPageView.js +1 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +3 -3
- package/dist/features/reviews/components/ReviewModal.js +2 -2
- package/dist/features/reviews/components/ReviewSummary.js +1 -1
- package/dist/features/reviews/components/ReviewsIndexListing.js +1 -1
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +4 -3
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +1 -1
- package/dist/features/seller/components/SellerAuctionsView.js +10 -3
- package/dist/features/seller/components/SellerBidsView.js +22 -7
- package/dist/features/seller/components/SellerBundlesView.js +10 -3
- package/dist/features/seller/components/SellerClassifiedView.js +10 -3
- package/dist/features/seller/components/SellerCouponsView.js +13 -4
- package/dist/features/seller/components/SellerDigitalCodesView.js +10 -3
- package/dist/features/seller/components/SellerGoogleReviewsView.js +10 -5
- package/dist/features/seller/components/SellerGroupedListingsView.js +2 -0
- package/dist/features/seller/components/SellerLiveView.js +10 -3
- package/dist/features/seller/components/SellerOffersPanel.js +2 -2
- package/dist/features/seller/components/SellerOffersView.js +1 -1
- package/dist/features/seller/components/SellerOrdersView.js +40 -17
- package/dist/features/seller/components/SellerPayoutMethodsView.js +13 -4
- package/dist/features/seller/components/SellerPayoutsView.js +1 -1
- package/dist/features/seller/components/SellerPreOrdersView.js +7 -2
- package/dist/features/seller/components/SellerPrizeDrawsView.js +7 -2
- package/dist/features/seller/components/SellerProductShell.js +27 -9
- package/dist/features/seller/components/SellerProductsCards.js +2 -1
- package/dist/features/seller/components/SellerProductsView.js +21 -8
- package/dist/features/seller/components/SellerReviewsView.js +8 -0
- package/dist/features/seller/components/SellerShippingConfigsView.js +13 -4
- package/dist/features/seller/components/SellerSidebar.js +2 -2
- package/dist/features/seller/components/SellerStoreCategoriesView.js +10 -3
- package/dist/features/seller/components/SellerTemplatesView.js +19 -6
- package/dist/features/shell/StepForm.js +9 -3
- package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
- package/dist/features/site-settings/components/NavPermissionsManager.js +1 -1
- package/dist/features/stores/components/InteractiveStoreCard.js +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +1 -1
- package/dist/features/stores/components/StoreReviewsListing.js +1 -1
- package/dist/features/stores/components/StoresIndexListing.js +17 -1
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +1 -1
- package/dist/features/wishlist/components/WishlistPage.js +2 -2
- package/dist/features/wishlist/hooks/useWishlistToggle.js +6 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +6 -3
- package/dist/react/hooks/use-action-dispatch.d.ts +5 -0
- package/dist/react/hooks/use-action-dispatch.js +10 -0
- package/dist/seed/cart-seed-data.js +50 -3
- package/dist/seed/categories-seed-data.js +80 -16
- package/dist/seed/index.d.ts +3 -0
- package/dist/seed/index.js +3 -0
- package/dist/seed/manifest.js +15 -1
- package/dist/seed/products-classifieds-seed-data.d.ts +10 -0
- package/dist/seed/products-classifieds-seed-data.js +197 -0
- package/dist/seed/products-digital-codes-seed-data.d.ts +10 -0
- package/dist/seed/products-digital-codes-seed-data.js +204 -0
- package/dist/seed/products-live-items-seed-data.d.ts +13 -0
- package/dist/seed/products-live-items-seed-data.js +190 -0
- package/dist/styles.css +16 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/index.d.ts +3 -3
- package/dist/tokens/index.js +3 -3
- package/dist/ui/DataTable.js +3 -3
- package/dist/ui/components/Button.style.css +15 -0
- package/dist/ui/components/Input.js +1 -1
- package/dist/ui/components/ListingToolbar.js +1 -1
- package/dist/ui/components/Select.js +1 -1
- package/dist/ui/components/StatsGrid.js +1 -1
- package/dist/ui/index.d.ts +0 -4
- package/dist/ui/index.js +0 -2
- package/package.json +1 -1
- package/dist/_internal/client/features/seller/print-center/InventoryLabel.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/InventoryLabel.js +0 -49
- package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.d.ts +0 -7
- package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.js +0 -32
- package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.d.ts +0 -28
- package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.js +0 -33
- package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.js +0 -30
- package/dist/_internal/client/features/seller/print-center/PrintCenterView.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/PrintCenterView.js +0 -102
- package/dist/_internal/client/features/seller/print-center/PrintGrid.d.ts +0 -12
- package/dist/_internal/client/features/seller/print-center/PrintGrid.js +0 -13
- package/dist/_internal/client/features/seller/print-center/StoreCard.d.ts +0 -19
- package/dist/_internal/client/features/seller/print-center/StoreCard.js +0 -30
- package/dist/_internal/client/features/seller/print-center/WebsiteCard.d.ts +0 -11
- package/dist/_internal/client/features/seller/print-center/WebsiteCard.js +0 -25
- package/dist/_internal/client/features/seller/print-center/index.d.ts +0 -11
- package/dist/_internal/client/features/seller/print-center/index.js +0 -10
- package/dist/_internal/client/features/seller/print-center/types.d.ts +0 -20
- package/dist/_internal/client/features/seller/print-center/types.js +0 -20
- package/dist/_internal/client/features/seller/print-center/useInventoryPdf.d.ts +0 -18
- package/dist/_internal/client/features/seller/print-center/useInventoryPdf.js +0 -76
- package/dist/features/account/repository/address.repository.d.ts +0 -17
- package/dist/features/account/repository/address.repository.js +0 -184
- package/dist/features/auctions/components/AuctionCard.d.ts +0 -29
- package/dist/features/auctions/components/AuctionCard.js +0 -38
- package/dist/features/categories/components/BundleAddToCartCta.d.ts +0 -16
- package/dist/features/categories/components/BundleAddToCartCta.js +0 -54
- package/dist/features/categories/components/CategoriesListView.d.ts +0 -31
- package/dist/features/categories/components/CategoriesListView.js +0 -27
- package/dist/features/products/components/AuctionsView.d.ts +0 -6
- package/dist/features/products/components/AuctionsView.js +0 -5
- package/dist/features/products/components/BuyBar.d.ts +0 -21
- package/dist/features/products/components/BuyBar.js +0 -19
- package/dist/features/products/components/PreOrdersView.d.ts +0 -6
- package/dist/features/products/components/PreOrdersView.js +0 -5
- package/dist/features/reviews/components/ReviewsListView.d.ts +0 -31
- package/dist/features/reviews/components/ReviewsListView.js +0 -16
- package/dist/features/stores/components/StoresListView.d.ts +0 -19
- package/dist/features/stores/components/StoresListView.js +0 -25
- package/dist/features/stores/repository/store-address.repository.d.ts +0 -55
- package/dist/features/stores/repository/store-address.repository.js +0 -187
- package/dist/ui/components/BulkActionsBar.d.ts +0 -18
- package/dist/ui/components/BulkActionsBar.js +0 -25
- package/dist/ui/components/CollageLightbox.d.ts +0 -18
- package/dist/ui/components/CollageLightbox.js +0 -41
- package/dist/ui/components/StickyBottomBar.d.ts +0 -22
- package/dist/ui/components/StickyBottomBar.js +0 -20
|
@@ -9,7 +9,7 @@ import { normalizeRichTextHtml } from "../../../utils/string.formatter";
|
|
|
9
9
|
import { safeDisplayName } from "../../../security";
|
|
10
10
|
import { Button, Container, Div, Heading, Main, RichText, Row, Section, Span, Stack, Text, } from "../../../ui";
|
|
11
11
|
import { PreOrderDetailView } from "../../products/components/PreOrderDetailView";
|
|
12
|
-
import {
|
|
12
|
+
import { PreOrderBottomActions } from "./PreOrderBottomActions";
|
|
13
13
|
import { ProductTabsShell } from "../../products/components/ProductTabsShell";
|
|
14
14
|
import { CustomSectionTabContent } from "../../products/components/CustomSectionTabContent";
|
|
15
15
|
import { PreOrderActionsClient } from "./PreOrderActionsClient";
|
|
@@ -46,10 +46,10 @@ function PreOrderInfoSection({ title, productionStatus, maxPerUser, deliveryDate
|
|
|
46
46
|
codAvailable: "Cash on Delivery",
|
|
47
47
|
wishlistCount: (n) => `${n} wishlisted`,
|
|
48
48
|
categoryProductCount: (n, cat) => `${n} in ${cat}`,
|
|
49
|
-
} }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && (_jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-300 transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryName || category })), !category && categoryName && (_jsx(Span, { className: "inline-flex items-center rounded-full border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-xs font-medium text-zinc-500 dark:text-zinc-400", children: categoryName })), brand && brandSlug && (_jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-300 transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand })), brand && !brandSlug && (_jsx(Span, { className: "inline-flex items-center rounded-full border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-xs font-medium text-zinc-500 dark:text-zinc-400", children: brand }))] })), productFeatures && features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures })), !productFeatures && features.length > 0 && (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 px-4 py-3", children: [_jsx(Text, { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "About this product" }), _jsx("ul", { className: "space-y-1.5", children: features.map((f, i) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i))) })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-zinc-600 dark:text-zinc-400 line-clamp-4" })), safeSeller && (_jsx(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-3", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] uppercase tracking-wide text-zinc-400 dark:text-zinc-
|
|
49
|
+
} }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && (_jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-300 transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryName || category })), !category && categoryName && (_jsx(Span, { className: "inline-flex items-center rounded-full border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-xs font-medium text-zinc-500 dark:text-zinc-400", children: categoryName })), brand && brandSlug && (_jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-300 transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand })), brand && !brandSlug && (_jsx(Span, { className: "inline-flex items-center rounded-full border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-xs font-medium text-zinc-500 dark:text-zinc-400", children: brand }))] })), productFeatures && features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures })), !productFeatures && features.length > 0 && (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 px-4 py-3", children: [_jsx(Text, { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "About this product" }), _jsx("ul", { className: "space-y-1.5", children: features.map((f, i) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i))) })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-zinc-600 dark:text-zinc-400 line-clamp-4" })), safeSeller && (_jsx(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-3", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-0.5", children: "Sold by" }), _jsx(Text, { className: "text-sm font-semibold text-zinc-800 dark:text-zinc-200", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-3 py-1.5 text-xs font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] }));
|
|
50
50
|
}
|
|
51
51
|
function PreOrderBuyBarPanel({ reserveTarget, reservedCount, progressPct, productId, price, currency, depositAmount, depositPercent, isCancellable, tags, onReserveNow, }) {
|
|
52
|
-
return (_jsxs(Div, { id: "pre-order-buy-bar", className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-4", children: [reserveTarget > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Text, { className: "text-xs text-zinc-500", children: [reservedCount, " of ", reserveTarget, " reserved"] }), _jsxs(Span, { className: "text-xs font-semibold text-primary-600 dark:text-primary-400", children: [progressPct, "%"] })] }), _jsx(Div, { className: "h-2 w-full overflow-hidden rounded-full bg-zinc-200 dark:bg-zinc-700", children: _jsx(Div, { className: "h-full rounded-full bg-primary transition-all", style: { width: `${progressPct}%` } }) })] })), onReserveNow ? (_jsx(PreOrderActionsClient, { productId: productId, price: price, currency: currency, depositAmount: depositAmount, depositPercent: depositPercent, isCancellable: isCancellable, tags: tags, onReserveNow: onReserveNow })) : (_jsxs(_Fragment, { children: [price !== null && (_jsxs(Div, { children: [_jsx(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formatCurrency(price, currency) }), depositAmount !== null && (_jsxs(Text, { className: "mt-0.5 text-xs text-zinc-500", children: ["Reserve with ", formatCurrency(depositAmount, currency), depositPercent !== null ? ` (${depositPercent}% deposit)` : ""] }))] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Reserve Now" }), isCancellable && (_jsx(Text, { className: "text-center text-xs text-zinc-500 dark:text-zinc-400", children: "\u2713 Free cancellation before production" }))] }), tags.length > 0 && (_jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-1 text-xs text-zinc-600 dark:text-zinc-300", children: tag }, tag))) }) })), _jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "sm", className: "justify-center text-center", children: [
|
|
52
|
+
return (_jsxs(Div, { id: "pre-order-buy-bar", className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-4", children: [reserveTarget > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: [reservedCount, " of ", reserveTarget, " reserved"] }), _jsxs(Span, { className: "text-xs font-semibold text-primary-600 dark:text-primary-400", children: [progressPct, "%"] })] }), _jsx(Div, { className: "h-2 w-full overflow-hidden rounded-full bg-zinc-200 dark:bg-zinc-700", children: _jsx(Div, { className: "h-full rounded-full bg-primary transition-all", style: { width: `${progressPct}%` } }) })] })), onReserveNow ? (_jsx(PreOrderActionsClient, { productId: productId, price: price, currency: currency, depositAmount: depositAmount, depositPercent: depositPercent, isCancellable: isCancellable, tags: tags, onReserveNow: onReserveNow })) : (_jsxs(_Fragment, { children: [price !== null && (_jsxs(Div, { children: [_jsx(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formatCurrency(price, currency) }), depositAmount !== null && (_jsxs(Text, { className: "mt-0.5 text-xs text-zinc-500 dark:text-zinc-400", children: ["Reserve with ", formatCurrency(depositAmount, currency), depositPercent !== null ? ` (${depositPercent}% deposit)` : ""] }))] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Reserve Now" }), isCancellable && (_jsx(Text, { className: "text-center text-xs text-zinc-500 dark:text-zinc-400", children: "\u2713 Free cancellation before production" }))] }), tags.length > 0 && (_jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-1 text-xs text-zinc-600 dark:text-zinc-300", children: tag }, tag))) }) })), _jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "sm", className: "justify-center text-center", children: [
|
|
53
53
|
{ icon: "🔒", label: "Secure\nPayment" },
|
|
54
54
|
{ icon: "📅", label: "Guaranteed\nDelivery" },
|
|
55
55
|
{ icon: "↩", label: "Free\nCancellation" },
|
|
@@ -60,7 +60,7 @@ export async function PreOrderDetailPageView({ id, initialPreOrder, onReserveNow
|
|
|
60
60
|
? (initialPreOrder ?? undefined)
|
|
61
61
|
: await productRepository.findByIdOrSlug(id).catch(() => undefined);
|
|
62
62
|
if (!product) {
|
|
63
|
-
return (_jsx(Main, { children: _jsx(Section, { className: "py-20", children: _jsx(Container, { size: "md", children: _jsxs(Stack, { align: "center", gap: "md", className: "text-center", children: [_jsx(Heading, { level: 1, className: "text-2xl font-semibold text-zinc-900 dark:text-zinc-50", children: "Pre-Order Not Found" }), _jsx(Text, { className: "text-zinc-500", children: "The pre-order item you are looking for may have been removed." }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRE_ORDERS), className: "text-sm font-medium text-primary-600 hover:underline", children: "Browse Pre-Orders" })] }) }) }) }));
|
|
63
|
+
return (_jsx(Main, { children: _jsx(Section, { className: "py-20", children: _jsx(Container, { size: "md", children: _jsxs(Stack, { align: "center", gap: "md", className: "text-center", children: [_jsx(Heading, { level: 1, className: "text-2xl font-semibold text-zinc-900 dark:text-zinc-50", children: "Pre-Order Not Found" }), _jsx(Text, { className: "text-zinc-500 dark:text-zinc-400", children: "The pre-order item you are looking for may have been removed." }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRE_ORDERS), className: "text-sm font-medium text-primary-600 hover:underline", children: "Browse Pre-Orders" })] }) }) }) }));
|
|
64
64
|
}
|
|
65
65
|
const p = product;
|
|
66
66
|
const currency = p.currency || getDefaultCurrency();
|
|
@@ -147,5 +147,5 @@ export async function PreOrderDetailPageView({ id, initialPreOrder, onReserveNow
|
|
|
147
147
|
id: s.id,
|
|
148
148
|
label: s.title,
|
|
149
149
|
content: _jsx(CustomSectionTabContent, { section: s }),
|
|
150
|
-
})) })), renderBuyBar: () => (_jsx(PreOrderBuyBarPanel, { reserveTarget: reserveTarget, reservedCount: reservedCount, progressPct: progressPct, productId: String(product.id), price: price, currency: currency, depositAmount: depositAmount, depositPercent: depositPercent, isCancellable: isCancellable, tags: tags, onReserveNow: onReserveNow })) }),
|
|
150
|
+
})) })), renderBuyBar: () => (_jsx(PreOrderBuyBarPanel, { reserveTarget: reserveTarget, reservedCount: reservedCount, progressPct: progressPct, productId: String(product.id), price: price, currency: currency, depositAmount: depositAmount, depositPercent: depositPercent, isCancellable: isCancellable, tags: tags, onReserveNow: onReserveNow })) }), _jsx(PreOrderBottomActions, { price: price, currency: currency })] })] }));
|
|
151
151
|
}
|
|
@@ -20,6 +20,7 @@ import { useBrands } from "../../products/hooks/useBrands";
|
|
|
20
20
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
21
21
|
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
22
22
|
import { PREORDER_SORT_OPTIONS } from "../../products/constants/sieve";
|
|
23
|
+
import { useBottomActions } from "../../layout";
|
|
23
24
|
const DEFAULT_SORT = PREORDER_SORT_OPTIONS[2].value;
|
|
24
25
|
const FILTER_KEYS = [TABLE_KEYS.CATEGORY, TABLE_KEYS.BRAND, TABLE_KEYS.MIN_PRICE, TABLE_KEYS.MAX_PRICE, TABLE_KEYS.STORE_ID, TABLE_KEYS.PREORDER_STATUS, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO];
|
|
25
26
|
export function PreOrdersIndexListing({ initialData, categorySlug, brandName }) {
|
|
@@ -120,6 +121,52 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
120
121
|
}, [localCart, showToast]);
|
|
121
122
|
const selection = useBulkSelection({ items: preOrders, keyExtractor: (p) => p.id });
|
|
122
123
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
|
|
124
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
125
|
+
{
|
|
126
|
+
id: ACTION_ID.ADD_TO_CART,
|
|
127
|
+
label: ACTION_META[ACTION_ID.ADD_TO_CART].label,
|
|
128
|
+
icon: _jsx(ShoppingCart, { className: "h-3.5 w-3.5" }),
|
|
129
|
+
variant: "primary",
|
|
130
|
+
onClick: () => {
|
|
131
|
+
const selected = preOrders.filter((p) => selection.selectedIdSet.has(p.id));
|
|
132
|
+
selected.forEach((p) => {
|
|
133
|
+
const snapshot = { productTitle: p.title, productImage: p.mainImage, price: p.price, storeId: p.storeId, storeName: p.storeName };
|
|
134
|
+
localCart.add(p.id, 1, snapshot);
|
|
135
|
+
pushCartOp({ op: "add", productId: p.id, quantity: 1, ...snapshot });
|
|
136
|
+
});
|
|
137
|
+
showToast(`${selected.length} items added to cart`, "success");
|
|
138
|
+
selection.clearSelection();
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: ACTION_ID.ADD_TO_WISHLIST,
|
|
143
|
+
label: ACTION_META[ACTION_ID.ADD_TO_WISHLIST].label,
|
|
144
|
+
icon: _jsx(Heart, { className: "h-3.5 w-3.5" }),
|
|
145
|
+
variant: "secondary",
|
|
146
|
+
onClick: () => {
|
|
147
|
+
requireAuth(ACTION_ID.ADD_TO_WISHLIST, () => {
|
|
148
|
+
const selected = preOrders.filter((p) => selection.selectedIdSet.has(p.id));
|
|
149
|
+
selected.forEach((p) => {
|
|
150
|
+
localWishlist.add(p.id, "preorder");
|
|
151
|
+
pushWishlistOp({ op: "add", itemId: p.id, type: "preorder" });
|
|
152
|
+
});
|
|
153
|
+
showToast(`${selected.length} items added to wishlist`, "success");
|
|
154
|
+
selection.clearSelection();
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
id: ACTION_ID.COMPARE,
|
|
160
|
+
label: ACTION_META[ACTION_ID.COMPARE].label,
|
|
161
|
+
icon: _jsx(Columns, { className: "h-3.5 w-3.5" }),
|
|
162
|
+
variant: "secondary",
|
|
163
|
+
disabled: selection.selectedCount < 2 || selection.selectedCount > COMPARE_MAX_ITEMS,
|
|
164
|
+
onClick: () => {
|
|
165
|
+
const ids = Array.from(selection.selectedIdSet).slice(0, COMPARE_MAX_ITEMS);
|
|
166
|
+
setCompareIds(ids);
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
] } } : {});
|
|
123
170
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: preOrders.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, toggles: [
|
|
124
171
|
{ label: "Show closed", active: showClosed, onChange: (next) => table.set(TABLE_KEYS.SHOW_CLOSED, next ? "true" : "") },
|
|
125
172
|
] }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
@@ -36,5 +36,5 @@ export async function PreOrdersListView({ searchParams = {} }) {
|
|
|
36
36
|
const result = await productRepository
|
|
37
37
|
.list({ filters, sorts: sort, page, pageSize })
|
|
38
38
|
.catch(() => null);
|
|
39
|
-
return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8 text-3xl font-semibold text-zinc-900", children: "Pre-Orders" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(PreOrdersIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
|
|
39
|
+
return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8 text-3xl font-semibold text-zinc-900 dark:text-zinc-50", children: "Pre-Orders" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(PreOrdersIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
|
|
40
40
|
}
|
|
@@ -7,9 +7,9 @@ const STATUS_LABELS = {
|
|
|
7
7
|
shipped: "Shipped",
|
|
8
8
|
};
|
|
9
9
|
const STATUS_COLORS = {
|
|
10
|
-
available: "bg-indigo-100 text-indigo-800",
|
|
11
|
-
shipping_soon: "bg-amber-100 text-amber-800",
|
|
12
|
-
shipped: "bg-green-100 text-green-800",
|
|
10
|
+
available: "bg-indigo-100 text-indigo-800 dark:bg-indigo-900/40 dark:text-indigo-300",
|
|
11
|
+
shipping_soon: "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300",
|
|
12
|
+
shipped: "bg-green-100 text-green-800 dark:bg-green-900/40 dark:text-green-300",
|
|
13
13
|
};
|
|
14
14
|
export function PreorderBadge({ shipDate, className }) {
|
|
15
15
|
const status = getPreorderStatus(shipDate);
|
|
@@ -16,6 +16,7 @@ import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
|
16
16
|
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
17
17
|
import { ACTION_ID } from "../constants/action-defs";
|
|
18
18
|
import { AUCTION_PUBLIC_SORT_OPTIONS } from "../constants/sieve";
|
|
19
|
+
import { useBottomActions } from "../../layout";
|
|
19
20
|
const DEFAULT_SORT = AUCTION_PUBLIC_SORT_OPTIONS[0].value;
|
|
20
21
|
const AUCTION_SORT_OPTIONS = AUCTION_PUBLIC_SORT_OPTIONS;
|
|
21
22
|
const FILTER_KEYS = [TABLE_KEYS.CATEGORY, TABLE_KEYS.BRAND, TABLE_KEYS.MIN_BID, TABLE_KEYS.MAX_BID, TABLE_KEYS.STORE_ID, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO];
|
|
@@ -103,6 +104,28 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
|
103
104
|
isWishlisted: (productId) => wishlistedIds.has(productId),
|
|
104
105
|
};
|
|
105
106
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-3 gap-4";
|
|
107
|
+
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
108
|
+
{
|
|
109
|
+
id: ACTION_ID.WATCH_AUCTION,
|
|
110
|
+
label: "Add to Watchlist",
|
|
111
|
+
variant: "primary",
|
|
112
|
+
onClick: () => {
|
|
113
|
+
const selected = auctions.filter((a) => selection.selectedIdSet.has(a.id));
|
|
114
|
+
selected.forEach((a) => { wishlistActions.addToWishlist(a.id); });
|
|
115
|
+
selection.clearSelection();
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: ACTION_ID.UNWATCH_AUCTION,
|
|
120
|
+
label: "Remove from Watchlist",
|
|
121
|
+
variant: "secondary",
|
|
122
|
+
onClick: () => {
|
|
123
|
+
const selected = auctions.filter((a) => selection.selectedIdSet.has(a.id));
|
|
124
|
+
selected.forEach((a) => { wishlistActions.removeFromWishlist(a.id); });
|
|
125
|
+
selection.clearSelection();
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
] } } : {});
|
|
106
129
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: auctions.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, toggles: [
|
|
107
130
|
{ label: "Show ended", active: showEnded, onChange: (next) => table.set(TABLE_KEYS.SHOW_ENDED, next ? "true" : "") },
|
|
108
131
|
] }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
@@ -21,7 +21,7 @@ export function BidHistory({ isLoading = false, isEmpty = false, renderList, ren
|
|
|
21
21
|
if (isLoading) {
|
|
22
22
|
if (renderSkeleton)
|
|
23
23
|
return _jsx(_Fragment, { children: renderSkeleton() });
|
|
24
|
-
return (_jsx("div", { className: "animate-pulse space-y-2", "data-section": "bidhistory-div-419", children: Array.from({ length: 3 }).map((_, i) => (_jsx("div", { className: "h-10 rounded-lg bg-
|
|
24
|
+
return (_jsx("div", { className: "animate-pulse space-y-2", "data-section": "bidhistory-div-419", children: Array.from({ length: 3 }).map((_, i) => (_jsx("div", { className: "h-10 rounded-lg bg-zinc-100 dark:bg-zinc-800" }, i))) }));
|
|
25
25
|
}
|
|
26
26
|
if (isEmpty) {
|
|
27
27
|
if (renderEmpty)
|
|
@@ -30,5 +30,5 @@ export function BidHistory({ isLoading = false, isEmpty = false, renderList, ren
|
|
|
30
30
|
}
|
|
31
31
|
if (renderList)
|
|
32
32
|
return _jsx("div", { className: className, "data-section": "bidhistory-div-420", children: renderList() });
|
|
33
|
-
return (_jsxs("div", { className: className, "data-section": "bidhistory-div-421", children: [labels.title && (_jsx(Heading, { level: 3, className: "mb-3 text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: labels.title })), _jsx("div", { className: "space-y-2", children: bids.map((bid, i) => renderBid ? (_jsx(React.Fragment, { children: renderBid(bid, i) }, bid.id)) : (_jsxs("div", { className: "flex items-center justify-between rounded-lg border border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-3 text-sm", "data-section": "bidhistory-div-422", children: [_jsx(Span, { className: "font-bold text-primary-600 dark:text-primary-400", children: currency ? formatCurrency(bid.amount, currency) : bid.amount.toLocaleString() }), _jsx(Span, { className: "text-xs text-zinc-400 dark:text-zinc-
|
|
33
|
+
return (_jsxs("div", { className: className, "data-section": "bidhistory-div-421", children: [labels.title && (_jsx(Heading, { level: 3, className: "mb-3 text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: labels.title })), _jsx("div", { className: "space-y-2", children: bids.map((bid, i) => renderBid ? (_jsx(React.Fragment, { children: renderBid(bid, i) }, bid.id)) : (_jsxs("div", { className: "flex items-center justify-between rounded-lg border border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-3 text-sm", "data-section": "bidhistory-div-422", children: [_jsx(Span, { className: "font-bold text-primary-600 dark:text-primary-400", children: currency ? formatCurrency(bid.amount, currency) : bid.amount.toLocaleString() }), _jsx(Span, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: formatBidDate(bid.placedAt) })] }, bid.id))) })] }));
|
|
34
34
|
}
|
|
@@ -147,7 +147,7 @@ export function CompareOverlay({ isOpen, items: passedItems, productIds, product
|
|
|
147
147
|
}, [onRemove]);
|
|
148
148
|
if (!isOpen)
|
|
149
149
|
return null;
|
|
150
|
-
return (_jsxs(Div, { className: OVERLAY_CLASS, style: OVERLAY_STYLE, role: "dialog", "aria-modal": "true", "aria-label": labels.title, children: [_jsxs(Row, { justify: "between", align: "center", className: HEADER_CLASS, children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: labels.title }), _jsx("button", { type: "button", onClick: onClose, "aria-label": labels.close, className: "rounded-full p-2 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-800", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsx(Div, { className: "flex-1 overflow-auto p-4", children: isLoading ? (_jsx(Text, { variant: "secondary", className: "text-center py-12", children: "Loading\u00E2\u20AC\u00A6" })) : trimmed.length === 0 ? (_jsx(Text, { variant: "secondary", className: "text-center py-12", children: labels.empty })) : (_jsxs(_Fragment, { children: [_jsx(Div, { className: "hidden md:grid gap-4", style: {
|
|
150
|
+
return (_jsxs(Div, { className: OVERLAY_CLASS, style: OVERLAY_STYLE, role: "dialog", "aria-modal": "true", "aria-label": labels.title, children: [_jsxs(Row, { justify: "between", align: "center", className: HEADER_CLASS, children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: labels.title }), _jsx("button", { type: "button", onClick: onClose, "aria-label": labels.close, className: "rounded-full p-2 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsx(Div, { className: "flex-1 overflow-auto p-4", children: isLoading ? (_jsx(Text, { variant: "secondary", className: "text-center py-12", children: "Loading\u00E2\u20AC\u00A6" })) : trimmed.length === 0 ? (_jsx(Text, { variant: "secondary", className: "text-center py-12", children: labels.empty })) : (_jsxs(_Fragment, { children: [_jsx(Div, { className: "hidden md:grid gap-4", style: {
|
|
151
151
|
gridTemplateColumns: `repeat(${trimmed.length}, minmax(0, 1fr))`,
|
|
152
152
|
}, children: trimmed.map((item) => (_jsx(CompareColumn, { item: item, productType: productType, labels: labels, onRemove: onRemove ? handleRemove : undefined, onClose: onClose }, item.id))) }), _jsxs(Div, { className: "md:hidden", children: [_jsx(Div, { ref: swipeRef, className: "touch-pan-y", children: trimmed[activeIndex] && (_jsx(CompareColumn, { item: trimmed[activeIndex], productType: productType, labels: labels, onRemove: onRemove ? handleRemove : undefined, onClose: onClose })) }), trimmed.length > 1 && (_jsx(Row, { justify: "center", gap: "xs", className: "mt-4", children: trimmed.map((it, idx) => (_jsx("button", { type: "button", "aria-label": `Show item ${idx + 1}`, onClick: () => setActiveIndex(idx), className: `h-2 w-2 rounded-full transition-colors ${idx === activeIndex
|
|
153
153
|
? "bg-primary"
|
|
@@ -29,5 +29,5 @@ export function CustomFieldsEditor({ fields, onChange, showUnit = false, }) {
|
|
|
29
29
|
return;
|
|
30
30
|
onChange([...fields, emptyField()]);
|
|
31
31
|
}
|
|
32
|
-
return (_jsxs(Stack, { gap: "xs", children: [fields.map((field, i) => (_jsxs(Div, { className: "grid grid-cols-[1fr_140px_1fr_auto] gap-2 items-start", children: [_jsx(Input, { value: field.key, onChange: (e) => update(i, { key: e.target.value }), placeholder: "Field name", "aria-label": `Custom field ${i + 1} name` }), _jsx(Select, { options: TYPE_OPTIONS, value: field.type, onChange: (e) => update(i, { type: e.target.value, value: "" }), "aria-label": `Custom field ${i + 1} type` }), field.type === "boolean" ? (_jsx(Select, { options: BOOL_OPTIONS, value: field.value, onChange: (e) => update(i, { value: e.target.value }), "aria-label": `Custom field ${i + 1} value` })) : (_jsxs(Div, { className: showUnit ? "flex gap-1" : "", children: [_jsx(Input, { type: field.type === "number" ? "number" : "text", value: field.value, onChange: (e) => update(i, { value: e.target.value }), placeholder: field.type === "url" ? "https://" : "Value", "aria-label": `Custom field ${i + 1} value`, className: "flex-1" }), showUnit && (_jsx(Input, { value: field.unit ?? "", onChange: (e) => update(i, { unit: e.target.value || undefined }), placeholder: "Unit", "aria-label": `Custom field ${i + 1} unit`, className: "w-20 flex-shrink-0" }))] })), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(i), "aria-label": `Remove field ${i + 1}`, className: "text-zinc-400 hover:text-red-500 dark:hover:text-red-400 px-2", children: "\u2715" })] }, i))), _jsxs(Row, { align: "center", justify: "between", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: add, disabled: fields.length >= MAX_CUSTOM_FIELDS, className: "text-primary-600 dark:text-primary-400", children: "+ Add field" }), fields.length > 0 && (_jsxs(Text, { className: "text-xs text-zinc-400", children: [fields.length, " / ", MAX_CUSTOM_FIELDS] }))] })] }));
|
|
32
|
+
return (_jsxs(Stack, { gap: "xs", children: [fields.map((field, i) => (_jsxs(Div, { className: "grid grid-cols-[1fr_140px_1fr_auto] gap-2 items-start", children: [_jsx(Input, { value: field.key, onChange: (e) => update(i, { key: e.target.value }), placeholder: "Field name", "aria-label": `Custom field ${i + 1} name` }), _jsx(Select, { options: TYPE_OPTIONS, value: field.type, onChange: (e) => update(i, { type: e.target.value, value: "" }), "aria-label": `Custom field ${i + 1} type` }), field.type === "boolean" ? (_jsx(Select, { options: BOOL_OPTIONS, value: field.value, onChange: (e) => update(i, { value: e.target.value }), "aria-label": `Custom field ${i + 1} value` })) : (_jsxs(Div, { className: showUnit ? "flex gap-1" : "", children: [_jsx(Input, { type: field.type === "number" ? "number" : "text", value: field.value, onChange: (e) => update(i, { value: e.target.value }), placeholder: field.type === "url" ? "https://" : "Value", "aria-label": `Custom field ${i + 1} value`, className: "flex-1" }), showUnit && (_jsx(Input, { value: field.unit ?? "", onChange: (e) => update(i, { unit: e.target.value || undefined }), placeholder: "Unit", "aria-label": `Custom field ${i + 1} unit`, className: "w-20 flex-shrink-0" }))] })), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(i), "aria-label": `Remove field ${i + 1}`, className: "text-zinc-400 hover:text-red-500 dark:hover:text-red-400 px-2", children: "\u2715" })] }, i))), _jsxs(Row, { align: "center", justify: "between", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: add, disabled: fields.length >= MAX_CUSTOM_FIELDS, className: "text-primary-600 dark:text-primary-400", children: "+ Add field" }), fields.length > 0 && (_jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: [fields.length, " / ", MAX_CUSTOM_FIELDS] }))] })] }));
|
|
33
33
|
}
|
|
@@ -9,5 +9,5 @@ function renderFieldValue(f) {
|
|
|
9
9
|
export function CustomSectionTabContent({ section }) {
|
|
10
10
|
const html = section.text ? normalizeRichTextHtml(section.text) : null;
|
|
11
11
|
const fields = section.fields?.filter((f) => f.key && f.value) ?? [];
|
|
12
|
-
return (_jsxs(Div, { className: "space-y-4", children: [html && (_jsx(RichText, { html: html, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-zinc-700 dark:text-zinc-300" })), fields.length > 0 && (_jsx("dl", { className: "divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800 overflow-hidden text-sm", children: fields.map((f, i) => (_jsxs("div", { className: "flex gap-4 px-4 py-3 bg-white dark:bg-zinc-900 even:bg-zinc-50 dark:even:bg-zinc-800/50", children: [_jsx("dt", { className: "w-36 flex-shrink-0 font-medium text-zinc-700 dark:text-zinc-300 capitalize", children: f.key }), _jsx("dd", { className: "flex-1 text-zinc-600 dark:text-zinc-400", children: f.type === "url" ? (_jsx("a", { href: f.value, target: "_blank", rel: "noopener noreferrer", className: "text-primary-600 hover:underline dark:text-primary-400 break-all", children: f.value })) : (renderFieldValue(f)) })] }, i))) })), !html && fields.length === 0 && (_jsx(Text, { className: "text-sm text-zinc-400", children: "No content in this section." }))] }));
|
|
12
|
+
return (_jsxs(Div, { className: "space-y-4", children: [html && (_jsx(RichText, { html: html, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-zinc-700 dark:text-zinc-300" })), fields.length > 0 && (_jsx("dl", { className: "divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800 overflow-hidden text-sm", children: fields.map((f, i) => (_jsxs("div", { className: "flex gap-4 px-4 py-3 bg-white dark:bg-zinc-900 even:bg-zinc-50 dark:even:bg-zinc-800/50", children: [_jsx("dt", { className: "w-36 flex-shrink-0 font-medium text-zinc-700 dark:text-zinc-300 capitalize", children: f.key }), _jsx("dd", { className: "flex-1 text-zinc-600 dark:text-zinc-400", children: f.type === "url" ? (_jsx("a", { href: f.value, target: "_blank", rel: "noopener noreferrer", className: "text-primary-600 hover:underline dark:text-primary-400 break-all", children: f.value })) : (renderFieldValue(f)) })] }, i))) })), !html && fields.length === 0 && (_jsx(Text, { className: "text-sm text-zinc-400 dark:text-zinc-400", children: "No content in this section." }))] }));
|
|
13
13
|
}
|
|
@@ -23,5 +23,5 @@ export function CustomSectionsEditor({ sections, onChange, }) {
|
|
|
23
23
|
return;
|
|
24
24
|
onChange([...sections, emptySection()]);
|
|
25
25
|
}
|
|
26
|
-
return (_jsxs(Stack, { gap: "md", children: [sections.map((section, i) => (_jsxs(Div, { className: "rounded-xl border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-900/40 p-4 space-y-3", children: [_jsxs(Div, { className: "flex items-center justify-between gap-2", children: [_jsxs(Heading, { level: 4, className: "text-sm font-semibold text-zinc-700 dark:text-zinc-200", children: ["Section ", i + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(i), className: "text-zinc-400 hover:text-red-500 dark:hover:text-red-400 text-xs", children: "Remove" })] }), _jsxs(Div, { children: [_jsxs("label", { htmlFor: `${baseId}-title-${i}`, className: "mb-1 block text-xs font-medium text-zinc-600 dark:text-zinc-400", children: ["Section title ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: `${baseId}-title-${i}`, value: section.title, onChange: (e) => update(i, { title: e.target.value }), placeholder: 'e.g. "Box Contents", "Compatibility", "Grading Details"', maxLength: 80 })] }), _jsxs(Div, { children: [_jsxs("label", { htmlFor: `${baseId}-text-${i}`, className: "mb-1 block text-xs font-medium text-zinc-600 dark:text-zinc-400", children: ["Body text", " ", _jsx("span", { className: "text-zinc-400 font-normal", children: "(optional)" })] }), _jsx("textarea", { id: `${baseId}-text-${i}`, value: section.text ?? "", onChange: (e) => update(i, { text: e.target.value || undefined }), placeholder: "Additional details for this section\u2026", rows: 3, maxLength: 2000, className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-800 dark:text-zinc-100 placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500 resize-y" })] }), _jsxs(Div, { children: [_jsxs(Text, { className: "mb-2 text-xs font-medium text-zinc-600 dark:text-zinc-400", children: ["Fields", " ", _jsx("span", { className: "text-zinc-400 font-normal", children: "(optional)" })] }), _jsx(CustomFieldsEditor, { fields: section.fields ?? [], onChange: (fields) => update(i, { fields: fields.length > 0 ? fields : undefined }), showUnit: true })] })] }, section.id))), _jsxs(Div, { className: "flex items-center justify-between", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: add, disabled: sections.length >= MAX_CUSTOM_SECTIONS, className: "text-primary-600 dark:text-primary-400", children: "+ Add section" }), _jsxs(Text, { className: "text-xs text-zinc-400", children: [sections.length, " / ", MAX_CUSTOM_SECTIONS, " sections"] })] })] }));
|
|
26
|
+
return (_jsxs(Stack, { gap: "md", children: [sections.map((section, i) => (_jsxs(Div, { className: "rounded-xl border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-900/40 p-4 space-y-3", children: [_jsxs(Div, { className: "flex items-center justify-between gap-2", children: [_jsxs(Heading, { level: 4, className: "text-sm font-semibold text-zinc-700 dark:text-zinc-200", children: ["Section ", i + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(i), className: "text-zinc-400 hover:text-red-500 dark:hover:text-red-400 text-xs", children: "Remove" })] }), _jsxs(Div, { children: [_jsxs("label", { htmlFor: `${baseId}-title-${i}`, className: "mb-1 block text-xs font-medium text-zinc-600 dark:text-zinc-400", children: ["Section title ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: `${baseId}-title-${i}`, value: section.title, onChange: (e) => update(i, { title: e.target.value }), placeholder: 'e.g. "Box Contents", "Compatibility", "Grading Details"', maxLength: 80 })] }), _jsxs(Div, { children: [_jsxs("label", { htmlFor: `${baseId}-text-${i}`, className: "mb-1 block text-xs font-medium text-zinc-600 dark:text-zinc-400", children: ["Body text", " ", _jsx("span", { className: "text-zinc-400 font-normal", children: "(optional)" })] }), _jsx("textarea", { id: `${baseId}-text-${i}`, value: section.text ?? "", onChange: (e) => update(i, { text: e.target.value || undefined }), placeholder: "Additional details for this section\u2026", rows: 3, maxLength: 2000, className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-800 dark:text-zinc-100 placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500 resize-y" })] }), _jsxs(Div, { children: [_jsxs(Text, { className: "mb-2 text-xs font-medium text-zinc-600 dark:text-zinc-400", children: ["Fields", " ", _jsx("span", { className: "text-zinc-400 font-normal", children: "(optional)" })] }), _jsx(CustomFieldsEditor, { fields: section.fields ?? [], onChange: (fields) => update(i, { fields: fields.length > 0 ? fields : undefined }), showUnit: true })] })] }, section.id))), _jsxs(Div, { className: "flex items-center justify-between", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: add, disabled: sections.length >= MAX_CUSTOM_SECTIONS, className: "text-primary-600 dark:text-primary-400", children: "+ Add section" }), _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: [sections.length, " / ", MAX_CUSTOM_SECTIONS, " sections"] })] })] }));
|
|
27
27
|
}
|
|
@@ -183,10 +183,10 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
|
|
|
183
183
|
setOpen(next);
|
|
184
184
|
if (next && isGroupParent && children === null)
|
|
185
185
|
loadChildren();
|
|
186
|
-
}, className: "w-full flex items-center justify-between py-2 text-left group", "aria-expanded": open, children: [_jsx(Heading, { level: 3, className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Group Settings" }), _jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-
|
|
186
|
+
}, className: "w-full flex items-center justify-between py-2 text-left group", "aria-expanded": open, children: [_jsx(Heading, { level: 3, className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Group Settings" }), _jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-400 group-hover:text-zinc-600 dark:group-hover:text-zinc-300 transition-colors", children: open ? "▲" : "▼" })] }), open && (_jsxs(Stack, { gap: "sm", className: "mt-3 p-4 rounded-lg border border-zinc-200 dark:border-zinc-700 bg-zinc-50/50 dark:bg-zinc-800/30", children: [!groupId && !groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Group related listings together \u2014 e.g. a set, bundle, or multi-part item. Parts can be sold individually but shown together." }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: startGroup, isLoading: loading, children: "Start a group" })] })), isGroupParent && groupId && (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { align: "start", gap: "sm", className: "flex-wrap", children: [_jsx("div", { className: "flex-1 min-w-[200px]", children: _jsx(FormField, { name: "groupTitle", label: "Group title", type: "text", value: editTitle, onChange: setEditTitle, placeholder: "e.g. Human Toy Complete Set" }) }), _jsx("div", { className: "pt-6", children: _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: saveTitle, isLoading: loading, children: "Save title" }) })] }), _jsxs(Div, { children: [_jsxs(Text, { className: "text-xs font-semibold text-zinc-600 dark:text-zinc-400 mb-2", children: ["Members (", childSlugsCount + 1, " \u2014 including this listing)"] }), loading && !children ? (_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: "Loading\u2026" })) : (_jsxs("div", { className: "divide-y divide-zinc-100 dark:divide-zinc-800", children: [_jsxs(Row, { align: "center", gap: "sm", className: "py-2", children: [_jsx("span", { className: "rounded bg-[var(--appkit-color-primary,#6366f1)]/10 text-[var(--appkit-color-primary,#6366f1)] text-[10px] font-semibold px-1.5 py-0.5", children: "Parent" }), _jsx(Text, { className: "text-sm text-zinc-800 dark:text-zinc-200 flex-1", children: productSlug })] }), (children ?? []).filter((c) => c.id !== productId).map((child) => (_jsxs(Row, { align: "center", gap: "sm", className: "py-2", children: [child.images?.[0] ? (
|
|
187
187
|
// eslint-disable-next-line @next/next/no-img-element
|
|
188
188
|
_jsx("img", { src: child.images[0], alt: child.title, className: "w-8 h-8 rounded-full object-cover border border-zinc-200 dark:border-zinc-700" })) : (_jsx("div", { className: "w-8 h-8 rounded-full bg-zinc-100 dark:bg-zinc-800" })), _jsx(Text, { className: "text-sm text-zinc-800 dark:text-zinc-200 flex-1 truncate", children: child.title }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => unlinkChild(child.id), isLoading: loading, className: "text-red-500 hover:text-red-600 text-xs", children: "Unlink" })] }, child.id)))] }))] }), _jsxs(Row, { gap: "sm", className: "flex-wrap", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setShowAddModal(true), children: "Add child listing" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: dissolveGroup, isLoading: loading, className: "text-red-500 hover:text-red-600", children: "Dissolve group" })] })] })), !isGroupParent && groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { className: "text-sm text-zinc-600 dark:text-zinc-400", children: ["Part of:", " ", _jsx("span", { className: "font-medium text-zinc-800 dark:text-zinc-200", children: groupTitle ?? groupParentSlug })] }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Parent listing: ", _jsx("code", { className: "font-mono", children: groupParentSlug })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: leaveGroup, isLoading: loading, className: "text-red-500 hover:text-red-600 w-fit", children: "Leave group" })] }))] })), (groupChildSlugs?.length ?? 0) >= 4 ? (_jsx(SideDrawer, { isOpen: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, linkTarget: linkTarget, setLinkTarget: setLinkTarget, loadLinkOptions: loadLinkOptions, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })) : (_jsx(Modal, { open: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", size: "lg", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, linkTarget: linkTarget, setLinkTarget: setLinkTarget, loadLinkOptions: loadLinkOptions, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) }))] }));
|
|
189
189
|
}
|
|
190
190
|
function AddChildContent({ addTab, setAddTab, createForm, setCreateForm, productSlug, linkTarget, setLinkTarget, loadLinkOptions, onAddCreate, onAddLink, loading, }) {
|
|
191
|
-
return (_jsx(Stack, { gap: "md", children: _jsxs(Tabs, { value: addTab, onChange: (v) => setAddTab(v), children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "create", children: "Create new child" }), _jsx(TabsTrigger, { value: "link", children: "Link existing" })] }), _jsx(TabsContent, { value: "create", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(FormField, { name: "childTitle", label: "Title *", type: "text", value: createForm.title, onChange: (v) => setCreateForm({ ...createForm, title: v }), placeholder: `${productSlug}-part` }), _jsx(FormField, { name: "childPrice", label: "Price (\u20B9) *", type: "number", value: createForm.price, onChange: (v) => setCreateForm({ ...createForm, price: v }), placeholder: "0" }), _jsx(Select, { label: "Condition", value: createForm.condition, onValueChange: (v) => setCreateForm({ ...createForm, condition: v }), options: CONDITION_OPTIONS }), _jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-
|
|
191
|
+
return (_jsx(Stack, { gap: "md", children: _jsxs(Tabs, { value: addTab, onChange: (v) => setAddTab(v), children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "create", children: "Create new child" }), _jsx(TabsTrigger, { value: "link", children: "Link existing" })] }), _jsx(TabsContent, { value: "create", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(FormField, { name: "childTitle", label: "Title *", type: "text", value: createForm.title, onChange: (v) => setCreateForm({ ...createForm, title: v }), placeholder: `${productSlug}-part` }), _jsx(FormField, { name: "childPrice", label: "Price (\u20B9) *", type: "number", value: createForm.price, onChange: (v) => setCreateForm({ ...createForm, price: v }), placeholder: "0" }), _jsx(Select, { label: "Condition", value: createForm.condition, onValueChange: (v) => setCreateForm({ ...createForm, condition: v }), options: CONDITION_OPTIONS }), _jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400/80", children: "Other fields (category, brand, shipping, return policy) are inherited from this parent listing. Need more control? Edit the full listing after saving." }), _jsx(Button, { type: "button", onClick: onAddCreate, isLoading: loading, disabled: !createForm.title || !createForm.price, children: "Create and link child" })] }) }), _jsx(TabsContent, { value: "link", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Search your existing products or pre-orders. Auctions cannot be linked." }), _jsx(DynamicSelect, { value: linkTarget, onChange: (v) => setLinkTarget(v), loadOptions: loadLinkOptions, placeholder: "Search listings\u2026", searchPlaceholder: "Type title or slug\u2026", noResultsText: "No matching listings found", ariaLabel: "Listing to link" }), _jsx(Button, { type: "button", onClick: onAddLink, isLoading: loading, disabled: !linkTarget, children: "Link to group" })] }) })] }) }));
|
|
192
192
|
}
|
|
@@ -73,5 +73,5 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
|
|
|
73
73
|
return (_jsxs(Div, { className: `rounded-xl border border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-900/20 p-4 text-center space-y-1 ${className}`, children: [_jsx(Span, { className: "text-lg", children: "\u23F3" }), _jsx(Text, { className: "text-sm font-medium text-amber-800 dark:text-amber-300", children: "Offer Pending" }), _jsx(Text, { className: "text-xs text-amber-700 dark:text-amber-400", children: "You already have an offer on this item. Check My Offers for updates." })] }));
|
|
74
74
|
}
|
|
75
75
|
const modalOpen = state === "confirm" || state === "loading" || state === "error";
|
|
76
|
-
return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", className: `w-full border border-zinc-300 dark:border-zinc-600 ${className}`, onClick: handleOpenConfirm, children: "Make Offer" }), _jsx(Modal, { isOpen: modalOpen, onClose: handleCancel, size: "md", title: "Make an offer", actions: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: handleCancel, disabled: isPending, children: "Cancel" }), _jsx(Button, { variant: "primary", size: "sm", onClick: handleSubmit, disabled: isPending || offerAmount < minOffer, children: isPending ? "Sending…" : `Send offer of ${fmt(offerAmount)}` })] }), children: _jsxs(Stack, { gap: "md", children: [_jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Your offer amount" }), _jsx(Input, { type: "number", value: String(offerAmount), onChange: (e) => handleAmountChange(e.target.value), min: minOffer, max: listedPrice - 1, step: 1, "aria-label": "Offer amount" }), _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-
|
|
76
|
+
return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", className: `w-full border border-zinc-300 dark:border-zinc-600 ${className}`, onClick: handleOpenConfirm, children: "Make Offer" }), _jsx(Modal, { isOpen: modalOpen, onClose: handleCancel, size: "md", title: "Make an offer", actions: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: handleCancel, disabled: isPending, children: "Cancel" }), _jsx(Button, { variant: "primary", size: "sm", onClick: handleSubmit, disabled: isPending || offerAmount < minOffer, children: isPending ? "Sending…" : `Send offer of ${fmt(offerAmount)}` })] }), children: _jsxs(Stack, { gap: "md", children: [_jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Your offer amount" }), _jsx(Input, { type: "number", value: String(offerAmount), onChange: (e) => handleAmountChange(e.target.value), min: minOffer, max: listedPrice - 1, step: 1, "aria-label": "Offer amount" }), _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: ["Must be between ", fmt(minOffer), " and ", fmt(listedPrice - 1)] })] }), _jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Note to seller (optional)" }), _jsx(Input, { type: "text", value: buyerNote, onChange: (e) => setBuyerNote(e.target.value), placeholder: "E.g. Bundle deal, long-time fan\u2026", maxLength: 300, "aria-label": "Note to seller" })] }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "The seller will accept, decline, or suggest a counter price." }), state === "error" && (_jsx(Text, { className: "text-xs text-red-600 dark:text-red-400", children: errorMsg }))] }) })] }));
|
|
77
77
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useBottomActions } from "../../layout/hooks/useBottomActions";
|
|
3
|
+
import { formatCurrency } from "../../../utils/number.formatter";
|
|
4
|
+
import { ACTION_ID } from "../constants/action-defs";
|
|
5
|
+
export function PrizeDrawBottomActions({ pricePerEntry, currency, closed, }) {
|
|
6
|
+
useBottomActions(closed
|
|
7
|
+
? {}
|
|
8
|
+
: {
|
|
9
|
+
actions: [
|
|
10
|
+
{
|
|
11
|
+
id: ACTION_ID.BUY_NOW,
|
|
12
|
+
label: "Buy Now",
|
|
13
|
+
variant: "primary",
|
|
14
|
+
onClick: () => {
|
|
15
|
+
document
|
|
16
|
+
.getElementById("prize-draw-buy-bar")
|
|
17
|
+
?.scrollIntoView({ behavior: "smooth" });
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
infoLabel: `${formatCurrency(pricePerEntry, currency)} per entry`,
|
|
22
|
+
});
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
@@ -7,14 +7,12 @@ import { normalizeRichTextHtml } from "../../../utils/string.formatter";
|
|
|
7
7
|
import { safeDisplayName } from "../../../security";
|
|
8
8
|
import { Container, Div, Heading, Main, RichText, Row, Section, Span, Stack, Text, } from "../../../ui";
|
|
9
9
|
import { PreOrderDetailView } from "./PreOrderDetailView";
|
|
10
|
-
import {
|
|
10
|
+
import { PrizeDrawBottomActions } from "./PrizeDrawBottomActions";
|
|
11
11
|
import { ProductTabsShell } from "./ProductTabsShell";
|
|
12
12
|
import { ShareButton } from "./ShareButton";
|
|
13
13
|
import { PrizeDrawCollage } from "./PrizeDrawCollage";
|
|
14
14
|
import { PrizeDrawEntryActions } from "./PrizeDrawEntryActions";
|
|
15
15
|
import { HistoryTracker } from "../../history/components/HistoryTracker";
|
|
16
|
-
import { formatCurrency } from "../../../utils/number.formatter";
|
|
17
|
-
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
18
16
|
function toDescriptionHtml(raw) {
|
|
19
17
|
if (!raw)
|
|
20
18
|
return "";
|
|
@@ -63,7 +61,7 @@ export async function PrizeDrawDetailPageView({ id, initialPrizeDraw, currentUse
|
|
|
63
61
|
? initialPrizeDraw ?? undefined
|
|
64
62
|
: await productRepository.findByIdOrSlug(id).catch(() => undefined);
|
|
65
63
|
if (!product) {
|
|
66
|
-
return (_jsx(Main, { children: _jsx(Section, { className: "py-20", children: _jsx(Container, { size: "md", children: _jsxs(Stack, { align: "center", gap: "md", className: "text-center", children: [_jsx(Heading, { level: 1, className: "text-2xl font-semibold text-zinc-900 dark:text-zinc-50", children: "Prize Draw Not Found" }), _jsx(Text, { className: "text-zinc-500", children: "The prize draw you are looking for may have been removed." }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRIZE_DRAWS), className: "text-sm font-medium text-primary-600 hover:underline", children: "Browse Prize Draws" })] }) }) }) }));
|
|
64
|
+
return (_jsx(Main, { children: _jsx(Section, { className: "py-20", children: _jsx(Container, { size: "md", children: _jsxs(Stack, { align: "center", gap: "md", className: "text-center", children: [_jsx(Heading, { level: 1, className: "text-2xl font-semibold text-zinc-900 dark:text-zinc-50", children: "Prize Draw Not Found" }), _jsx(Text, { className: "text-zinc-500 dark:text-zinc-400", children: "The prize draw you are looking for may have been removed." }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRIZE_DRAWS), className: "text-sm font-medium text-primary-600 hover:underline", children: "Browse Prize Draws" })] }) }) }) }));
|
|
67
65
|
}
|
|
68
66
|
const p = product;
|
|
69
67
|
const currency = p.currency || getDefaultCurrency();
|
|
@@ -115,5 +113,5 @@ export async function PrizeDrawDetailPageView({ id, initialPrizeDraw, currentUse
|
|
|
115
113
|
price: pricePerEntry,
|
|
116
114
|
storeId: typeof p.storeId === "string" ? p.storeId : undefined,
|
|
117
115
|
storeName: storeName ?? undefined,
|
|
118
|
-
} }), _jsxs(Container, { size: "xl", className: "px-4 py-6", children: [_jsxs("div", { className: "mb-4 flex items-center justify-between flex-wrap gap-2", children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center gap-1.5 text-xs text-zinc-500 dark:text-zinc-400 flex-wrap", children: [_jsx(Link, { href: String(ROUTES.HOME), className: "hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRIZE_DRAWS), className: "hover:text-primary-600 transition-colors", children: "Prize Draws" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "text-zinc-700 dark:text-zinc-300 truncate max-w-[200px]", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => (_jsx(PrizeDrawCollage, { items: items, hideWonState: true })), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", className: "mb-2 flex-wrap", children: [_jsx(Span, { className: "inline-block rounded-full bg-fuchsia-100 dark:bg-fuchsia-900/30 px-2.5 py-0.5 text-xs font-semibold text-fuchsia-700 dark:text-fuchsia-300", children: "Prize Draw" }), _jsx(Span, { className: `inline-block rounded-full px-2.5 py-0.5 text-xs font-semibold ${statusClass(revealStatus)}`, children: statusLabel(revealStatus) }), maxPerUser !== null && (_jsxs(Span, { className: "inline-block rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-0.5 text-xs font-medium text-zinc-700 dark:text-zinc-300", children: ["Limit: ", maxPerUser, " entries per customer"] })), maxPerUser !== null && userEntriesUsed !== null && (_jsxs(Span, { className: "inline-block rounded-full bg-fuchsia-100 dark:bg-fuchsia-900/30 px-2.5 py-0.5 text-xs font-semibold text-fuchsia-700 dark:text-fuchsia-300", children: ["You have used ", userEntriesUsed, "/", maxPerUser] }))] }), _jsx(Heading, { level: 1, className: "text-xl font-bold leading-snug text-zinc-900 dark:text-zinc-50 sm:text-2xl", children: title })] }), _jsx(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-4", children: _jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "Entries" }), _jsxs(Text, { className: "text-sm font-semibold", children: [current, " / ", max, " (", remaining, " left)"] })] }), revealStart && (_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "Reveal window opens" }), _jsx(Text, { className: "text-sm font-medium", children: revealStart.toLocaleString() })] })), revealEnd && (_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "Reveal window closes" }), _jsx(Text, { className: "text-sm font-medium", children: revealEnd.toLocaleString() })] }))] }) }), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-zinc-600 dark:text-zinc-400 line-clamp-4" })), safeSeller && (_jsx(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-3", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] uppercase tracking-wide text-zinc-400 dark:text-zinc-
|
|
116
|
+
} }), _jsxs(Container, { size: "xl", className: "px-4 py-6", children: [_jsxs("div", { className: "mb-4 flex items-center justify-between flex-wrap gap-2", children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center gap-1.5 text-xs text-zinc-500 dark:text-zinc-400 flex-wrap", children: [_jsx(Link, { href: String(ROUTES.HOME), className: "hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRIZE_DRAWS), className: "hover:text-primary-600 transition-colors", children: "Prize Draws" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "text-zinc-700 dark:text-zinc-300 truncate max-w-[200px]", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => (_jsx(PrizeDrawCollage, { items: items, hideWonState: true })), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", className: "mb-2 flex-wrap", children: [_jsx(Span, { className: "inline-block rounded-full bg-fuchsia-100 dark:bg-fuchsia-900/30 px-2.5 py-0.5 text-xs font-semibold text-fuchsia-700 dark:text-fuchsia-300", children: "Prize Draw" }), _jsx(Span, { className: `inline-block rounded-full px-2.5 py-0.5 text-xs font-semibold ${statusClass(revealStatus)}`, children: statusLabel(revealStatus) }), maxPerUser !== null && (_jsxs(Span, { className: "inline-block rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-0.5 text-xs font-medium text-zinc-700 dark:text-zinc-300", children: ["Limit: ", maxPerUser, " entries per customer"] })), maxPerUser !== null && userEntriesUsed !== null && (_jsxs(Span, { className: "inline-block rounded-full bg-fuchsia-100 dark:bg-fuchsia-900/30 px-2.5 py-0.5 text-xs font-semibold text-fuchsia-700 dark:text-fuchsia-300", children: ["You have used ", userEntriesUsed, "/", maxPerUser] }))] }), _jsx(Heading, { level: 1, className: "text-xl font-bold leading-snug text-zinc-900 dark:text-zinc-50 sm:text-2xl", children: title })] }), _jsx(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-4", children: _jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "Entries" }), _jsxs(Text, { className: "text-sm font-semibold", children: [current, " / ", max, " (", remaining, " left)"] })] }), revealStart && (_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "Reveal window opens" }), _jsx(Text, { className: "text-sm font-medium", children: revealStart.toLocaleString() })] })), revealEnd && (_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "Reveal window closes" }), _jsx(Text, { className: "text-sm font-medium", children: revealEnd.toLocaleString() })] }))] }) }), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-zinc-600 dark:text-zinc-400 line-clamp-4" })), safeSeller && (_jsx(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-3", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-0.5", children: "Sold by" }), _jsx(Text, { className: "text-sm font-semibold text-zinc-800 dark:text-zinc-200", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-3 py-1.5 text-xs font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-zinc-700 dark:text-zinc-300" })) : undefined })), renderBuyBar: () => (_jsx(Div, { id: "prize-draw-buy-bar", className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5", children: _jsx(PrizeDrawEntryActions, { productId: String(product.id), productSlug: slug, title: title, thumb: thumb, pricePerEntry: pricePerEntry, currency: currency, remainingEntries: remaining, revealStatus: revealStatus, prizeGithubFileUrl: githubUrl, storeId: typeof p.storeId === "string" ? p.storeId : undefined, storeName: storeName ?? undefined }) })) }), _jsx(PrizeDrawBottomActions, { pricePerEntry: pricePerEntry, currency: currency, closed: revealStatus === "closed" || remaining === 0 })] })] }));
|
|
119
117
|
}
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useState } from "react";
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
|
-
import { Button, Stack, Text,
|
|
5
|
+
import { Button, Stack, Text, LoginRequiredModal } from "../../../ui";
|
|
6
6
|
import { ROUTES } from "../../../next";
|
|
7
7
|
import { formatCurrency } from "../../../utils/number.formatter";
|
|
8
|
-
import { useGuestCart } from "../../cart/hooks/useGuestCart";
|
|
9
|
-
import { pushCartOp } from "../../cart/utils/pending-ops";
|
|
10
8
|
import { NonRefundableConsentModal } from "./NonRefundableConsentModal";
|
|
11
9
|
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
12
10
|
import { ACTION_ID } from "../constants/action-defs";
|
|
@@ -20,8 +18,6 @@ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
|
20
18
|
*/
|
|
21
19
|
export function PrizeDrawEntryActions({ productId, productSlug, title, thumb, pricePerEntry, currency, remainingEntries, revealStatus, prizeGithubFileUrl, storeId, storeName, }) {
|
|
22
20
|
const router = useRouter();
|
|
23
|
-
const cart = useGuestCart();
|
|
24
|
-
const { showToast } = useToast();
|
|
25
21
|
const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
|
|
26
22
|
const [consentOpen, setConsentOpen] = useState(false);
|
|
27
23
|
const closed = revealStatus === "closed" || remainingEntries === 0;
|
|
@@ -31,18 +27,9 @@ export function PrizeDrawEntryActions({ productId, productSlug, title, thumb, pr
|
|
|
31
27
|
requireAuth(ACTION_ID.ENTER_PRIZE_DRAW, () => setConsentOpen(true));
|
|
32
28
|
}, [closed, requireAuth]);
|
|
33
29
|
const handleConfirm = useCallback(() => {
|
|
34
|
-
const snapshot = {
|
|
35
|
-
productTitle: title,
|
|
36
|
-
productImage: thumb,
|
|
37
|
-
price: pricePerEntry,
|
|
38
|
-
storeId,
|
|
39
|
-
storeName,
|
|
40
|
-
};
|
|
41
|
-
cart.add(productId, 1, snapshot);
|
|
42
|
-
pushCartOp({ op: "add", productId, quantity: 1, ...snapshot });
|
|
43
30
|
setConsentOpen(false);
|
|
44
|
-
|
|
45
|
-
router.push(String(ROUTES.USER.
|
|
46
|
-
}, [
|
|
47
|
-
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: [formatCurrency(pricePerEntry, currency), _jsx(Text, { as: "span", className: "ml-1 text-xs font-normal text-[var(--appkit-color-text-muted)]", children: "per entry" })] }), _jsx(Button, { action: closed ? undefined : ACTIONS.PRIZE_DRAW["
|
|
31
|
+
const slug = productSlug ?? productId;
|
|
32
|
+
router.push(`${String(ROUTES.USER.CHECKOUT)}?directItem=${encodeURIComponent(slug)}&type=prize-draw`);
|
|
33
|
+
}, [productSlug, productId, router]);
|
|
34
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: [formatCurrency(pricePerEntry, currency), _jsx(Text, { as: "span", className: "ml-1 text-xs font-normal text-[var(--appkit-color-text-muted)]", children: "per entry" })] }), _jsx(Button, { action: closed ? undefined : ACTIONS.PRIZE_DRAW["buy-now"], variant: "primary", size: "md", className: "w-full", disabled: closed, onClick: handleEnter, children: closed ? "Draw closed" : undefined }), prizeGithubFileUrl ? (_jsx("a", { href: prizeGithubFileUrl, target: "_blank", rel: "noopener noreferrer", className: "text-center text-xs font-medium text-primary-600 underline-offset-4 hover:underline dark:text-primary-400", children: "View RNG source code on GitHub \u2192" })) : null, _jsx(Text, { className: "text-center text-xs text-[var(--appkit-color-text-muted)]", children: "Winners chosen by Node.js crypto.randomInt \u2014 fully auditable. Entries are locked once paid; refunds only if the prize pool is exhausted." }), _jsx(NonRefundableConsentModal, { open: consentOpen, listingType: "prize-draw", itemTitle: title, priceLabel: formatCurrency(pricePerEntry, currency), onCancel: () => setConsentOpen(false), onConfirm: handleConfirm }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
48
35
|
}
|
|
@@ -15,7 +15,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
15
15
|
* nor LetItRip admins can influence the outcome.
|
|
16
16
|
*/
|
|
17
17
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
18
|
-
import { Button, Div, Heading, LoginRequiredModal, Modal, Stack, Text } from "../../../ui";
|
|
18
|
+
import { Button, Div, Heading, LoginRequiredModal, Modal, Stack, Text, useToast } from "../../../ui";
|
|
19
19
|
import { isAuthError } from "../../../utils/auth-error";
|
|
20
20
|
import { PrizeDrawCollage } from "./PrizeDrawCollage";
|
|
21
21
|
const REVEAL_DURATION_MS = 3200; // total animation length
|
|
@@ -46,6 +46,7 @@ export function PrizeRevealModal({ open, onClose, items, orderId, productId, onR
|
|
|
46
46
|
const [errorMessage, setErrorMessage] = useState("");
|
|
47
47
|
const [effectiveRngUrl, setEffectiveRngUrl] = useState(rngSourceUrl);
|
|
48
48
|
const [showLoginModal, setShowLoginModal] = useState(false);
|
|
49
|
+
const { showToast } = useToast();
|
|
49
50
|
const cycleTimerRef = useRef(null);
|
|
50
51
|
const endTimerRef = useRef(null);
|
|
51
52
|
// Clean up timers on close/unmount so we don't leak between sessions.
|
|
@@ -113,7 +114,9 @@ export function PrizeRevealModal({ open, onClose, items, orderId, productId, onR
|
|
|
113
114
|
return;
|
|
114
115
|
}
|
|
115
116
|
setPhase("error");
|
|
116
|
-
|
|
117
|
+
const msg = err instanceof Error ? err.message : "Reveal request failed";
|
|
118
|
+
setErrorMessage(msg);
|
|
119
|
+
showToast(msg, "error");
|
|
117
120
|
return;
|
|
118
121
|
}
|
|
119
122
|
if (response.refunded) {
|
|
@@ -10,6 +10,11 @@ export interface ProductDetailActionsProps {
|
|
|
10
10
|
/** Denormalised store display name — used by the cart group header. */
|
|
11
11
|
storeName?: string;
|
|
12
12
|
inStock: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* "desktop" renders inline buttons. "mobile" registers actions via
|
|
15
|
+
* useBottomActions() for the layout-level BottomActions bar and renders
|
|
16
|
+
* nothing inline (only the auth modal).
|
|
17
|
+
*/
|
|
13
18
|
variant?: "desktop" | "mobile";
|
|
14
19
|
}
|
|
15
20
|
/**
|
|
@@ -17,4 +22,4 @@ export interface ProductDetailActionsProps {
|
|
|
17
22
|
* to the server APIs with guest-fallback. Replaces the previously-unwired
|
|
18
23
|
* placeholder buttons inside ProductDetailPageView.
|
|
19
24
|
*/
|
|
20
|
-
export declare function ProductDetailActions({ productId, productSlug, productTitle, productImage, price, storeId, storeName, inStock, variant, }: ProductDetailActionsProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export declare function ProductDetailActions({ productId, productSlug, productTitle, productImage, price, currency, storeId, storeName, inStock, variant, }: ProductDetailActionsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -10,12 +10,14 @@ import { addToGuestWishlist } from "../../wishlist/utils/guest-wishlist";
|
|
|
10
10
|
import { ROUTES } from "../../../next";
|
|
11
11
|
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
12
12
|
import { ACTION_ID } from "../constants/action-defs";
|
|
13
|
+
import { useBottomActions } from "../../layout/hooks/useBottomActions";
|
|
14
|
+
import { formatCurrency } from "../../../utils/number.formatter";
|
|
13
15
|
/**
|
|
14
16
|
* Wires the three primary product-detail CTAs (Buy Now / Add to Cart / Wishlist)
|
|
15
17
|
* to the server APIs with guest-fallback. Replaces the previously-unwired
|
|
16
18
|
* placeholder buttons inside ProductDetailPageView.
|
|
17
19
|
*/
|
|
18
|
-
export function ProductDetailActions({ productId, productSlug, productTitle, productImage, price, storeId, storeName, inStock, variant = "desktop", }) {
|
|
20
|
+
export function ProductDetailActions({ productId, productSlug, productTitle, productImage, price, currency, storeId, storeName, inStock, variant = "desktop", }) {
|
|
19
21
|
const router = useRouter();
|
|
20
22
|
const { showToast } = useToast();
|
|
21
23
|
const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
|
|
@@ -84,13 +86,41 @@ export function ProductDetailActions({ productId, productSlug, productTitle, pro
|
|
|
84
86
|
void doWishlistAdd();
|
|
85
87
|
});
|
|
86
88
|
}
|
|
89
|
+
// Mobile: register actions with layout-level BottomActions bar
|
|
90
|
+
useBottomActions(variant === "mobile"
|
|
91
|
+
? {
|
|
92
|
+
actions: [
|
|
93
|
+
{
|
|
94
|
+
id: ACTION_ID.ADD_TO_WISHLIST,
|
|
95
|
+
label: wishlisted ? "♥ Saved" : "♡ Wishlist",
|
|
96
|
+
variant: "ghost",
|
|
97
|
+
grow: false,
|
|
98
|
+
disabled: busy !== null || wishlisted,
|
|
99
|
+
onClick: handleWishlist,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: ACTION_ID.ADD_TO_CART,
|
|
103
|
+
label: busy === "cart" ? "Adding…" : "Add to Cart",
|
|
104
|
+
variant: "outline",
|
|
105
|
+
disabled: !inStock || busy !== null,
|
|
106
|
+
onClick: () => handleAddToCart(),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: ACTION_ID.BUY_NOW,
|
|
110
|
+
label: !inStock ? "Out of Stock" : busy === "buy" ? "Loading…" : "Buy Now",
|
|
111
|
+
variant: "primary",
|
|
112
|
+
disabled: !inStock || busy !== null,
|
|
113
|
+
onClick: handleBuyNow,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
infoLabel: price != null && currency
|
|
117
|
+
? formatCurrency(price, currency)
|
|
118
|
+
: undefined,
|
|
119
|
+
}
|
|
120
|
+
: {});
|
|
87
121
|
const authModal = (_jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage }));
|
|
88
122
|
if (variant === "mobile") {
|
|
89
|
-
return (
|
|
90
|
-
? "Out of Stock"
|
|
91
|
-
: busy === "buy"
|
|
92
|
-
? "Loading…"
|
|
93
|
-
: "Buy Now" })] }));
|
|
123
|
+
return _jsx(_Fragment, { children: authModal });
|
|
94
124
|
}
|
|
95
125
|
return (_jsxs(_Fragment, { children: [authModal, _jsxs(Stack, { gap: "sm", children: [_jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: !inStock || busy !== null, onClick: handleBuyNow, children: !inStock ? "Out of Stock" : busy === "buy" ? "Loading…" : "Buy Now" }), _jsx(Button, { variant: "secondary", size: "md", className: "w-full", disabled: !inStock || busy !== null, onClick: () => handleAddToCart(), children: !inStock ? "Out of Stock" : busy === "cart" ? "Adding…" : "Add to Cart" }), _jsx(Button, { variant: "ghost", size: "md", className: "w-full", disabled: busy !== null || wishlisted, onClick: handleWishlist, children: wishlisted ? "♥ In Wishlist" : "♡ Add to Wishlist" })] })] }));
|
|
96
126
|
}
|