@mohasinac/appkit 2.3.1 → 2.3.2
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/client.d.ts +12 -8
- package/dist/client.js +7 -4
- package/dist/constants/api-endpoints.d.ts +4 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/core/contact-submissions.repository.d.ts +32 -0
- package/dist/core/contact-submissions.repository.js +49 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +1 -0
- package/dist/features/about/components/HowPayoutsWorkView.js +1 -1
- package/dist/features/account/components/AddressFilters.d.ts +5 -0
- package/dist/features/account/components/AddressFilters.js +20 -0
- package/dist/features/account/components/AddressesIndexListing.d.ts +6 -0
- package/dist/features/account/components/AddressesIndexListing.js +51 -0
- package/dist/features/account/components/UserSidebar.d.ts +7 -3
- package/dist/features/account/components/UserSidebar.js +55 -7
- package/dist/features/account/hooks/useAddresses.d.ts +7 -0
- package/dist/features/account/hooks/useAddresses.js +12 -1
- package/dist/features/admin/actions/admin-read-actions.d.ts +12 -0
- package/dist/features/admin/actions/admin-read-actions.js +18 -0
- package/dist/features/admin/components/AdminBlogView.js +26 -2
- package/dist/features/admin/components/AdminCarouselView.js +18 -2
- package/dist/features/admin/components/AdminCategoriesView.js +27 -2
- package/dist/features/admin/components/AdminContactView.d.ts +4 -0
- package/dist/features/admin/components/AdminContactView.js +50 -0
- package/dist/features/admin/components/AdminFaqsView.js +19 -2
- package/dist/features/admin/components/AdminListingScaffold.d.ts +11 -2
- package/dist/features/admin/components/AdminListingScaffold.js +14 -3
- package/dist/features/admin/components/AdminNewsletterView.d.ts +4 -0
- package/dist/features/admin/components/AdminNewsletterView.js +50 -0
- package/dist/features/admin/components/AdminProductsView.js +30 -2
- package/dist/features/admin/components/AdminReviewsView.js +26 -2
- package/dist/features/admin/components/AdminStoresView.js +17 -2
- package/dist/features/admin/components/AdminUsersView.js +27 -2
- package/dist/features/admin/components/DataTable.d.ts +2 -1
- package/dist/features/admin/components/DataTable.js +18 -4
- package/dist/features/admin/components/index.d.ts +6 -0
- package/dist/features/admin/components/index.js +3 -0
- package/dist/features/admin/hooks/useAdminListingData.d.ts +3 -1
- package/dist/features/admin/hooks/useAdminListingData.js +12 -7
- package/dist/features/admin/server.d.ts +3 -0
- package/dist/features/admin/server.js +2 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +93 -47
- package/dist/features/auctions/components/AuctionFilters.d.ts +8 -0
- package/dist/features/auctions/components/AuctionFilters.js +12 -0
- package/dist/features/auctions/components/AuctionsListView.d.ts +6 -1
- package/dist/features/auctions/components/AuctionsListView.js +37 -5
- package/dist/features/auctions/schemas/index.d.ts +4 -4
- package/dist/features/blog/components/BlogFeaturedCard.d.ts +1 -7
- package/dist/features/blog/components/BlogFeaturedCard.js +4 -5
- package/dist/features/blog/components/BlogFilters.js +2 -1
- package/dist/features/blog/components/BlogIndexListing.js +14 -9
- package/dist/features/blog/components/BlogIndexPageView.d.ts +6 -1
- package/dist/features/blog/components/BlogIndexPageView.js +10 -2
- package/dist/features/blog/components/BlogListView.d.ts +2 -1
- package/dist/features/blog/components/BlogListView.js +10 -3
- package/dist/features/categories/components/CategoriesIndexListing.d.ts +1 -1
- package/dist/features/categories/components/CategoriesIndexListing.js +41 -38
- package/dist/features/categories/components/CategoriesIndexPageView.d.ts +6 -1
- package/dist/features/categories/components/CategoriesIndexPageView.js +41 -2
- package/dist/features/categories/components/CategoryDetailPageView.js +13 -6
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +5 -0
- package/dist/features/categories/components/CategoryDetailTabs.js +17 -0
- package/dist/features/categories/components/CategoryFilters.js +2 -1
- package/dist/features/categories/components/CategoryGrid.d.ts +2 -1
- package/dist/features/categories/components/CategoryGrid.js +8 -6
- package/dist/features/categories/components/CategoryProductsListing.js +22 -11
- package/dist/features/categories/components/index.d.ts +2 -0
- package/dist/features/categories/components/index.js +1 -0
- package/dist/features/categories/hooks/useCategories.d.ts +20 -0
- package/dist/features/categories/hooks/useCategories.js +50 -1
- package/dist/features/categories/hooks/useCategoryTree.d.ts +17 -0
- package/dist/features/categories/hooks/useCategoryTree.js +65 -0
- package/dist/features/events/components/AdminEventEditorView.d.ts +6 -0
- package/dist/features/events/components/AdminEventEditorView.js +203 -0
- package/dist/features/events/components/AdminEventsView.js +28 -2
- package/dist/features/events/components/EventCard.js +4 -2
- package/dist/features/events/components/EventFilters.js +2 -1
- package/dist/features/events/components/EventsIndexListing.js +40 -10
- package/dist/features/events/components/EventsListPageView.d.ts +6 -1
- package/dist/features/events/components/EventsListPageView.js +40 -7
- package/dist/features/events/components/index.d.ts +2 -0
- package/dist/features/events/components/index.js +1 -0
- package/dist/features/events/hooks/useEvents.js +2 -0
- package/dist/features/events/types/index.d.ts +1 -0
- package/dist/features/homepage/components/BlogArticlesSection.js +1 -1
- package/dist/features/homepage/components/EventsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedAuctionsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedPreOrdersSection.js +1 -1
- package/dist/features/homepage/components/FeaturedProductsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedStoresSection.js +1 -1
- package/dist/features/homepage/components/HeroCarousel.js +1 -1
- package/dist/features/homepage/components/MarketplaceHomepageView.js +27 -17
- package/dist/features/homepage/components/SectionCarousel.js +4 -4
- package/dist/features/homepage/components/ShopByCategorySection.js +2 -2
- package/dist/features/homepage/schemas/firestore.d.ts +1 -1
- package/dist/features/homepage/schemas/firestore.js +2 -1
- package/dist/features/layout/AppLayoutShell.d.ts +6 -2
- package/dist/features/layout/AppLayoutShell.js +7 -3
- package/dist/features/pre-orders/components/MarketplacePreorderCard.d.ts +3 -1
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +6 -2
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +80 -12
- package/dist/features/pre-orders/components/PreOrderFilters.d.ts +8 -0
- package/dist/features/pre-orders/components/PreOrderFilters.js +21 -0
- package/dist/features/pre-orders/components/PreOrdersIndexListing.d.ts +2 -1
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +69 -10
- package/dist/features/pre-orders/components/PreOrdersListView.d.ts +6 -1
- package/dist/features/pre-orders/components/PreOrdersListView.js +26 -7
- package/dist/features/pre-orders/components/index.d.ts +2 -0
- package/dist/features/pre-orders/components/index.js +1 -0
- package/dist/features/products/components/AuctionsIndexListing.d.ts +2 -1
- package/dist/features/products/components/AuctionsIndexListing.js +61 -9
- package/dist/features/products/components/InteractiveProductCard.d.ts +2 -4
- package/dist/features/products/components/InteractiveProductCard.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.d.ts +1 -1
- package/dist/features/products/components/ProductDetailPageView.js +116 -25
- package/dist/features/products/components/ProductFilters.d.ts +6 -11
- package/dist/features/products/components/ProductFilters.js +5 -3
- package/dist/features/products/components/ProductGrid.d.ts +8 -2
- package/dist/features/products/components/ProductGrid.js +20 -5
- package/dist/features/products/components/ProductTabsShell.d.ts +3 -11
- package/dist/features/products/components/ProductTabsShell.js +14 -14
- package/dist/features/products/components/ProductsIndexListing.js +73 -9
- package/dist/features/products/components/ProductsIndexPageView.d.ts +6 -1
- package/dist/features/products/components/ProductsIndexPageView.js +39 -6
- package/dist/features/products/components/RelatedProductsCarousel.d.ts +7 -0
- package/dist/features/products/components/RelatedProductsCarousel.js +11 -0
- package/dist/features/products/components/index.d.ts +1 -0
- package/dist/features/products/components/index.js +1 -0
- package/dist/features/products/hooks/useProducts.js +16 -0
- package/dist/features/products/repository/products.repository.d.ts +8 -0
- package/dist/features/products/repository/products.repository.js +2 -0
- package/dist/features/products/schemas/index.d.ts +8 -8
- package/dist/features/products/types/index.d.ts +12 -0
- package/dist/features/promotions/components/CouponsIndexListing.d.ts +9 -0
- package/dist/features/promotions/components/CouponsIndexListing.js +86 -0
- package/dist/features/promotions/components/PromotionsView.d.ts +11 -5
- package/dist/features/promotions/components/PromotionsView.js +6 -1
- package/dist/features/promotions/components/index.d.ts +4 -2
- package/dist/features/promotions/components/index.js +2 -1
- package/dist/features/reviews/components/ReviewDetailPageView.d.ts +4 -0
- package/dist/features/reviews/components/ReviewDetailPageView.js +20 -0
- package/dist/features/reviews/components/ReviewDetailShell.d.ts +7 -0
- package/dist/features/reviews/components/ReviewDetailShell.js +80 -0
- package/dist/features/reviews/components/ReviewFilters.d.ts +3 -3
- package/dist/features/reviews/components/ReviewFilters.js +5 -4
- package/dist/features/reviews/components/ReviewsIndexListing.d.ts +4 -3
- package/dist/features/reviews/components/ReviewsIndexListing.js +35 -51
- package/dist/features/reviews/components/ReviewsIndexPageView.d.ts +6 -1
- package/dist/features/reviews/components/ReviewsIndexPageView.js +49 -3
- package/dist/features/reviews/components/ReviewsList.js +9 -1
- package/dist/features/reviews/components/index.d.ts +1 -0
- package/dist/features/reviews/hooks/useReviews.js +15 -1
- package/dist/features/reviews/types/index.d.ts +6 -1
- package/dist/features/seller/components/SellerSidebar.d.ts +8 -4
- package/dist/features/seller/components/SellerSidebar.js +6 -4
- package/dist/features/seller/components/index.d.ts +30 -0
- package/dist/features/seller/components/index.js +17 -0
- package/dist/features/seller/hooks/useSellerStore.d.ts +2 -0
- package/dist/features/seller/hooks/useSellerStore.js +2 -0
- package/dist/features/seller/permission-map.d.ts +4 -2
- package/dist/features/seller/permission-map.js +16 -14
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/stores/api/[storeSlug]/reviews/route.d.ts +1 -1
- package/dist/features/stores/api/[storeSlug]/reviews/route.js +24 -19
- package/dist/features/stores/components/InteractiveStoreCard.d.ts +0 -5
- package/dist/features/stores/components/InteractiveStoreCard.js +9 -9
- package/dist/features/stores/components/StoreAuctionsListing.js +27 -9
- package/dist/features/stores/components/StoreDetailLayoutView.js +2 -0
- package/dist/features/stores/components/StoreFilters.d.ts +5 -0
- package/dist/features/stores/components/StoreFilters.js +20 -0
- package/dist/features/stores/components/StoreHeader.js +2 -2
- package/dist/features/stores/components/StorePreOrdersListing.d.ts +5 -0
- package/dist/features/stores/components/StorePreOrdersListing.js +40 -0
- package/dist/features/stores/components/StorePreOrdersPageView.d.ts +4 -0
- package/dist/features/stores/components/StorePreOrdersPageView.js +21 -0
- package/dist/features/stores/components/StoreProductsListing.js +21 -11
- package/dist/features/stores/components/StoreReviewsListing.js +2 -7
- package/dist/features/stores/components/StoresIndexListing.js +42 -8
- package/dist/features/stores/components/StoresIndexPageView.d.ts +6 -1
- package/dist/features/stores/components/StoresIndexPageView.js +9 -2
- package/dist/features/stores/components/index.d.ts +3 -0
- package/dist/features/stores/components/index.js +1 -0
- package/dist/features/stores/hooks/useStores.d.ts +7 -1
- package/dist/features/stores/hooks/useStores.js +16 -3
- package/dist/features/stores/schemas/index.d.ts +2 -2
- package/dist/features/stores/types/index.d.ts +3 -0
- package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +20 -0
- package/dist/features/wishlist/hooks/useGuestWishlist.js +49 -0
- package/dist/features/wishlist/hooks/useWishlistCount.d.ts +7 -0
- package/dist/features/wishlist/hooks/useWishlistCount.js +31 -0
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +56 -0
- package/dist/features/wishlist/hooks/useWishlistWithGuest.js +57 -0
- package/dist/features/wishlist/index.d.ts +3 -0
- package/dist/features/wishlist/index.js +3 -0
- package/dist/features/wishlist/utils/guest-wishlist.d.ts +22 -0
- package/dist/features/wishlist/utils/guest-wishlist.js +70 -0
- package/dist/index.d.ts +50 -1
- package/dist/index.js +63 -1
- package/dist/next/routing/route-map.d.ts +70 -36
- package/dist/next/routing/route-map.js +30 -22
- package/dist/seed/addresses-seed-data.js +62 -261
- package/dist/seed/beyblade-seed-data.d.ts +7 -0
- package/dist/seed/beyblade-seed-data.js +947 -0
- package/dist/seed/bids-seed-data.d.ts +10 -2
- package/dist/seed/bids-seed-data.js +220 -1071
- package/dist/seed/blog-posts-seed-data.d.ts +2 -2
- package/dist/seed/blog-posts-seed-data.js +455 -117
- package/dist/seed/cart-seed-data.d.ts +9 -9
- package/dist/seed/cart-seed-data.js +73 -74
- package/dist/seed/coupons-seed-data.d.ts +3 -4
- package/dist/seed/coupons-seed-data.js +3 -509
- package/dist/seed/events-seed-data.d.ts +2 -2
- package/dist/seed/events-seed-data.js +315 -476
- package/dist/seed/faq-seed-data.d.ts +18 -41
- package/dist/seed/faq-seed-data.js +1059 -1172
- package/dist/seed/hot-wheels-seed-data.d.ts +7 -0
- package/dist/seed/hot-wheels-seed-data.js +1365 -0
- package/dist/seed/index.d.ts +6 -1
- package/dist/seed/index.js +6 -1
- package/dist/seed/pokemon-carousel-slides-seed-data.d.ts +4 -2
- package/dist/seed/pokemon-carousel-slides-seed-data.js +152 -268
- package/dist/seed/pokemon-categories-seed-data.d.ts +18 -21
- package/dist/seed/pokemon-categories-seed-data.js +424 -1004
- package/dist/seed/pokemon-coupons-seed-data.d.ts +6 -0
- package/dist/seed/pokemon-coupons-seed-data.js +465 -0
- package/dist/seed/pokemon-homepage-sections-seed-data.d.ts +3 -2
- package/dist/seed/pokemon-homepage-sections-seed-data.js +67 -289
- package/dist/seed/pokemon-products-seed-data.js +662 -0
- package/dist/seed/pokemon-seed-bundle.d.ts +32 -11
- package/dist/seed/pokemon-seed-bundle.js +41 -11
- package/dist/seed/pokemon-stores-seed-data.d.ts +2 -3
- package/dist/seed/pokemon-stores-seed-data.js +56 -31
- package/dist/seed/pokemon-users-seed-data.d.ts +2 -2
- package/dist/seed/pokemon-users-seed-data.js +245 -261
- package/dist/seed/reviews-seed-data.d.ts +17 -2
- package/dist/seed/reviews-seed-data.js +519 -483
- package/dist/seed/site-settings-seed-data.js +14 -14
- package/dist/seed/store-addresses-seed-data.js +68 -50
- package/dist/seed/transformers-seed-data.d.ts +7 -0
- package/dist/seed/transformers-seed-data.js +510 -0
- package/dist/seed/wishlists-seed-data.d.ts +5 -1
- package/dist/seed/wishlists-seed-data.js +82 -4
- package/dist/server.d.ts +1 -0
- package/dist/server.js +2 -0
- package/dist/tokens/index.d.ts +6 -0
- package/dist/tokens/index.js +2 -0
- package/dist/ui/components/BaseListingCard.js +24 -26
- package/dist/ui/components/BaseListingCard.style.css +5 -5
- package/dist/ui/components/HorizontalScroller.d.ts +1 -1
- package/dist/ui/components/HorizontalScroller.js +19 -5
- package/dist/ui/components/SideDrawer.style.css +3 -11
- package/dist/ui/rich-text/RichText.js +19 -1
- package/package.json +1 -1
|
@@ -1,25 +1,84 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
import { Search, SlidersHorizontal, LayoutGrid, List, X } from "lucide-react";
|
|
3
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
4
6
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
5
|
-
import {
|
|
7
|
+
import { Pagination, SortDropdown } from "../../../ui";
|
|
6
8
|
import { ROUTES } from "../../../next";
|
|
7
9
|
import { MarketplacePreorderCard } from "./MarketplacePreorderCard";
|
|
8
|
-
|
|
10
|
+
import { PreOrderFilters } from "./PreOrderFilters";
|
|
11
|
+
import { useSession } from "../../../react/contexts/SessionContext";
|
|
12
|
+
import { useWishlistWithGuest } from "../../wishlist/hooks/useWishlistWithGuest";
|
|
13
|
+
import { apiClient } from "../../../http";
|
|
14
|
+
const PREORDER_SORT_OPTIONS = [
|
|
15
|
+
{ value: "-createdAt", label: "Newest First" },
|
|
16
|
+
{ value: "preOrderDeliveryDate", label: "Delivery Soon" },
|
|
17
|
+
{ value: "price", label: "Price: Low to High" },
|
|
18
|
+
{ value: "-price", label: "Price: High to Low" },
|
|
19
|
+
];
|
|
20
|
+
export function PreOrdersIndexListing({ initialData, categorySlug }) {
|
|
9
21
|
const table = useUrlTable({ defaults: { pageSize: "24", sort: "-createdAt" } });
|
|
22
|
+
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
23
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
24
|
+
const [view, setView] = useState(table.get("view") || "grid");
|
|
25
|
+
const { user } = useSession();
|
|
26
|
+
const wl = useWishlistWithGuest(user?.uid ?? null);
|
|
10
27
|
const params = {
|
|
11
28
|
q: table.get("q") || undefined,
|
|
12
29
|
category: table.get("category") || undefined,
|
|
13
|
-
|
|
30
|
+
categorySlug: categorySlug || undefined,
|
|
31
|
+
minPrice: table.get("minPrice") ? Number(table.get("minPrice")) : undefined,
|
|
32
|
+
maxPrice: table.get("maxPrice") ? Number(table.get("maxPrice")) : undefined,
|
|
33
|
+
storeId: table.get("storeId") || undefined,
|
|
34
|
+
preOrderStatus: table.get("preOrderStatus") || undefined,
|
|
35
|
+
dateFrom: table.get("dateFrom") || undefined,
|
|
36
|
+
dateTo: table.get("dateTo") || undefined,
|
|
37
|
+
sort: table.get("sort") || "-createdAt",
|
|
14
38
|
page: table.getNumber("page", 1),
|
|
15
39
|
perPage: table.getNumber("pageSize", 24),
|
|
16
40
|
isPreOrder: true,
|
|
17
41
|
};
|
|
18
42
|
const { products: preOrders, total, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
const commitSearch = useCallback(() => {
|
|
44
|
+
table.set("q", searchInput.trim());
|
|
45
|
+
table.setPage(1);
|
|
46
|
+
}, [searchInput, table]);
|
|
47
|
+
const handleSearchKeyDown = (e) => {
|
|
48
|
+
if (e.key === "Enter")
|
|
49
|
+
commitSearch();
|
|
50
|
+
};
|
|
51
|
+
const handleViewToggle = (next) => {
|
|
52
|
+
setView(next);
|
|
53
|
+
table.set("view", next);
|
|
54
|
+
};
|
|
55
|
+
const closeFilters = () => setFilterOpen(false);
|
|
56
|
+
const wishlistActions = {
|
|
57
|
+
addToWishlist: async (productId) => {
|
|
58
|
+
if (wl.isGuest)
|
|
59
|
+
wl.guestWishlist?.add(productId, "preorder");
|
|
60
|
+
else
|
|
61
|
+
await apiClient.post("/api/user/wishlist", { productId });
|
|
62
|
+
},
|
|
63
|
+
removeFromWishlist: async (productId) => {
|
|
64
|
+
if (wl.isGuest)
|
|
65
|
+
wl.guestWishlist?.remove(productId, "preorder");
|
|
66
|
+
else
|
|
67
|
+
await apiClient.delete(`/api/user/wishlist/${productId}`);
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
const handleAddToCart = useCallback(async (product) => {
|
|
71
|
+
try {
|
|
72
|
+
await apiClient.post("/api/cart", { productId: product.id, quantity: 1, isPreOrder: true });
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// silently ignore cart errors on listing page
|
|
76
|
+
}
|
|
77
|
+
}, []);
|
|
78
|
+
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
|
|
79
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx("div", { className: "sticky top-0 z-20 border-b border-zinc-200 dark:border-slate-700 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm py-2.5 px-4", children: _jsxs("div", { className: "flex items-center gap-2.5 max-w-full", children: [_jsxs("button", { type: "button", onClick: () => setFilterOpen(true), className: "flex shrink-0 items-center gap-2 rounded-lg border border-zinc-300 dark:border-slate-600 px-3.5 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsx("span", { className: "hidden sm:inline", children: "Filters" })] }), _jsxs("div", { className: "flex flex-1 items-center overflow-hidden rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-900", children: [_jsx("input", { type: "text", value: searchInput, onChange: (e) => setSearchInput(e.target.value), onKeyDown: handleSearchKeyDown, placeholder: "Search pre-orders...", className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none" }), _jsx("button", { type: "button", onClick: commitSearch, className: "flex shrink-0 items-center justify-center px-3 py-2 text-zinc-400 hover:text-primary dark:hover:text-primary-400 transition-colors", "aria-label": "Search", children: _jsx(Search, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5 text-sm text-zinc-500 dark:text-zinc-400", children: [_jsx("span", { className: "hidden md:inline whitespace-nowrap", children: "Sort by" }), _jsx(SortDropdown, { value: table.get("sort") || "-createdAt", onChange: (v) => { table.set("sort", v); table.setPage(1); }, options: PREORDER_SORT_OPTIONS })] }), _jsxs("div", { className: "flex shrink-0 items-center rounded-lg border border-zinc-300 dark:border-slate-600 overflow-hidden", children: [_jsx("button", { type: "button", onClick: () => handleViewToggle("grid"), "aria-label": "Grid view", className: `p-2 transition-colors ${view === "grid"
|
|
80
|
+
? "bg-primary text-white"
|
|
81
|
+
: "text-zinc-500 hover:bg-zinc-50 dark:hover:bg-slate-800 dark:text-zinc-400"}`, children: _jsx(LayoutGrid, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => handleViewToggle("list"), "aria-label": "List view", className: `p-2 transition-colors ${view === "list"
|
|
82
|
+
? "bg-primary text-white"
|
|
83
|
+
: "text-zinc-500 hover:bg-zinc-50 dark:hover:bg-slate-800 dark:text-zinc-400"}`, children: _jsx(List, { className: "h-4 w-4" }) })] })] }) }), _jsxs("div", { className: "py-6", children: [isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No pre-orders found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions }, product.id))) })) : (_jsx("div", { className: gridClass, children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions }, product.id))) })), totalPages > 1 && (_jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: closeFilters }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsx("button", { type: "button", onClick: closeFilters, "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(PreOrderFilters, { table: table, currencyPrefix: "\u20B9" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsx("button", { type: "button", onClick: closeFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors", children: "Apply filters" }) })] })] }))] }));
|
|
25
84
|
}
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
type SearchParams = Record<string, string | string[]>;
|
|
2
|
+
export interface PreOrdersListViewProps {
|
|
3
|
+
searchParams?: SearchParams;
|
|
4
|
+
}
|
|
5
|
+
export declare function PreOrdersListView({ searchParams }: PreOrdersListViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
6
|
+
export {};
|
|
@@ -3,14 +3,33 @@ import { productRepository } from "../../../repositories";
|
|
|
3
3
|
import { Container, Main, Heading, Section } from "../../../ui";
|
|
4
4
|
import { AdSlot } from "../../homepage/components/AdSlot";
|
|
5
5
|
import { PreOrdersIndexListing } from "./PreOrdersIndexListing";
|
|
6
|
-
|
|
6
|
+
function sp(params, key) {
|
|
7
|
+
const v = params[key];
|
|
8
|
+
return Array.isArray(v) ? v[0] ?? "" : v ?? "";
|
|
9
|
+
}
|
|
10
|
+
function buildPreOrderFilters(params) {
|
|
11
|
+
const parts = ["status==published", "isPreOrder==true"];
|
|
12
|
+
const minPrice = sp(params, "minPrice");
|
|
13
|
+
const maxPrice = sp(params, "maxPrice");
|
|
14
|
+
if (minPrice)
|
|
15
|
+
parts.push(`price>=${minPrice}`);
|
|
16
|
+
if (maxPrice)
|
|
17
|
+
parts.push(`price<=${maxPrice}`);
|
|
18
|
+
const store = sp(params, "storeId");
|
|
19
|
+
if (store)
|
|
20
|
+
parts.push(`sellerId==${store}`);
|
|
21
|
+
const preOrderStatus = sp(params, "preOrderStatus");
|
|
22
|
+
if (preOrderStatus)
|
|
23
|
+
parts.push(`preOrderStatus==${preOrderStatus}`);
|
|
24
|
+
return parts.join(",");
|
|
25
|
+
}
|
|
26
|
+
export async function PreOrdersListView({ searchParams = {} }) {
|
|
27
|
+
const sort = sp(searchParams, "sort") || "-createdAt";
|
|
28
|
+
const page = Number(sp(searchParams, "page")) || 1;
|
|
29
|
+
const pageSize = Number(sp(searchParams, "pageSize")) || 24;
|
|
30
|
+
const filters = buildPreOrderFilters(searchParams);
|
|
7
31
|
const result = await productRepository
|
|
8
|
-
.list({
|
|
9
|
-
filters: "status==published,isPreOrder==true",
|
|
10
|
-
sorts: "-createdAt",
|
|
11
|
-
page: 1,
|
|
12
|
-
pageSize: 24,
|
|
13
|
-
})
|
|
32
|
+
.list({ filters, sorts: sort, page, pageSize })
|
|
14
33
|
.catch(() => null);
|
|
15
34
|
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" })] }) }) }));
|
|
16
35
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { PreorderBadge, PreorderCard } from "./PreorderCard";
|
|
2
|
+
export { PreOrderFilters } from "./PreOrderFilters";
|
|
3
|
+
export type { PreOrderFiltersProps } from "./PreOrderFilters";
|
|
2
4
|
export { MarketplacePreorderCard, type MarketplacePreorderCardData, type MarketplacePreorderCardLabels, type MarketplacePreorderCardProps, } from "./MarketplacePreorderCard";
|
|
3
5
|
export { PreOrdersListView } from "./PreOrdersListView";
|
|
4
6
|
export { PreOrdersIndexListing } from "./PreOrdersIndexListing";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { PreorderBadge, PreorderCard } from "./PreorderCard";
|
|
2
|
+
export { PreOrderFilters } from "./PreOrderFilters";
|
|
2
3
|
export { MarketplacePreorderCard, } from "./MarketplacePreorderCard";
|
|
3
4
|
export { PreOrdersListView } from "./PreOrdersListView";
|
|
4
5
|
export { PreOrdersIndexListing } from "./PreOrdersIndexListing";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export interface AuctionsIndexListingProps {
|
|
2
2
|
initialData?: any;
|
|
3
|
+
categorySlug?: string;
|
|
3
4
|
}
|
|
4
|
-
export declare function AuctionsIndexListing({ initialData }: AuctionsIndexListingProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function AuctionsIndexListing({ initialData, categorySlug }: AuctionsIndexListingProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,23 +1,75 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
import { Search, SlidersHorizontal, LayoutGrid, List, X } from "lucide-react";
|
|
3
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
4
6
|
import { useProducts } from "../hooks/useProducts";
|
|
5
|
-
import {
|
|
7
|
+
import { Pagination, SortDropdown } from "../../../ui";
|
|
6
8
|
import { MarketplaceAuctionGrid } from "../../auctions/components/MarketplaceAuctionGrid";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
+
import { AuctionFilters } from "../../auctions/components/AuctionFilters";
|
|
10
|
+
import { useSession } from "../../../react/contexts/SessionContext";
|
|
11
|
+
import { useWishlistWithGuest } from "../../wishlist/hooks/useWishlistWithGuest";
|
|
12
|
+
import { apiClient } from "../../../http";
|
|
13
|
+
const AUCTION_SORT_OPTIONS = [
|
|
14
|
+
{ value: "auctionEndDate", label: "Ending Soonest" },
|
|
15
|
+
{ value: "-currentBid", label: "Highest Bid" },
|
|
16
|
+
{ value: "-createdAt", label: "Newest" },
|
|
17
|
+
{ value: "price", label: "Price: Low to High" },
|
|
18
|
+
{ value: "-price", label: "Price: High to Low" },
|
|
19
|
+
];
|
|
20
|
+
export function AuctionsIndexListing({ initialData, categorySlug }) {
|
|
9
21
|
const table = useUrlTable({ defaults: { pageSize: "24", sort: "auctionEndDate" } });
|
|
22
|
+
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
23
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
24
|
+
const [view, setView] = useState(table.get("view") || "grid");
|
|
25
|
+
const { user } = useSession();
|
|
26
|
+
const wl = useWishlistWithGuest(user?.uid ?? null);
|
|
10
27
|
const params = {
|
|
11
28
|
q: table.get("q") || undefined,
|
|
12
29
|
category: table.get("category") || undefined,
|
|
13
|
-
|
|
30
|
+
categorySlug: categorySlug || undefined,
|
|
31
|
+
minBid: table.get("minBid") ? Number(table.get("minBid")) : undefined,
|
|
32
|
+
maxBid: table.get("maxBid") ? Number(table.get("maxBid")) : undefined,
|
|
33
|
+
storeId: table.get("storeId") || undefined,
|
|
34
|
+
dateFrom: table.get("dateFrom") || undefined,
|
|
35
|
+
dateTo: table.get("dateTo") || undefined,
|
|
36
|
+
sort: table.get("sort") || "auctionEndDate",
|
|
14
37
|
page: table.getNumber("page", 1),
|
|
15
38
|
perPage: table.getNumber("pageSize", 24),
|
|
16
39
|
isAuction: true,
|
|
17
40
|
};
|
|
18
41
|
const { products: auctions, total, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
42
|
+
const commitSearch = useCallback(() => {
|
|
43
|
+
table.set("q", searchInput.trim());
|
|
44
|
+
table.setPage(1);
|
|
45
|
+
}, [searchInput, table]);
|
|
46
|
+
const handleSearchKeyDown = (e) => {
|
|
47
|
+
if (e.key === "Enter")
|
|
48
|
+
commitSearch();
|
|
49
|
+
};
|
|
50
|
+
const handleViewToggle = (next) => {
|
|
51
|
+
setView(next);
|
|
52
|
+
table.set("view", next);
|
|
53
|
+
};
|
|
54
|
+
const closeFilters = () => setFilterOpen(false);
|
|
55
|
+
const wishlistActions = {
|
|
56
|
+
addToWishlist: async (productId) => {
|
|
57
|
+
if (wl.isGuest)
|
|
58
|
+
wl.guestWishlist?.add(productId, "auction");
|
|
59
|
+
else
|
|
60
|
+
await apiClient.post("/api/user/wishlist", { productId });
|
|
61
|
+
},
|
|
62
|
+
removeFromWishlist: async (productId) => {
|
|
63
|
+
if (wl.isGuest)
|
|
64
|
+
wl.guestWishlist?.remove(productId, "auction");
|
|
65
|
+
else
|
|
66
|
+
await apiClient.delete(`/api/user/wishlist/${productId}`);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-3 gap-4";
|
|
70
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx("div", { className: "sticky top-0 z-20 border-b border-zinc-200 dark:border-slate-700 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm py-2.5 px-4", children: _jsxs("div", { className: "flex items-center gap-2.5 max-w-full", children: [_jsxs("button", { type: "button", onClick: () => setFilterOpen(true), className: "flex shrink-0 items-center gap-2 rounded-lg border border-zinc-300 dark:border-slate-600 px-3.5 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsx("span", { className: "hidden sm:inline", children: "Filters" })] }), _jsxs("div", { className: "flex flex-1 items-center overflow-hidden rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-900", children: [_jsx("input", { type: "text", value: searchInput, onChange: (e) => setSearchInput(e.target.value), onKeyDown: handleSearchKeyDown, placeholder: "Search auctions...", className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none" }), _jsx("button", { type: "button", onClick: commitSearch, className: "flex shrink-0 items-center justify-center px-3 py-2 text-zinc-400 hover:text-primary dark:hover:text-primary-400 transition-colors", "aria-label": "Search", children: _jsx(Search, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5 text-sm text-zinc-500 dark:text-zinc-400", children: [_jsx("span", { className: "hidden md:inline whitespace-nowrap", children: "Sort by" }), _jsx(SortDropdown, { value: table.get("sort") || "auctionEndDate", onChange: (v) => { table.set("sort", v); table.setPage(1); }, options: AUCTION_SORT_OPTIONS })] }), _jsxs("div", { className: "flex shrink-0 items-center rounded-lg border border-zinc-300 dark:border-slate-600 overflow-hidden", children: [_jsx("button", { type: "button", onClick: () => handleViewToggle("grid"), "aria-label": "Grid view", className: `p-2 transition-colors ${view === "grid"
|
|
71
|
+
? "bg-primary text-white"
|
|
72
|
+
: "text-zinc-500 hover:bg-zinc-50 dark:hover:bg-slate-800 dark:text-zinc-400"}`, children: _jsx(LayoutGrid, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => handleViewToggle("list"), "aria-label": "List view", className: `p-2 transition-colors ${view === "list"
|
|
73
|
+
? "bg-primary text-white"
|
|
74
|
+
: "text-zinc-500 hover:bg-zinc-50 dark:hover:bg-slate-800 dark:text-zinc-400"}`, children: _jsx(List, { className: "h-4 w-4" }) })] })] }) }), _jsxs("div", { className: "py-6", children: [isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-4" : gridClass, wishlistActions: wishlistActions })), totalPages > 1 && (_jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: closeFilters }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsx("button", { type: "button", onClick: closeFilters, "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(AuctionFilters, { table: table, currencyPrefix: "\u20B9" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsx("button", { type: "button", onClick: closeFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors", children: "Apply filters" }) })] })] }))] }));
|
|
23
75
|
}
|
|
@@ -9,11 +9,9 @@ export interface InteractiveProductCardProps {
|
|
|
9
9
|
selectable?: boolean;
|
|
10
10
|
isSelected?: boolean;
|
|
11
11
|
onSelect?: (id: string, selected: boolean) => void;
|
|
12
|
-
/** Whether this product is currently in the user's wishlist */
|
|
13
12
|
isWishlisted?: boolean;
|
|
14
|
-
/** Called when the wishlist icon is toggled */
|
|
15
13
|
onToggleWishlist?: (productId: string) => void;
|
|
16
|
-
/** Called when the card body is clicked in non-selectable mode */
|
|
17
14
|
onAddToCart?: (product: ProductItem) => void;
|
|
15
|
+
onBuyNow?: (product: ProductItem) => void;
|
|
18
16
|
}
|
|
19
|
-
export declare function InteractiveProductCard({ product, href, className, selectable, isSelected, onSelect, isWishlisted, onToggleWishlist, onAddToCart, }: InteractiveProductCardProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function InteractiveProductCard({ product, href, className, selectable, isSelected, onSelect, isWishlisted, onToggleWishlist, onAddToCart, onBuyNow, }: InteractiveProductCardProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import { ProductCard } from "./ProductGrid";
|
|
4
|
-
export function InteractiveProductCard({ product, href, className, selectable, isSelected, onSelect, isWishlisted = false, onToggleWishlist, onAddToCart, }) {
|
|
4
|
+
export function InteractiveProductCard({ product, href, className, selectable, isSelected, onSelect, isWishlisted = false, onToggleWishlist, onAddToCart, onBuyNow, }) {
|
|
5
5
|
return (_jsx(Link, { href: href, className: [
|
|
6
6
|
"block",
|
|
7
7
|
selectable ? "cursor-pointer" : "",
|
|
@@ -13,5 +13,5 @@ export function InteractiveProductCard({ product, href, className, selectable, i
|
|
|
13
13
|
e.preventDefault();
|
|
14
14
|
onSelect(product.id, !isSelected);
|
|
15
15
|
}
|
|
16
|
-
: undefined, children: _jsx(ProductCard, { product: product, className: className, isWishlisted: isWishlisted, onAddToWishlist: onToggleWishlist,
|
|
16
|
+
: undefined, children: _jsx(ProductCard, { product: product, className: className, isWishlisted: isWishlisted, onAddToWishlist: onToggleWishlist, onAddToCart: onAddToCart, onBuyNow: onBuyNow }) }));
|
|
17
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export interface ProductDetailPageViewProps {
|
|
2
2
|
slug: string;
|
|
3
3
|
}
|
|
4
|
-
export declare function ProductDetailPageView({ slug }: ProductDetailPageViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
4
|
+
export declare function ProductDetailPageView({ slug, }: ProductDetailPageViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
@@ -5,25 +5,28 @@ import { ROUTES } from "../../../next";
|
|
|
5
5
|
import { getDefaultCurrency } from "../../../core/baseline-resolver";
|
|
6
6
|
import { Button, Container, Div, Heading, Main, RichText, Row, Section, Span, Stack, Text, } from "../../../ui";
|
|
7
7
|
import { normalizeRichTextHtml } from "../../../utils/string.formatter";
|
|
8
|
+
import { formatCurrency } from "../../../utils/number.formatter";
|
|
9
|
+
import { safeDisplayName } from "../../../security";
|
|
8
10
|
import { ReviewsList } from "../../reviews/components/ReviewsList";
|
|
9
|
-
import { ProductGrid } from "./ProductGrid";
|
|
10
|
-
import { RelatedProducts } from "./RelatedProducts";
|
|
11
|
-
import { BuyBar } from "./BuyBar";
|
|
12
11
|
import { ProductDetailView } from "./ProductDetailView";
|
|
13
12
|
import { ProductGalleryClient } from "./ProductGalleryClient";
|
|
14
13
|
import { ProductTabsShell } from "./ProductTabsShell";
|
|
14
|
+
import { ProductFeatureBadges } from "./ProductFeatureBadges";
|
|
15
|
+
import { RelatedProductsCarousel } from "./RelatedProductsCarousel";
|
|
16
|
+
import { BuyBar } from "./BuyBar";
|
|
15
17
|
// ---------------------------------------------------------------------------
|
|
16
18
|
// Helpers
|
|
17
19
|
// ---------------------------------------------------------------------------
|
|
18
|
-
function
|
|
20
|
+
function toProductItem(doc) {
|
|
19
21
|
return {
|
|
20
22
|
id: String(doc.id ?? ""),
|
|
21
|
-
title: String(doc.
|
|
23
|
+
title: String(doc.title ?? doc.name ?? ""),
|
|
22
24
|
price: typeof doc.price === "number" ? doc.price : 0,
|
|
25
|
+
originalPrice: typeof doc.originalPrice === "number" ? doc.originalPrice : undefined,
|
|
23
26
|
mainImage: Array.isArray(doc.images)
|
|
24
27
|
? doc.images[0]
|
|
25
|
-
: typeof doc.
|
|
26
|
-
? doc.
|
|
28
|
+
: typeof doc.mainImage === "string"
|
|
29
|
+
? doc.mainImage
|
|
27
30
|
: undefined,
|
|
28
31
|
status: doc.status ?? "published",
|
|
29
32
|
slug: typeof doc.slug === "string" ? doc.slug : undefined,
|
|
@@ -32,7 +35,7 @@ function mapToProductItem(doc) {
|
|
|
32
35
|
reviewCount: typeof doc.reviewCount === "number" ? doc.reviewCount : undefined,
|
|
33
36
|
};
|
|
34
37
|
}
|
|
35
|
-
function
|
|
38
|
+
function toReview(doc) {
|
|
36
39
|
const images = Array.isArray(doc.images)
|
|
37
40
|
? doc.images.map((url) => ({ url }))
|
|
38
41
|
: undefined;
|
|
@@ -60,41 +63,129 @@ function mapToReview(doc) {
|
|
|
60
63
|
createdAt,
|
|
61
64
|
};
|
|
62
65
|
}
|
|
66
|
+
function toDescriptionHtml(raw) {
|
|
67
|
+
if (!raw)
|
|
68
|
+
return "";
|
|
69
|
+
const s = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
70
|
+
return normalizeRichTextHtml(s);
|
|
71
|
+
}
|
|
72
|
+
function StarRating({ value }) {
|
|
73
|
+
const full = Math.floor(value);
|
|
74
|
+
const half = value - full >= 0.5;
|
|
75
|
+
return (_jsx(Row, { align: "center", gap: "xs", children: Array.from({ length: 5 }).map((_, i) => (_jsx(Span, { className: i < full
|
|
76
|
+
? "text-yellow-400 text-sm"
|
|
77
|
+
: i === full && half
|
|
78
|
+
? "text-yellow-300 text-sm"
|
|
79
|
+
: "text-zinc-300 dark:text-zinc-600 text-sm", children: "\u2605" }, i))) }));
|
|
80
|
+
}
|
|
63
81
|
// ---------------------------------------------------------------------------
|
|
64
82
|
// Component
|
|
65
83
|
// ---------------------------------------------------------------------------
|
|
66
|
-
export async function ProductDetailPageView({ slug }) {
|
|
67
|
-
const product = await productRepository
|
|
84
|
+
export async function ProductDetailPageView({ slug, }) {
|
|
85
|
+
const product = await productRepository
|
|
86
|
+
.findByIdOrSlug(slug)
|
|
87
|
+
.catch(() => undefined);
|
|
68
88
|
if (!product) {
|
|
69
89
|
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: "Product Not Found" }), _jsx(Text, { className: "text-zinc-500", children: "The product you are looking for may have been removed or the link is incorrect." }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRODUCTS), className: "text-sm font-medium text-primary-600 hover:underline", children: "Browse Products" })] }) }) }) }));
|
|
70
90
|
}
|
|
71
91
|
const p = product;
|
|
72
92
|
const currency = p.currency || getDefaultCurrency();
|
|
73
|
-
|
|
74
|
-
|
|
93
|
+
// -- Derived values ---------------------------------------------------------
|
|
94
|
+
const title = String(p.title ?? p.name ?? "");
|
|
95
|
+
const price = typeof p.price === "number" ? p.price : null;
|
|
96
|
+
const originalPrice = typeof p.originalPrice === "number" ? p.originalPrice : null;
|
|
97
|
+
const discount = price && originalPrice && originalPrice > price
|
|
98
|
+
? Math.round(((originalPrice - price) / originalPrice) * 100)
|
|
75
99
|
: null;
|
|
76
100
|
const images = Array.isArray(p.images)
|
|
77
101
|
? p.images
|
|
78
|
-
: typeof p.
|
|
79
|
-
? [p.
|
|
102
|
+
: typeof p.mainImage === "string"
|
|
103
|
+
? [p.mainImage]
|
|
80
104
|
: [];
|
|
81
|
-
const
|
|
105
|
+
const stockQuantity = typeof p.stockQuantity === "number" ? p.stockQuantity : null;
|
|
106
|
+
const availableQuantity = typeof p.availableQuantity === "number"
|
|
107
|
+
? p.availableQuantity
|
|
108
|
+
: null;
|
|
109
|
+
const effectiveStock = availableQuantity ?? stockQuantity;
|
|
110
|
+
const inStock = effectiveStock !== null ? effectiveStock > 0 : p.status === "published";
|
|
111
|
+
const avgRating = typeof p.avgRating === "number" ? p.avgRating : null;
|
|
112
|
+
const reviewCount = typeof p.reviewCount === "number" ? p.reviewCount : null;
|
|
113
|
+
const category = typeof p.category === "string" ? p.category : null;
|
|
114
|
+
const subcategory = typeof p.subcategory === "string" ? p.subcategory : null;
|
|
115
|
+
const brand = typeof p.brand === "string" ? p.brand : null;
|
|
116
|
+
const condition = typeof p.condition === "string" ? p.condition : null;
|
|
117
|
+
const tags = Array.isArray(p.tags) ? p.tags : [];
|
|
118
|
+
const features = Array.isArray(p.features)
|
|
119
|
+
? p.features
|
|
120
|
+
: [];
|
|
121
|
+
const ingredients = Array.isArray(p.ingredients)
|
|
122
|
+
? p.ingredients
|
|
123
|
+
: [];
|
|
124
|
+
const howToUse = Array.isArray(p.howToUse)
|
|
125
|
+
? p.howToUse
|
|
126
|
+
: [];
|
|
82
127
|
const specs = Array.isArray(p.specifications)
|
|
83
128
|
? p.specifications
|
|
84
129
|
: [];
|
|
85
|
-
|
|
130
|
+
const shippingInfo = typeof p.shippingInfo === "string" ? p.shippingInfo : null;
|
|
131
|
+
const returnPolicy = typeof p.returnPolicy === "string" ? p.returnPolicy : null;
|
|
132
|
+
const shippingPaidBy = p.shippingPaidBy;
|
|
133
|
+
const freeShipping = shippingPaidBy === "seller";
|
|
134
|
+
const featured = p.featured === true;
|
|
135
|
+
const sellerName = typeof p.sellerName === "string" ? p.sellerName : null;
|
|
136
|
+
const safeSeller = sellerName
|
|
137
|
+
? safeDisplayName(sellerName, "")
|
|
138
|
+
: null;
|
|
139
|
+
const descriptionHtml = toDescriptionHtml(p.description);
|
|
140
|
+
// -- Fetch reviews + related in parallel ------------------------------------
|
|
86
141
|
const [reviewDocs, relatedDocs] = await Promise.all([
|
|
87
|
-
reviewRepository
|
|
88
|
-
|
|
89
|
-
|
|
142
|
+
reviewRepository
|
|
143
|
+
.findApprovedByProduct(product.id)
|
|
144
|
+
.catch(() => []),
|
|
145
|
+
category
|
|
146
|
+
? productRepository.findByCategory(category).catch(() => [])
|
|
90
147
|
: Promise.resolve([]),
|
|
91
148
|
]);
|
|
92
|
-
const reviews = reviewDocs.map(
|
|
149
|
+
const reviews = reviewDocs.map(toReview);
|
|
93
150
|
const relatedItems = relatedDocs
|
|
94
151
|
.filter((r) => r.id !== product.id)
|
|
95
|
-
.slice(0,
|
|
96
|
-
.map(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
152
|
+
.slice(0, 8)
|
|
153
|
+
.map(toProductItem);
|
|
154
|
+
const formattedPrice = price !== null ? formatCurrency(price, currency) : null;
|
|
155
|
+
const formattedOriginal = originalPrice !== null ? formatCurrency(originalPrice, currency) : null;
|
|
156
|
+
return (_jsx(Main, { children: _jsxs(Container, { size: "xl", className: "px-4 py-6", children: [_jsx(ProductDetailView, { renderBreadcrumb: () => (_jsxs("nav", { "aria-label": "Breadcrumb", className: "mb-4 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.PRODUCTS), className: "hover:text-primary-600 transition-colors", children: "Products" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "capitalize", children: category })] })), subcategory && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "capitalize", children: subcategory })] }))] })), renderGallery: () => (_jsx(ProductGalleryClient, { images: images, productName: title || undefined })), renderInfo: () => (_jsxs(Stack, { gap: "sm", children: [_jsxs(Div, { children: [condition && (_jsx(Span, { className: "mb-1.5 inline-block rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-0.5 text-xs font-medium capitalize text-zinc-600 dark:text-zinc-300", children: condition === "new"
|
|
157
|
+
? "Brand New"
|
|
158
|
+
: condition === "like_new"
|
|
159
|
+
? "Like New"
|
|
160
|
+
: condition === "refurbished"
|
|
161
|
+
? "Refurbished"
|
|
162
|
+
: condition === "used"
|
|
163
|
+
? "Used"
|
|
164
|
+
: condition })), _jsx(Heading, { level: 1, className: "text-xl font-bold leading-snug text-zinc-900 dark:text-zinc-50 sm:text-2xl", children: title || "Untitled Product" })] }), avgRating !== null && (_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(StarRating, { value: avgRating }), _jsxs(Span, { className: "text-sm text-zinc-600 dark:text-zinc-400", children: [avgRating.toFixed(1), reviewCount ? ` (${reviewCount} reviews)` : ""] }), _jsxs(Span, { className: `ml-auto rounded-full px-2.5 py-0.5 text-xs font-medium ${inStock
|
|
165
|
+
? "bg-emerald-50 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400"
|
|
166
|
+
: "bg-red-50 text-red-700 dark:bg-red-900/30 dark:text-red-400"}`, children: [inStock ? "✓ In Stock" : "✗ Out of Stock", inStock && effectiveStock !== null && effectiveStock <= 10
|
|
167
|
+
? ` — only ${effectiveStock} left`
|
|
168
|
+
: ""] })] })), formattedPrice && (_jsxs(Row, { align: "baseline", gap: "sm", children: [_jsx(Span, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formattedPrice }), formattedOriginal && discount && (_jsxs(_Fragment, { children: [_jsx(Span, { className: "text-sm text-zinc-400 line-through dark:text-zinc-500", children: formattedOriginal }), _jsxs(Span, { className: "rounded-full bg-red-500 px-2 py-0.5 text-xs font-bold text-white", children: ["-", discount, "%"] })] }))] })), avgRating === null && (_jsxs(Span, { className: `inline-flex w-fit items-center gap-1.5 rounded-full px-3 py-1 text-xs font-medium ${inStock
|
|
169
|
+
? "bg-emerald-50 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400"
|
|
170
|
+
: "bg-red-50 text-red-700 dark:bg-red-900/30 dark:text-red-400"}`, children: [inStock ? "✓ In Stock" : "✗ Out of Stock", inStock && effectiveStock !== null && effectiveStock <= 10
|
|
171
|
+
? ` — only ${effectiveStock} left`
|
|
172
|
+
: ""] })), (category || brand) && (_jsxs(Row, { align: "center", gap: "xs", className: "text-xs text-zinc-400 dark:text-zinc-500 flex-wrap", children: [category && _jsx(Span, { className: "capitalize", children: category }), category && brand && _jsx(Span, { children: "\u203A" }), brand && _jsx(Span, { className: "font-medium text-zinc-600 dark:text-zinc-300", children: brand })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, returnable: returnPolicy != null && returnPolicy.length > 0, labels: {
|
|
173
|
+
featured: "Featured",
|
|
174
|
+
fasterDelivery: "Faster Delivery",
|
|
175
|
+
ratedSeller: "Rated Seller",
|
|
176
|
+
condition: "Condition",
|
|
177
|
+
conditionNew: "New",
|
|
178
|
+
conditionUsed: "Used",
|
|
179
|
+
conditionBroken: "For Parts",
|
|
180
|
+
conditionRefurbished: "Refurbished",
|
|
181
|
+
returnable: "Returnable",
|
|
182
|
+
freeShipping: "Free Shipping",
|
|
183
|
+
codAvailable: "Cash on Delivery",
|
|
184
|
+
wishlistCount: (n) => `${n} wishlisted`,
|
|
185
|
+
categoryProductCount: (n, cat) => `${n} in ${cat}`,
|
|
186
|
+
} }), 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 && (_jsxs(Row, { align: "center", gap: "xs", className: "border-t border-zinc-100 dark:border-zinc-800 pt-3", children: [_jsx(Span, { className: "text-xs text-zinc-500", children: "Sold by" }), _jsx(Span, { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: safeSeller })] }))] })), renderActions: () => (_jsxs(Div, { 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: [formattedPrice && (_jsxs(Div, { children: [_jsx(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formattedPrice }), inStock && effectiveStock !== null && effectiveStock <= 10 && (_jsxs(Text, { className: "mt-0.5 text-xs text-amber-600 dark:text-amber-400", children: ["Only ", effectiveStock, " left \u2014 order soon!"] }))] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: !inStock, children: inStock ? "Add to Cart" : "Out of Stock" }), _jsx(Button, { variant: "secondary", size: "md", className: "w-full", children: "\u2661 Add to Wishlist" })] }), (shippingInfo || returnPolicy || freeShipping) && (_jsxs(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4 space-y-2.5", children: [freeShipping && (_jsxs(Row, { align: "start", gap: "sm", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-emerald-500", children: "\uD83D\uDE9A" }), _jsxs(Div, { children: [_jsx(Text, { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Free Delivery" }), shippingInfo && (_jsx(Text, { className: "text-xs text-zinc-500", children: shippingInfo }))] })] })), !freeShipping && shippingInfo && (_jsxs(Row, { align: "start", gap: "sm", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-zinc-400", children: "\uD83D\uDCE6" }), _jsx(Text, { className: "text-xs text-zinc-600 dark:text-zinc-400", children: shippingInfo })] })), returnPolicy && (_jsxs(Row, { align: "start", gap: "sm", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-zinc-400", children: "\u21BA" }), _jsx(Text, { className: "text-xs text-zinc-600 dark:text-zinc-400", children: returnPolicy })] }))] })), tags.length > 0 && (_jsxs(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: [_jsx(Text, { className: "mb-2 text-xs font-medium text-zinc-500 dark:text-zinc-400", children: "Tags" }), _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: [
|
|
187
|
+
{ icon: "🔒", label: "Secure\nPayment" },
|
|
188
|
+
{ icon: "✓", label: "Verified\nSeller" },
|
|
189
|
+
{ icon: "⭐", label: "Quality\nGuarantee" },
|
|
190
|
+
].map(({ icon, label }) => (_jsxs(Div, { className: "flex flex-col items-center gap-1 text-xs text-zinc-500 dark:text-zinc-400 min-w-[60px]", children: [_jsx(Span, { className: "text-base", children: icon }), _jsx(Span, { className: "whitespace-pre-line leading-tight", children: label })] }, label))) }) })] })), 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, specsContent: specs.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: specs.map((s, 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", children: s.name }), _jsxs("dd", { className: "flex-1 text-zinc-600 dark:text-zinc-400", children: [s.value, s.unit ? ` ${s.unit}` : ""] })] }, i))) })) : undefined, ingredientsContent: ingredients.length > 0 ? (_jsx("ul", { className: "space-y-2", children: ingredients.map((item, i) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "mt-1 flex-shrink-0 h-1.5 w-1.5 rounded-full bg-primary-400" }), item] }, i))) })) : undefined, howToUseContent: howToUse.length > 0 ? (_jsx("ol", { className: "space-y-3", children: howToUse.map((step, i) => (_jsxs("li", { className: "flex items-start gap-3 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "flex-shrink-0 flex h-6 w-6 items-center justify-center rounded-full bg-primary-100 dark:bg-primary-900/30 text-xs font-bold text-primary-700 dark:text-primary-300", children: i + 1 }), step] }, i))) })) : undefined, reviewsContent: _jsx(ReviewsList, { reviews: reviews, emptyLabel: "No reviews yet \u2014 be the first to review this product." }) })), renderRelated: () => relatedItems.length > 0 ? (_jsx(RelatedProductsCarousel, { items: relatedItems })) : null }), _jsxs(BuyBar, { children: [formattedPrice && (_jsx(Span, { className: "mr-auto text-sm font-bold text-zinc-900 dark:text-zinc-50", children: formattedPrice })), _jsx(Button, { variant: "primary", size: "sm", className: "flex-1", disabled: !inStock, children: inStock ? "Add to Cart" : "Out of Stock" })] })] }) }));
|
|
100
191
|
}
|
|
@@ -68,23 +68,18 @@ export declare function getProductSortOptions(variant: ProductFilterVariant): Re
|
|
|
68
68
|
}>;
|
|
69
69
|
export interface ProductFiltersProps {
|
|
70
70
|
table: UrlTable;
|
|
71
|
-
/** Pass category options loaded from the API */
|
|
72
71
|
categoryOptions?: FacetOption[];
|
|
73
|
-
/** Pass brand options loaded from the API */
|
|
74
72
|
brandOptions?: FacetOption[];
|
|
75
|
-
/**
|
|
73
|
+
/** Store options (formerly sellerOptions) */
|
|
74
|
+
storeOptions?: FacetOption[];
|
|
75
|
+
/** @deprecated use storeOptions */
|
|
76
76
|
sellerOptions?: FacetOption[];
|
|
77
|
-
/** Override defaults with dynamic tag options */
|
|
78
77
|
tagOptions?: FacetOption[];
|
|
79
|
-
/** Show status filter (admin / seller only) */
|
|
80
78
|
showStatus?: boolean;
|
|
81
|
-
/** Preferred variant contract (overrides showStatus defaults). */
|
|
82
79
|
variant?: ProductFilterVariant;
|
|
83
80
|
statusOptions?: FacetOption[];
|
|
84
|
-
/**
|
|
85
|
-
* Currency prefix shown in price range labels (e.g. "₹", "$", "€").
|
|
86
|
-
* Injected by the consumer app — never hard-coded in appkit.
|
|
87
|
-
*/
|
|
88
81
|
currencyPrefix?: string;
|
|
82
|
+
/** Show free-shipping toggle */
|
|
83
|
+
showShipping?: boolean;
|
|
89
84
|
}
|
|
90
|
-
export declare function ProductFilters({ table, categoryOptions, brandOptions, sellerOptions, tagOptions, showStatus, variant, statusOptions, currencyPrefix, }: ProductFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
85
|
+
export declare function ProductFilters({ table, categoryOptions, brandOptions, storeOptions, sellerOptions, tagOptions, showStatus, variant, statusOptions, currencyPrefix, showShipping, }: ProductFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useTranslations } from "next-intl";
|
|
3
3
|
import { FilterFacetSection } from "../../filters/FilterFacetSection";
|
|
4
4
|
import { RangeFilter } from "../../filters/RangeFilter";
|
|
5
|
+
import { SwitchFilter } from "../../filters/SwitchFilter";
|
|
5
6
|
import { Div } from "../../../ui";
|
|
6
7
|
export const PRODUCT_FILTER_KEYS = {
|
|
7
8
|
admin: [
|
|
@@ -71,7 +72,8 @@ export function getProductSortOptions(variant) {
|
|
|
71
72
|
return PRODUCT_PUBLIC_SORT_OPTIONS;
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
|
-
export function ProductFilters({ table, categoryOptions = [], brandOptions = [],
|
|
75
|
+
export function ProductFilters({ table, categoryOptions = [], brandOptions = [], storeOptions = [], sellerOptions, tagOptions = [], showStatus = false, variant, statusOptions, currencyPrefix = "", showShipping = true, }) {
|
|
76
|
+
const resolvedStoreOptions = storeOptions.length > 0 ? storeOptions : (sellerOptions ?? []);
|
|
75
77
|
const t = useTranslations("filters");
|
|
76
78
|
const conditionOptions = [
|
|
77
79
|
{ value: "new", label: t("conditionNew") },
|
|
@@ -88,7 +90,7 @@ export function ProductFilters({ table, categoryOptions = [], brandOptions = [],
|
|
|
88
90
|
? table.get("category").split("|").filter(Boolean)
|
|
89
91
|
: [];
|
|
90
92
|
const selectedBrands = table.get("brand") ? [table.get("brand")] : [];
|
|
91
|
-
const selectedSellers = table.get("
|
|
93
|
+
const selectedSellers = table.get("storeId") ? [table.get("storeId")] : [];
|
|
92
94
|
const selectedConditions = table.get("condition")
|
|
93
95
|
? table.get("condition").split("|").filter(Boolean)
|
|
94
96
|
: [];
|
|
@@ -100,5 +102,5 @@ export function ProductFilters({ table, categoryOptions = [], brandOptions = [],
|
|
|
100
102
|
: [];
|
|
101
103
|
const resolvedVariant = variant ?? (showStatus ? "admin" : "public");
|
|
102
104
|
const shouldShowStatus = resolvedVariant !== "public" || showStatus;
|
|
103
|
-
return (_jsxs(Div, { children: [categoryOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("category"), options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), searchable: categoryOptions.length > 6, defaultCollapsed: true })), _jsx(FilterFacetSection, { title: t("condition"), options: conditionOptions, selected: selectedConditions, onChange: (vals) => table.set("condition", vals.join("|")), searchable: false, defaultCollapsed: true }), _jsx(RangeFilter, { title: t("priceRange"), minValue: table.get("minPrice"), maxValue: table.get("maxPrice"), onMinChange: (v) => table.set("minPrice", v), onMaxChange: (v) => table.set("maxPrice", v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 500, minPlaceholder: t("minPrice"), maxPlaceholder: t("maxPrice"), defaultCollapsed: true }), brandOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("brand"), options: brandOptions, selected: selectedBrands, onChange: (vals) => table.set("brand", vals[0] ?? ""), searchable: brandOptions.length > 6, defaultCollapsed: true })),
|
|
105
|
+
return (_jsxs(Div, { children: [categoryOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("category"), options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), searchable: categoryOptions.length > 6, defaultCollapsed: true })), _jsx(FilterFacetSection, { title: t("condition"), options: conditionOptions, selected: selectedConditions, onChange: (vals) => table.set("condition", vals.join("|")), searchable: false, defaultCollapsed: true }), _jsx(RangeFilter, { title: t("priceRange"), minValue: table.get("minPrice"), maxValue: table.get("maxPrice"), onMinChange: (v) => table.set("minPrice", v), onMaxChange: (v) => table.set("maxPrice", v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 500, minPlaceholder: t("minPrice"), maxPlaceholder: t("maxPrice"), defaultCollapsed: true }), brandOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("brand"), options: brandOptions, selected: selectedBrands, onChange: (vals) => table.set("brand", vals[0] ?? ""), searchable: brandOptions.length > 6, defaultCollapsed: true })), resolvedStoreOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("store"), options: resolvedStoreOptions, selected: selectedSellers, onChange: (vals) => table.set("storeId", vals[0] ?? ""), searchable: resolvedStoreOptions.length > 6, defaultCollapsed: true })), showShipping && (_jsx(SwitchFilter, { title: t("shipping"), label: t("freeShippingOnly"), checked: table.get("freeShipping") === "true", onChange: (v) => table.set("freeShipping", v ? "true" : ""), defaultCollapsed: true })), tagOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("tags"), options: tagOptions, selected: selectedTags, onChange: (vals) => table.set("tags", vals.join("|")), searchable: tagOptions.length > 6, defaultCollapsed: true })), shouldShowStatus && (_jsx(FilterFacetSection, { title: t("status"), options: statusOptions ?? defaultStatusOptions, selected: selectedStatuses, onChange: (vals) => table.set("status", vals.join("|")), searchable: false, defaultCollapsed: true }))] }));
|
|
104
106
|
}
|