@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.
Files changed (253) hide show
  1. package/dist/client.d.ts +12 -8
  2. package/dist/client.js +7 -4
  3. package/dist/constants/api-endpoints.d.ts +4 -0
  4. package/dist/constants/api-endpoints.js +2 -0
  5. package/dist/core/contact-submissions.repository.d.ts +32 -0
  6. package/dist/core/contact-submissions.repository.js +49 -0
  7. package/dist/core/index.d.ts +2 -0
  8. package/dist/core/index.js +1 -0
  9. package/dist/features/about/components/HowPayoutsWorkView.js +1 -1
  10. package/dist/features/account/components/AddressFilters.d.ts +5 -0
  11. package/dist/features/account/components/AddressFilters.js +20 -0
  12. package/dist/features/account/components/AddressesIndexListing.d.ts +6 -0
  13. package/dist/features/account/components/AddressesIndexListing.js +51 -0
  14. package/dist/features/account/components/UserSidebar.d.ts +7 -3
  15. package/dist/features/account/components/UserSidebar.js +55 -7
  16. package/dist/features/account/hooks/useAddresses.d.ts +7 -0
  17. package/dist/features/account/hooks/useAddresses.js +12 -1
  18. package/dist/features/admin/actions/admin-read-actions.d.ts +12 -0
  19. package/dist/features/admin/actions/admin-read-actions.js +18 -0
  20. package/dist/features/admin/components/AdminBlogView.js +26 -2
  21. package/dist/features/admin/components/AdminCarouselView.js +18 -2
  22. package/dist/features/admin/components/AdminCategoriesView.js +27 -2
  23. package/dist/features/admin/components/AdminContactView.d.ts +4 -0
  24. package/dist/features/admin/components/AdminContactView.js +50 -0
  25. package/dist/features/admin/components/AdminFaqsView.js +19 -2
  26. package/dist/features/admin/components/AdminListingScaffold.d.ts +11 -2
  27. package/dist/features/admin/components/AdminListingScaffold.js +14 -3
  28. package/dist/features/admin/components/AdminNewsletterView.d.ts +4 -0
  29. package/dist/features/admin/components/AdminNewsletterView.js +50 -0
  30. package/dist/features/admin/components/AdminProductsView.js +30 -2
  31. package/dist/features/admin/components/AdminReviewsView.js +26 -2
  32. package/dist/features/admin/components/AdminStoresView.js +17 -2
  33. package/dist/features/admin/components/AdminUsersView.js +27 -2
  34. package/dist/features/admin/components/DataTable.d.ts +2 -1
  35. package/dist/features/admin/components/DataTable.js +18 -4
  36. package/dist/features/admin/components/index.d.ts +6 -0
  37. package/dist/features/admin/components/index.js +3 -0
  38. package/dist/features/admin/hooks/useAdminListingData.d.ts +3 -1
  39. package/dist/features/admin/hooks/useAdminListingData.js +12 -7
  40. package/dist/features/admin/server.d.ts +3 -0
  41. package/dist/features/admin/server.js +2 -0
  42. package/dist/features/auctions/components/AuctionDetailPageView.js +93 -47
  43. package/dist/features/auctions/components/AuctionFilters.d.ts +8 -0
  44. package/dist/features/auctions/components/AuctionFilters.js +12 -0
  45. package/dist/features/auctions/components/AuctionsListView.d.ts +6 -1
  46. package/dist/features/auctions/components/AuctionsListView.js +37 -5
  47. package/dist/features/auctions/schemas/index.d.ts +4 -4
  48. package/dist/features/blog/components/BlogFeaturedCard.d.ts +1 -7
  49. package/dist/features/blog/components/BlogFeaturedCard.js +4 -5
  50. package/dist/features/blog/components/BlogFilters.js +2 -1
  51. package/dist/features/blog/components/BlogIndexListing.js +14 -9
  52. package/dist/features/blog/components/BlogIndexPageView.d.ts +6 -1
  53. package/dist/features/blog/components/BlogIndexPageView.js +10 -2
  54. package/dist/features/blog/components/BlogListView.d.ts +2 -1
  55. package/dist/features/blog/components/BlogListView.js +10 -3
  56. package/dist/features/categories/components/CategoriesIndexListing.d.ts +1 -1
  57. package/dist/features/categories/components/CategoriesIndexListing.js +41 -38
  58. package/dist/features/categories/components/CategoriesIndexPageView.d.ts +6 -1
  59. package/dist/features/categories/components/CategoriesIndexPageView.js +41 -2
  60. package/dist/features/categories/components/CategoryDetailPageView.js +13 -6
  61. package/dist/features/categories/components/CategoryDetailTabs.d.ts +5 -0
  62. package/dist/features/categories/components/CategoryDetailTabs.js +17 -0
  63. package/dist/features/categories/components/CategoryFilters.js +2 -1
  64. package/dist/features/categories/components/CategoryGrid.d.ts +2 -1
  65. package/dist/features/categories/components/CategoryGrid.js +8 -6
  66. package/dist/features/categories/components/CategoryProductsListing.js +22 -11
  67. package/dist/features/categories/components/index.d.ts +2 -0
  68. package/dist/features/categories/components/index.js +1 -0
  69. package/dist/features/categories/hooks/useCategories.d.ts +20 -0
  70. package/dist/features/categories/hooks/useCategories.js +50 -1
  71. package/dist/features/categories/hooks/useCategoryTree.d.ts +17 -0
  72. package/dist/features/categories/hooks/useCategoryTree.js +65 -0
  73. package/dist/features/events/components/AdminEventEditorView.d.ts +6 -0
  74. package/dist/features/events/components/AdminEventEditorView.js +203 -0
  75. package/dist/features/events/components/AdminEventsView.js +28 -2
  76. package/dist/features/events/components/EventCard.js +4 -2
  77. package/dist/features/events/components/EventFilters.js +2 -1
  78. package/dist/features/events/components/EventsIndexListing.js +40 -10
  79. package/dist/features/events/components/EventsListPageView.d.ts +6 -1
  80. package/dist/features/events/components/EventsListPageView.js +40 -7
  81. package/dist/features/events/components/index.d.ts +2 -0
  82. package/dist/features/events/components/index.js +1 -0
  83. package/dist/features/events/hooks/useEvents.js +2 -0
  84. package/dist/features/events/types/index.d.ts +1 -0
  85. package/dist/features/homepage/components/BlogArticlesSection.js +1 -1
  86. package/dist/features/homepage/components/EventsSection.js +1 -1
  87. package/dist/features/homepage/components/FeaturedAuctionsSection.js +1 -1
  88. package/dist/features/homepage/components/FeaturedPreOrdersSection.js +1 -1
  89. package/dist/features/homepage/components/FeaturedProductsSection.js +1 -1
  90. package/dist/features/homepage/components/FeaturedStoresSection.js +1 -1
  91. package/dist/features/homepage/components/HeroCarousel.js +1 -1
  92. package/dist/features/homepage/components/MarketplaceHomepageView.js +27 -17
  93. package/dist/features/homepage/components/SectionCarousel.js +4 -4
  94. package/dist/features/homepage/components/ShopByCategorySection.js +2 -2
  95. package/dist/features/homepage/schemas/firestore.d.ts +1 -1
  96. package/dist/features/homepage/schemas/firestore.js +2 -1
  97. package/dist/features/layout/AppLayoutShell.d.ts +6 -2
  98. package/dist/features/layout/AppLayoutShell.js +7 -3
  99. package/dist/features/pre-orders/components/MarketplacePreorderCard.d.ts +3 -1
  100. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +6 -2
  101. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +80 -12
  102. package/dist/features/pre-orders/components/PreOrderFilters.d.ts +8 -0
  103. package/dist/features/pre-orders/components/PreOrderFilters.js +21 -0
  104. package/dist/features/pre-orders/components/PreOrdersIndexListing.d.ts +2 -1
  105. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +69 -10
  106. package/dist/features/pre-orders/components/PreOrdersListView.d.ts +6 -1
  107. package/dist/features/pre-orders/components/PreOrdersListView.js +26 -7
  108. package/dist/features/pre-orders/components/index.d.ts +2 -0
  109. package/dist/features/pre-orders/components/index.js +1 -0
  110. package/dist/features/products/components/AuctionsIndexListing.d.ts +2 -1
  111. package/dist/features/products/components/AuctionsIndexListing.js +61 -9
  112. package/dist/features/products/components/InteractiveProductCard.d.ts +2 -4
  113. package/dist/features/products/components/InteractiveProductCard.js +2 -2
  114. package/dist/features/products/components/ProductDetailPageView.d.ts +1 -1
  115. package/dist/features/products/components/ProductDetailPageView.js +116 -25
  116. package/dist/features/products/components/ProductFilters.d.ts +6 -11
  117. package/dist/features/products/components/ProductFilters.js +5 -3
  118. package/dist/features/products/components/ProductGrid.d.ts +8 -2
  119. package/dist/features/products/components/ProductGrid.js +20 -5
  120. package/dist/features/products/components/ProductTabsShell.d.ts +3 -11
  121. package/dist/features/products/components/ProductTabsShell.js +14 -14
  122. package/dist/features/products/components/ProductsIndexListing.js +73 -9
  123. package/dist/features/products/components/ProductsIndexPageView.d.ts +6 -1
  124. package/dist/features/products/components/ProductsIndexPageView.js +39 -6
  125. package/dist/features/products/components/RelatedProductsCarousel.d.ts +7 -0
  126. package/dist/features/products/components/RelatedProductsCarousel.js +11 -0
  127. package/dist/features/products/components/index.d.ts +1 -0
  128. package/dist/features/products/components/index.js +1 -0
  129. package/dist/features/products/hooks/useProducts.js +16 -0
  130. package/dist/features/products/repository/products.repository.d.ts +8 -0
  131. package/dist/features/products/repository/products.repository.js +2 -0
  132. package/dist/features/products/schemas/index.d.ts +8 -8
  133. package/dist/features/products/types/index.d.ts +12 -0
  134. package/dist/features/promotions/components/CouponsIndexListing.d.ts +9 -0
  135. package/dist/features/promotions/components/CouponsIndexListing.js +86 -0
  136. package/dist/features/promotions/components/PromotionsView.d.ts +11 -5
  137. package/dist/features/promotions/components/PromotionsView.js +6 -1
  138. package/dist/features/promotions/components/index.d.ts +4 -2
  139. package/dist/features/promotions/components/index.js +2 -1
  140. package/dist/features/reviews/components/ReviewDetailPageView.d.ts +4 -0
  141. package/dist/features/reviews/components/ReviewDetailPageView.js +20 -0
  142. package/dist/features/reviews/components/ReviewDetailShell.d.ts +7 -0
  143. package/dist/features/reviews/components/ReviewDetailShell.js +80 -0
  144. package/dist/features/reviews/components/ReviewFilters.d.ts +3 -3
  145. package/dist/features/reviews/components/ReviewFilters.js +5 -4
  146. package/dist/features/reviews/components/ReviewsIndexListing.d.ts +4 -3
  147. package/dist/features/reviews/components/ReviewsIndexListing.js +35 -51
  148. package/dist/features/reviews/components/ReviewsIndexPageView.d.ts +6 -1
  149. package/dist/features/reviews/components/ReviewsIndexPageView.js +49 -3
  150. package/dist/features/reviews/components/ReviewsList.js +9 -1
  151. package/dist/features/reviews/components/index.d.ts +1 -0
  152. package/dist/features/reviews/hooks/useReviews.js +15 -1
  153. package/dist/features/reviews/types/index.d.ts +6 -1
  154. package/dist/features/seller/components/SellerSidebar.d.ts +8 -4
  155. package/dist/features/seller/components/SellerSidebar.js +6 -4
  156. package/dist/features/seller/components/index.d.ts +30 -0
  157. package/dist/features/seller/components/index.js +17 -0
  158. package/dist/features/seller/hooks/useSellerStore.d.ts +2 -0
  159. package/dist/features/seller/hooks/useSellerStore.js +2 -0
  160. package/dist/features/seller/permission-map.d.ts +4 -2
  161. package/dist/features/seller/permission-map.js +16 -14
  162. package/dist/features/seller/schemas/index.d.ts +2 -2
  163. package/dist/features/stores/api/[storeSlug]/reviews/route.d.ts +1 -1
  164. package/dist/features/stores/api/[storeSlug]/reviews/route.js +24 -19
  165. package/dist/features/stores/components/InteractiveStoreCard.d.ts +0 -5
  166. package/dist/features/stores/components/InteractiveStoreCard.js +9 -9
  167. package/dist/features/stores/components/StoreAuctionsListing.js +27 -9
  168. package/dist/features/stores/components/StoreDetailLayoutView.js +2 -0
  169. package/dist/features/stores/components/StoreFilters.d.ts +5 -0
  170. package/dist/features/stores/components/StoreFilters.js +20 -0
  171. package/dist/features/stores/components/StoreHeader.js +2 -2
  172. package/dist/features/stores/components/StorePreOrdersListing.d.ts +5 -0
  173. package/dist/features/stores/components/StorePreOrdersListing.js +40 -0
  174. package/dist/features/stores/components/StorePreOrdersPageView.d.ts +4 -0
  175. package/dist/features/stores/components/StorePreOrdersPageView.js +21 -0
  176. package/dist/features/stores/components/StoreProductsListing.js +21 -11
  177. package/dist/features/stores/components/StoreReviewsListing.js +2 -7
  178. package/dist/features/stores/components/StoresIndexListing.js +42 -8
  179. package/dist/features/stores/components/StoresIndexPageView.d.ts +6 -1
  180. package/dist/features/stores/components/StoresIndexPageView.js +9 -2
  181. package/dist/features/stores/components/index.d.ts +3 -0
  182. package/dist/features/stores/components/index.js +1 -0
  183. package/dist/features/stores/hooks/useStores.d.ts +7 -1
  184. package/dist/features/stores/hooks/useStores.js +16 -3
  185. package/dist/features/stores/schemas/index.d.ts +2 -2
  186. package/dist/features/stores/types/index.d.ts +3 -0
  187. package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +20 -0
  188. package/dist/features/wishlist/hooks/useGuestWishlist.js +49 -0
  189. package/dist/features/wishlist/hooks/useWishlistCount.d.ts +7 -0
  190. package/dist/features/wishlist/hooks/useWishlistCount.js +31 -0
  191. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +56 -0
  192. package/dist/features/wishlist/hooks/useWishlistWithGuest.js +57 -0
  193. package/dist/features/wishlist/index.d.ts +3 -0
  194. package/dist/features/wishlist/index.js +3 -0
  195. package/dist/features/wishlist/utils/guest-wishlist.d.ts +22 -0
  196. package/dist/features/wishlist/utils/guest-wishlist.js +70 -0
  197. package/dist/index.d.ts +50 -1
  198. package/dist/index.js +63 -1
  199. package/dist/next/routing/route-map.d.ts +70 -36
  200. package/dist/next/routing/route-map.js +30 -22
  201. package/dist/seed/addresses-seed-data.js +62 -261
  202. package/dist/seed/beyblade-seed-data.d.ts +7 -0
  203. package/dist/seed/beyblade-seed-data.js +947 -0
  204. package/dist/seed/bids-seed-data.d.ts +10 -2
  205. package/dist/seed/bids-seed-data.js +220 -1071
  206. package/dist/seed/blog-posts-seed-data.d.ts +2 -2
  207. package/dist/seed/blog-posts-seed-data.js +455 -117
  208. package/dist/seed/cart-seed-data.d.ts +9 -9
  209. package/dist/seed/cart-seed-data.js +73 -74
  210. package/dist/seed/coupons-seed-data.d.ts +3 -4
  211. package/dist/seed/coupons-seed-data.js +3 -509
  212. package/dist/seed/events-seed-data.d.ts +2 -2
  213. package/dist/seed/events-seed-data.js +315 -476
  214. package/dist/seed/faq-seed-data.d.ts +18 -41
  215. package/dist/seed/faq-seed-data.js +1059 -1172
  216. package/dist/seed/hot-wheels-seed-data.d.ts +7 -0
  217. package/dist/seed/hot-wheels-seed-data.js +1365 -0
  218. package/dist/seed/index.d.ts +6 -1
  219. package/dist/seed/index.js +6 -1
  220. package/dist/seed/pokemon-carousel-slides-seed-data.d.ts +4 -2
  221. package/dist/seed/pokemon-carousel-slides-seed-data.js +152 -268
  222. package/dist/seed/pokemon-categories-seed-data.d.ts +18 -21
  223. package/dist/seed/pokemon-categories-seed-data.js +424 -1004
  224. package/dist/seed/pokemon-coupons-seed-data.d.ts +6 -0
  225. package/dist/seed/pokemon-coupons-seed-data.js +465 -0
  226. package/dist/seed/pokemon-homepage-sections-seed-data.d.ts +3 -2
  227. package/dist/seed/pokemon-homepage-sections-seed-data.js +67 -289
  228. package/dist/seed/pokemon-products-seed-data.js +662 -0
  229. package/dist/seed/pokemon-seed-bundle.d.ts +32 -11
  230. package/dist/seed/pokemon-seed-bundle.js +41 -11
  231. package/dist/seed/pokemon-stores-seed-data.d.ts +2 -3
  232. package/dist/seed/pokemon-stores-seed-data.js +56 -31
  233. package/dist/seed/pokemon-users-seed-data.d.ts +2 -2
  234. package/dist/seed/pokemon-users-seed-data.js +245 -261
  235. package/dist/seed/reviews-seed-data.d.ts +17 -2
  236. package/dist/seed/reviews-seed-data.js +519 -483
  237. package/dist/seed/site-settings-seed-data.js +14 -14
  238. package/dist/seed/store-addresses-seed-data.js +68 -50
  239. package/dist/seed/transformers-seed-data.d.ts +7 -0
  240. package/dist/seed/transformers-seed-data.js +510 -0
  241. package/dist/seed/wishlists-seed-data.d.ts +5 -1
  242. package/dist/seed/wishlists-seed-data.js +82 -4
  243. package/dist/server.d.ts +1 -0
  244. package/dist/server.js +2 -0
  245. package/dist/tokens/index.d.ts +6 -0
  246. package/dist/tokens/index.js +2 -0
  247. package/dist/ui/components/BaseListingCard.js +24 -26
  248. package/dist/ui/components/BaseListingCard.style.css +5 -5
  249. package/dist/ui/components/HorizontalScroller.d.ts +1 -1
  250. package/dist/ui/components/HorizontalScroller.js +19 -5
  251. package/dist/ui/components/SideDrawer.style.css +3 -11
  252. package/dist/ui/rich-text/RichText.js +19 -1
  253. package/package.json +1 -1
@@ -9,12 +9,16 @@ interface ProductCardProps<T extends ProductItem = ProductItem> {
9
9
  onClick?: (product: T) => void;
10
10
  onAddToWishlist?: (productId: string) => void;
11
11
  isWishlisted?: boolean;
12
+ onAddToCart?: (product: T) => void;
13
+ onBuyNow?: (product: T) => void;
12
14
  className?: string;
13
15
  }
14
- export declare function ProductCard<T extends ProductItem = ProductItem>({ product, href, onClick, onAddToWishlist, isWishlisted, className, }: ProductCardProps<T>): import("react/jsx-runtime").JSX.Element;
16
+ export declare function ProductCard<T extends ProductItem = ProductItem>({ product, href, onClick, onAddToWishlist, isWishlisted, onAddToCart, onBuyNow, className, }: ProductCardProps<T>): import("react/jsx-runtime").JSX.Element;
15
17
  export interface ProductCardContext<T extends ProductItem = ProductItem> {
16
18
  onClick?: (product: T) => void;
17
19
  onWishlistToggle?: (productId: string) => void;
20
+ onAddToCart?: (product: T) => void;
21
+ onBuyNow?: (product: T) => void;
18
22
  isWishlisted: boolean;
19
23
  }
20
24
  interface ProductGridProps<T extends ProductItem = ProductItem> {
@@ -36,6 +40,8 @@ interface ProductGridProps<T extends ProductItem = ProductItem> {
36
40
  /** When provided, each card is wrapped in a Link using this href generator. */
37
41
  getProductHref?: (product: T) => string;
38
42
  onWishlistToggle?: (productId: string) => void;
43
+ onAddToCart?: (product: T) => void;
44
+ onBuyNow?: (product: T) => void;
39
45
  wishlistedIds?: Set<string>;
40
46
  /** Text shown when the list is empty and no `emptySlot` is provided. */
41
47
  emptyLabel?: string;
@@ -69,5 +75,5 @@ interface ProductGridProps<T extends ProductItem = ProductItem> {
69
75
  */
70
76
  view?: ViewMode;
71
77
  }
72
- export declare function ProductGrid<T extends ProductItem = ProductItem>({ products, renderCard, onProductClick, getProductHref, onWishlistToggle, wishlistedIds, emptyLabel, emptySlot, headerSlot, footerSlot, className, slots, total, currentPage, totalPages, view, }: ProductGridProps<T>): import("react/jsx-runtime").JSX.Element;
78
+ export declare function ProductGrid<T extends ProductItem = ProductItem>({ products, renderCard, onProductClick, getProductHref, onWishlistToggle, onAddToCart, onBuyNow, wishlistedIds, emptyLabel, emptySlot, headerSlot, footerSlot, className, slots, total, currentPage, totalPages, view, }: ProductGridProps<T>): import("react/jsx-runtime").JSX.Element;
73
79
  export {};
@@ -7,7 +7,7 @@ import { formatCurrency } from "../../../utils/number.formatter";
7
7
  import { getDefaultCurrency, getDefaultCurrencySymbol } from "../../../core/baseline-resolver";
8
8
  import { normalizeRichTextHtml } from "../../../utils/string.formatter";
9
9
  import { safeDisplayName } from "../../../security";
10
- export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlisted, className = "", }) {
10
+ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlisted, onAddToCart, onBuyNow, className = "", }) {
11
11
  const discount = product.originalPrice && product.originalPrice > product.price
12
12
  ? Math.round(((product.originalPrice - product.price) / product.originalPrice) *
13
13
  100)
@@ -17,7 +17,18 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
17
17
  : undefined, onClick: onClick && !href ? () => onClick(product) : undefined, className: `group relative flex h-full flex-col overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-sm transition hover:shadow-md dark:border-slate-700 dark:bg-slate-900 ${onClick || href ? "cursor-pointer" : ""} ${className}`, children: [_jsxs(Div, { className: "relative aspect-square overflow-hidden bg-neutral-100 dark:bg-slate-800", children: [product.mainImage ? (_jsx(Div, { role: "img", "aria-label": product.title, className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${product.mainImage})` } })) : (_jsx(Div, { className: "h-full w-full bg-neutral-200 dark:bg-slate-700" })), discount && (_jsxs(Span, { className: "absolute left-2 top-2 rounded-full bg-red-500 px-2 py-0.5 text-xs font-bold text-white", children: ["-", discount, "%"] })), product.isAuction && (_jsx(Span, { className: "absolute right-2 top-2 rounded-full bg-amber-500 px-2 py-0.5 text-xs font-bold text-white", children: "Auction" })), onAddToWishlist && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => {
18
18
  e.stopPropagation();
19
19
  onAddToWishlist(product.id);
20
- }, "aria-label": isWishlisted ? "Remove from wishlist" : "Add to wishlist", className: "absolute bottom-2 right-2 flex h-8 w-8 items-center justify-center rounded-full bg-white/90 dark:bg-slate-800/90 text-neutral-600 dark:text-zinc-300 shadow transition hover:bg-white dark:hover:bg-slate-700 hover:text-red-500", children: isWishlisted ? "♥" : "♡" }))] }), _jsxs(Div, { className: "flex flex-1 flex-col border-t border-zinc-200/80 bg-zinc-50 p-3 dark:border-slate-700/80 dark:bg-slate-900", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-medium text-zinc-950 dark:text-white`, children: product.title }), product.description && (_jsx(RichText, { html: normalizeRichTextHtml(product.description), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `mt-1 ${THEME_CONSTANTS.utilities.textClamp2} text-xs text-zinc-600 dark:text-zinc-400` })), product.sellerName && (_jsx(Text, { className: "mt-0.5 text-xs text-zinc-600 dark:text-zinc-400", children: safeDisplayName(product.sellerName, "Seller") })), _jsxs(Div, { className: "mt-2 flex items-baseline gap-2", children: [_jsx(Span, { className: "font-semibold text-zinc-950 dark:text-white", children: formatCurrency(product.price, getDefaultCurrency()) }), product.originalPrice && (_jsx(Span, { className: "text-xs text-zinc-500 line-through dark:text-zinc-400", children: formatCurrency(product.originalPrice, getDefaultCurrency()) }))] }), product.rating !== undefined && (_jsxs(Row, { className: "mt-1 gap-1", children: [_jsx(Span, { className: "text-xs text-yellow-500", children: "\u2605" }), _jsxs(Span, { className: "text-xs text-neutral-500 dark:text-zinc-400", children: [product.rating.toFixed(1), product.reviewCount ? ` (${product.reviewCount})` : ""] })] }))] })] }));
20
+ }, "aria-label": isWishlisted ? "Remove from wishlist" : "Add to wishlist", className: "absolute bottom-2 right-2 flex h-8 w-8 items-center justify-center rounded-full bg-white/90 dark:bg-slate-800/90 text-neutral-600 dark:text-zinc-300 shadow transition hover:bg-white dark:hover:bg-slate-700 hover:text-red-500", children: isWishlisted ? "♥" : "♡" }))] }), _jsxs(Div, { className: "flex flex-1 flex-col border-t border-zinc-200/80 bg-zinc-50 p-3 dark:border-slate-700/80 dark:bg-slate-900", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-medium text-zinc-950 dark:text-white`, children: product.title }), product.description && (_jsx(RichText, { html: normalizeRichTextHtml(product.description), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `mt-1 ${THEME_CONSTANTS.utilities.textClamp2} text-xs text-zinc-600 dark:text-zinc-400` })), (() => {
21
+ const seller = safeDisplayName(product.sellerName, "");
22
+ return seller ? (_jsxs(Text, { className: "mt-0.5 text-xs text-zinc-500 dark:text-zinc-400", children: ["by ", seller] })) : null;
23
+ })(), _jsxs(Div, { className: "mt-2 flex items-baseline gap-2", children: [_jsx(Span, { className: "font-semibold text-zinc-950 dark:text-white", children: formatCurrency(product.price, getDefaultCurrency()) }), product.originalPrice && (_jsx(Span, { className: "text-xs text-zinc-500 line-through dark:text-zinc-400", children: formatCurrency(product.originalPrice, getDefaultCurrency()) }))] }), product.rating !== undefined && (_jsxs(Row, { className: "mt-1 gap-1", children: [_jsx(Span, { className: "text-xs text-yellow-500", children: "\u2605" }), _jsxs(Span, { className: "text-xs text-neutral-500 dark:text-zinc-400", children: [product.rating.toFixed(1), product.reviewCount ? ` (${product.reviewCount})` : ""] })] })), (onAddToCart || onBuyNow) && (_jsxs(Row, { className: "mt-3 gap-1.5", children: [onBuyNow && (_jsx(Button, { type: "button", onClick: (e) => {
24
+ e.stopPropagation();
25
+ e.preventDefault();
26
+ onBuyNow(product);
27
+ }, className: "flex-1 rounded-lg bg-primary py-1.5 text-xs font-semibold text-white hover:bg-primary-600 transition-colors", children: "\u26A1 Buy Now" })), onAddToCart && (_jsx(Button, { type: "button", onClick: (e) => {
28
+ e.stopPropagation();
29
+ e.preventDefault();
30
+ onAddToCart(product);
31
+ }, className: "flex-1 rounded-lg border border-primary py-1.5 text-xs font-semibold text-primary hover:bg-primary/5 transition-colors", children: "\uD83D\uDED2 Add to Cart" }))] }))] })] }));
21
32
  if (href) {
22
33
  return (_jsx(Link, { href: href, className: "block h-full", children: cardBody }));
23
34
  }
@@ -46,7 +57,7 @@ function ProductListRow({ product, onClick, onAddToWishlist, isWishlisted, }) {
46
57
  onAddToWishlist(product.id);
47
58
  }, "aria-label": isWishlisted ? "Remove from wishlist" : "Add to wishlist", className: "flex-shrink-0 w-[36px] h-[36px] flex items-center justify-center rounded-lg text-neutral-400 hover:text-red-500 transition-colors", children: isWishlisted ? "♥" : "♡" }))] }));
48
59
  }
49
- export function ProductGrid({ products, renderCard, onProductClick, getProductHref, onWishlistToggle, wishlistedIds, emptyLabel = "No products found", emptySlot, headerSlot, footerSlot, className = "", slots, total = 0, currentPage = 1, totalPages = 1, view = "card", }) {
60
+ export function ProductGrid({ products, renderCard, onProductClick, getProductHref, onWishlistToggle, onAddToCart, onBuyNow, wishlistedIds, emptyLabel = "No products found", emptySlot, headerSlot, footerSlot, className = "", slots, total = 0, currentPage = 1, totalPages = 1, view = "card", }) {
50
61
  const isEmpty = products.length === 0;
51
62
  // Slot resolution: explicit props win over `slots` object
52
63
  const resolvedHeader = headerSlot ??
@@ -73,12 +84,14 @@ export function ProductGrid({ products, renderCard, onProductClick, getProductHr
73
84
  const ctx = {
74
85
  onClick: onProductClick,
75
86
  onWishlistToggle,
87
+ onAddToCart,
88
+ onBuyNow,
76
89
  isWishlisted: wishlistedIds?.has(p.id) ?? false,
77
90
  };
78
91
  const cardRenderer = renderCard ?? slots?.renderCard;
79
92
  return cardRenderer ? (_jsx(React.Fragment, { children: cardRenderer === renderCard
80
93
  ? renderCard(p, ctx)
81
- : slots.renderCard(p, i) }, p.id)) : (_jsx(ProductCard, { product: p, href: getProductHref ? getProductHref(p) : undefined, onClick: onProductClick, onAddToWishlist: onWishlistToggle, isWishlisted: ctx.isWishlisted }, p.id));
94
+ : slots.renderCard(p, i) }, p.id)) : (_jsx(ProductCard, { product: p, href: getProductHref ? getProductHref(p) : undefined, onClick: onProductClick, onAddToWishlist: onWishlistToggle, onAddToCart: onAddToCart, onBuyNow: onBuyNow, isWishlisted: ctx.isWishlisted }, p.id));
82
95
  }) }));
83
96
  }
84
97
  const gridClass = GRID_CLASSES.card;
@@ -86,12 +99,14 @@ export function ProductGrid({ products, renderCard, onProductClick, getProductHr
86
99
  const ctx = {
87
100
  onClick: onProductClick,
88
101
  onWishlistToggle,
102
+ onAddToCart,
103
+ onBuyNow,
89
104
  isWishlisted: wishlistedIds?.has(p.id) ?? false,
90
105
  };
91
106
  const cardRenderer = renderCard ?? slots?.renderCard;
92
107
  return cardRenderer ? (_jsx(React.Fragment, { children: cardRenderer === renderCard
93
108
  ? renderCard(p, ctx)
94
- : slots.renderCard(p, i) }, p.id)) : (_jsx(ProductCard, { product: p, href: getProductHref ? getProductHref(p) : undefined, onClick: onProductClick, onAddToWishlist: onWishlistToggle, isWishlisted: ctx.isWishlisted }, p.id));
109
+ : slots.renderCard(p, i) }, p.id)) : (_jsx(ProductCard, { product: p, href: getProductHref ? getProductHref(p) : undefined, onClick: onProductClick, onAddToWishlist: onWishlistToggle, onAddToCart: onAddToCart, onBuyNow: onBuyNow, isWishlisted: ctx.isWishlisted }, p.id));
95
110
  }) }));
96
111
  };
97
112
  return (_jsxs(Div, { children: [resolvedHeader, isEmpty
@@ -1,18 +1,10 @@
1
1
  import React from "react";
2
2
  export interface ProductTabsShellProps {
3
- /** Pre-rendered description content (server RSC node) */
4
3
  descriptionContent?: React.ReactNode;
5
- /** Pre-rendered specifications content (server RSC node) */
6
4
  specsContent?: React.ReactNode;
7
- /** Pre-rendered reviews content (server RSC node) */
5
+ ingredientsContent?: React.ReactNode;
6
+ howToUseContent?: React.ReactNode;
8
7
  reviewsContent?: React.ReactNode;
9
8
  className?: string;
10
9
  }
11
- /**
12
- * RSC-safe tab shell for product detail pages.
13
- *
14
- * Accepts pre-rendered ReactNode children instead of render functions so the
15
- * component can be used inside a server component tree. (Functions cannot cross
16
- * the RSC serialisation boundary to a "use client" component.)
17
- */
18
- export declare function ProductTabsShell({ descriptionContent, specsContent, reviewsContent, className, }: ProductTabsShellProps): import("react/jsx-runtime").JSX.Element | null;
10
+ export declare function ProductTabsShell({ descriptionContent, specsContent, ingredientsContent, howToUseContent, reviewsContent, className, }: ProductTabsShellProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,26 +1,26 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- const TABS = [
4
+ const ALL_TABS = [
5
5
  { id: "description", label: "Description" },
6
6
  { id: "specs", label: "Specifications" },
7
+ { id: "ingredients", label: "Ingredients" },
8
+ { id: "howToUse", label: "How to Use" },
7
9
  { id: "reviews", label: "Reviews" },
8
10
  ];
9
- /**
10
- * RSC-safe tab shell for product detail pages.
11
- *
12
- * Accepts pre-rendered ReactNode children instead of render functions so the
13
- * component can be used inside a server component tree. (Functions cannot cross
14
- * the RSC serialisation boundary to a "use client" component.)
15
- */
16
- export function ProductTabsShell({ descriptionContent, specsContent, reviewsContent, className = "", }) {
17
- const visibleTabs = TABS.filter((t) => (t.id === "description" && descriptionContent != null) ||
18
- (t.id === "specs" && specsContent != null) ||
19
- (t.id === "reviews" && reviewsContent != null));
11
+ export function ProductTabsShell({ descriptionContent, specsContent, ingredientsContent, howToUseContent, reviewsContent, className = "", }) {
12
+ const contentMap = {
13
+ description: descriptionContent,
14
+ specs: specsContent,
15
+ ingredients: ingredientsContent,
16
+ howToUse: howToUseContent,
17
+ reviews: reviewsContent,
18
+ };
19
+ const visibleTabs = ALL_TABS.filter((t) => contentMap[t.id] != null);
20
20
  const [activeTab, setActiveTab] = useState(visibleTabs[0]?.id ?? "description");
21
21
  if (visibleTabs.length === 0)
22
22
  return null;
23
- return (_jsxs("div", { className: className, children: [_jsx("div", { className: "mb-6 flex gap-4 border-b border-zinc-200 dark:border-zinc-700", children: visibleTabs.map((t) => (_jsx("button", { type: "button", onClick: () => setActiveTab(t.id), className: `-mb-px pb-2.5 text-sm font-medium border-b-2 transition-colors ${activeTab === t.id
23
+ return (_jsxs("div", { className: `mt-8 ${className}`, children: [_jsx("div", { className: "mb-6 flex gap-1 overflow-x-auto border-b border-zinc-200 dark:border-zinc-700 pb-px", children: visibleTabs.map((t) => (_jsx("button", { type: "button", onClick: () => setActiveTab(t.id), className: `flex-shrink-0 -mb-px pb-3 px-4 text-sm font-medium border-b-2 transition-colors ${activeTab === t.id
24
24
  ? "border-primary-500 text-primary-600 dark:text-primary-400"
25
- : "border-transparent text-neutral-500 hover:text-neutral-700 dark:hover:text-zinc-300"}`, children: t.label }, t.id))) }), _jsxs("div", { children: [activeTab === "description" && descriptionContent, activeTab === "specs" && specsContent, activeTab === "reviews" && reviewsContent] })] }));
25
+ : "border-transparent text-zinc-500 hover:text-zinc-700 dark:hover:text-zinc-300"}`, children: t.label }, t.id))) }), _jsx("div", { children: visibleTabs.map((t) => (_jsx("div", { hidden: activeTab !== t.id, children: contentMap[t.id] }, t.id))) })] }));
26
26
  }
@@ -1,27 +1,91 @@
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";
5
+ import { useRouter } from "next/navigation";
3
6
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
4
7
  import { useProducts } from "../hooks/useProducts";
5
- import { Div, Input, Pagination, SortDropdown, SlottedListingView, } from "../../../ui";
8
+ import { Pagination, SortDropdown } from "../../../ui";
6
9
  import { ROUTES } from "../../../next";
7
- import { ProductGrid, ProductFilters } from ".";
10
+ import { ProductGrid, ProductFilters, PRODUCT_PUBLIC_SORT_OPTIONS } from ".";
11
+ import { useSession } from "../../../react/contexts/SessionContext";
12
+ import { useWishlistWithGuest } from "../../wishlist/hooks/useWishlistWithGuest";
13
+ import { apiClient } from "../../../http";
14
+ import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
8
15
  export function ProductsIndexListing({ initialData }) {
16
+ const router = useRouter();
9
17
  const table = useUrlTable({ defaults: { pageSize: "24", sort: "-createdAt" } });
18
+ const [searchInput, setSearchInput] = useState(table.get("q") || "");
19
+ const [filterOpen, setFilterOpen] = useState(false);
20
+ const [view, setView] = useState(table.get("view") || "card");
21
+ const { user } = useSession();
22
+ const wl = useWishlistWithGuest(user?.uid ?? null);
23
+ const { categories } = useCategoryTree();
24
+ const categoryOptions = categoriesToFacetOptions(categories);
10
25
  const params = {
11
26
  q: table.get("q") || undefined,
12
27
  category: table.get("category") || undefined,
13
28
  minPrice: table.get("minPrice") ? Number(table.get("minPrice")) : undefined,
14
29
  maxPrice: table.get("maxPrice") ? Number(table.get("maxPrice")) : undefined,
15
30
  condition: table.get("condition") || undefined,
16
- sort: table.get("sort") || undefined,
31
+ brand: table.get("brand") || undefined,
32
+ storeId: table.get("storeId") || undefined,
33
+ freeShipping: table.get("freeShipping") === "true" ? true : undefined,
34
+ sort: table.get("sort") || "-createdAt",
17
35
  page: table.getNumber("page", 1),
18
36
  perPage: table.getNumber("pageSize", 24),
19
37
  isAuction: false,
20
38
  };
21
39
  const { products, total, totalPages, page, isLoading } = useProducts(params, { initialData });
22
- return (_jsx(Div, { className: "min-h-screen", children: _jsx(SlottedListingView, { portal: "public", manageSearch: true, manageSort: true, inlineToolbar: true, renderSearch: (search, setSearch) => (_jsx(Input, { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Search products...", className: "max-w-sm" })), renderSort: (value, onChange) => (_jsx(SortDropdown, { value: value, onChange: onChange, options: [
23
- { value: "-createdAt", label: "Newest First" },
24
- { value: "-price", label: "Price: High to Low" },
25
- { value: "price", label: "Price: Low to High" },
26
- ] })), renderFilters: () => _jsx(ProductFilters, { table: table }), renderTable: () => (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: table.get("view") || "card" })), renderPagination: () => (_jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) })), total: total, isLoading: isLoading }) }));
40
+ const commitSearch = useCallback(() => {
41
+ table.set("q", searchInput.trim());
42
+ table.setPage(1);
43
+ }, [searchInput, table]);
44
+ const handleSearchKeyDown = (e) => {
45
+ if (e.key === "Enter")
46
+ commitSearch();
47
+ };
48
+ const handleViewToggle = (next) => {
49
+ setView(next);
50
+ table.set("view", next);
51
+ };
52
+ const closeFilters = () => setFilterOpen(false);
53
+ const handleWishlistToggle = useCallback(async (productId) => {
54
+ const isWishlisted = wl.wishlistedIds?.has(productId) ?? false;
55
+ if (wl.isGuest) {
56
+ const guestWl = wl.guestWishlist;
57
+ if (isWishlisted)
58
+ guestWl?.remove(productId, "product");
59
+ else
60
+ guestWl?.add(productId, "product");
61
+ }
62
+ else {
63
+ if (isWishlisted)
64
+ await apiClient.delete(`/api/user/wishlist/${productId}`);
65
+ else
66
+ await apiClient.post("/api/user/wishlist", { productId });
67
+ }
68
+ }, [wl]);
69
+ const handleAddToCart = useCallback(async (product) => {
70
+ try {
71
+ await apiClient.post("/api/cart", { productId: product.id, quantity: 1 });
72
+ }
73
+ catch {
74
+ // silently ignore cart errors on listing page
75
+ }
76
+ }, []);
77
+ const handleBuyNow = useCallback(async (product) => {
78
+ try {
79
+ await apiClient.post("/api/cart", { productId: product.id, quantity: 1 });
80
+ }
81
+ catch {
82
+ // silently ignore cart errors; still navigate
83
+ }
84
+ router.push("/cart");
85
+ }, [router]);
86
+ 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 products...", 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: PRODUCT_PUBLIC_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("card"), "aria-label": "Grid view", className: `p-2 transition-colors ${view === "card"
87
+ ? "bg-primary text-white"
88
+ : "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"
89
+ ? "bg-primary text-white"
90
+ : "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: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4", 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" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: view, onWishlistToggle: handleWishlistToggle, wishlistedIds: wl.wishlistedIds, onAddToCart: handleAddToCart, onBuyNow: handleBuyNow })), 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(ProductFilters, { table: table, currencyPrefix: "\u20B9", categoryOptions: categoryOptions }) }), _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" }) })] })] }))] }));
27
91
  }
@@ -1 +1,6 @@
1
- export declare function ProductsIndexPageView(): Promise<import("react/jsx-runtime").JSX.Element>;
1
+ type SearchParams = Record<string, string | string[]>;
2
+ export interface ProductsIndexPageViewProps {
3
+ searchParams?: SearchParams;
4
+ }
5
+ export declare function ProductsIndexPageView({ searchParams }: ProductsIndexPageViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
6
+ export {};
@@ -1,17 +1,50 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { productRepository } from "../../../repositories";
3
+ import { ROUTES } from "../../../constants";
3
4
  import { Container, Heading, Main, Section } from "../../../ui";
4
5
  import { AdSlot } from "../../homepage/components/AdSlot";
5
6
  import { ProductsIndexListing } from "./ProductsIndexListing";
6
- export async function ProductsIndexPageView() {
7
+ function sp(params, key) {
8
+ const v = params[key];
9
+ return Array.isArray(v) ? v[0] ?? "" : v ?? "";
10
+ }
11
+ function buildProductFilters(params) {
12
+ const parts = ["status==published", "isAuction==false"];
13
+ const condition = sp(params, "condition");
14
+ if (condition) {
15
+ const values = condition.split("|").filter(Boolean);
16
+ if (values.length === 1)
17
+ parts.push(`condition==${values[0]}`);
18
+ else if (values.length > 1)
19
+ parts.push(`condition==${values.join("|")}`);
20
+ }
21
+ const minPrice = sp(params, "minPrice");
22
+ const maxPrice = sp(params, "maxPrice");
23
+ if (minPrice)
24
+ parts.push(`price>=${minPrice}`);
25
+ if (maxPrice)
26
+ parts.push(`price<=${maxPrice}`);
27
+ const sellerId = sp(params, "seller");
28
+ if (sellerId)
29
+ parts.push(`sellerId==${sellerId}`);
30
+ const freeShipping = sp(params, "freeShipping");
31
+ if (freeShipping === "true")
32
+ parts.push("freeShipping==true");
33
+ return parts.join(",");
34
+ }
35
+ export async function ProductsIndexPageView({ searchParams = {} }) {
36
+ const sort = sp(searchParams, "sort") || "-createdAt";
37
+ const page = Number(sp(searchParams, "page")) || 1;
38
+ const pageSize = Number(sp(searchParams, "pageSize")) || 24;
39
+ const filters = buildProductFilters(searchParams);
7
40
  const result = await productRepository
8
41
  .list({
9
- filters: "status==published,isAuction==false",
10
- sorts: "-createdAt",
11
- page: 1,
12
- pageSize: 24,
42
+ filters,
43
+ sorts: sort,
44
+ page,
45
+ pageSize,
13
46
  })
14
47
  .catch(() => null);
15
48
  const products = result ?? null;
16
- 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: "Products" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(ProductsIndexListing, { initialData: products }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
49
+ return (_jsxs(Main, { children: [_jsx(Section, { className: "pt-8 pb-4 border-b border-zinc-100 dark:border-slate-800", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "text-3xl font-bold text-zinc-900 dark:text-zinc-100", children: "Products" }), _jsx("p", { className: "mt-1 text-sm text-zinc-500 dark:text-zinc-400", children: "Discover amazing products and deals" }), _jsx("div", { className: "mt-3", children: _jsx("a", { href: ROUTES.PUBLIC.AUCTIONS, className: "inline-flex items-center gap-1.5 rounded-full border border-primary/30 bg-primary/10 px-4 py-1.5 text-xs font-medium text-primary-700 dark:text-primary-400 hover:bg-primary/15 transition-colors", children: "\uD83C\uDFF7\uFE0F Looking for unique deals? Browse Auctions \u2192" }) })] }) }), _jsxs(Container, { size: "xl", className: "px-4", children: [_jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-4 mt-4" }), _jsx(ProductsIndexListing, { initialData: products }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] })] }));
17
50
  }
@@ -0,0 +1,7 @@
1
+ import type { ProductItem } from "../types";
2
+ interface RelatedProductsCarouselProps {
3
+ items: ProductItem[];
4
+ title?: string;
5
+ }
6
+ export declare function RelatedProductsCarousel({ items, title, }: RelatedProductsCarouselProps): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { ROUTES } from "../../../next";
4
+ import { THEME_CONSTANTS } from "../../../tokens";
5
+ import { SectionCarousel } from "../../homepage/components/SectionCarousel";
6
+ import { ProductCard } from "./ProductGrid";
7
+ export function RelatedProductsCarousel({ items, title = "You might also like", }) {
8
+ if (items.length === 0)
9
+ return null;
10
+ return (_jsx(SectionCarousel, { title: title, pillLabel: "Related Products", headingVariant: "editorial", items: items, isLoading: false, perView: THEME_CONSTANTS.carousel.perView.standard, keyExtractor: (item) => item.id, renderItem: (item) => (_jsx(ProductCard, { product: item, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(item.slug || item.id)) })) }));
11
+ }
@@ -36,3 +36,4 @@ export { ProductGalleryClient } from "./ProductGalleryClient";
36
36
  export type { ProductGalleryClientProps } from "./ProductGalleryClient";
37
37
  export { ProductTabsShell } from "./ProductTabsShell";
38
38
  export type { ProductTabsShellProps } from "./ProductTabsShell";
39
+ export { RelatedProductsCarousel } from "./RelatedProductsCarousel";
@@ -18,3 +18,4 @@ export { MakeOfferForm } from "./MakeOfferForm";
18
18
  export { RelatedProducts } from "./RelatedProducts";
19
19
  export { ProductGalleryClient } from "./ProductGalleryClient";
20
20
  export { ProductTabsShell } from "./ProductTabsShell";
21
+ export { RelatedProductsCarousel } from "./RelatedProductsCarousel";
@@ -21,8 +21,12 @@ export function useProducts(params = {}, opts) {
21
21
  sp.set("inStock", String(params.inStock));
22
22
  if (params.isAuction !== undefined)
23
23
  sp.set("isAuction", String(params.isAuction));
24
+ if (params.isPreOrder !== undefined)
25
+ sp.set("isPreOrder", String(params.isPreOrder));
24
26
  if (params.sellerId)
25
27
  sp.set("sellerId", params.sellerId);
28
+ if (params.storeId)
29
+ sp.set("storeId", params.storeId);
26
30
  if (params.sort)
27
31
  sp.set("sorts", params.sort);
28
32
  if (params.page)
@@ -31,6 +35,18 @@ export function useProducts(params = {}, opts) {
31
35
  sp.set("pageSize", String(params.perPage));
32
36
  if (params.featured !== undefined)
33
37
  sp.set("featured", String(params.featured));
38
+ if (params.minBid !== undefined)
39
+ sp.set("minBid", String(params.minBid));
40
+ if (params.maxBid !== undefined)
41
+ sp.set("maxBid", String(params.maxBid));
42
+ if (params.dateFrom)
43
+ sp.set("dateFrom", params.dateFrom);
44
+ if (params.dateTo)
45
+ sp.set("dateTo", params.dateTo);
46
+ if (params.preOrderStatus)
47
+ sp.set("preOrderStatus", params.preOrderStatus);
48
+ if (params.freeShipping !== undefined)
49
+ sp.set("freeShipping", String(params.freeShipping));
34
50
  const qs = sp.toString();
35
51
  const query = useQuery({
36
52
  queryKey: ["products", qs],
@@ -68,6 +68,10 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
68
68
  canFilter: boolean;
69
69
  canSort: boolean;
70
70
  };
71
+ storeId: {
72
+ canFilter: boolean;
73
+ canSort: boolean;
74
+ };
71
75
  sellerName: {
72
76
  canFilter: boolean;
73
77
  canSort: boolean;
@@ -184,6 +188,10 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
184
188
  canFilter: boolean;
185
189
  canSort: boolean;
186
190
  };
191
+ freeShipping: {
192
+ canFilter: boolean;
193
+ canSort: boolean;
194
+ };
187
195
  };
188
196
  list(model: SieveModel, opts?: {
189
197
  sellerId?: string;
@@ -314,6 +314,7 @@ ProductRepository.SIEVE_FIELDS = {
314
314
  condition: { canFilter: true, canSort: false },
315
315
  status: { canFilter: true, canSort: true },
316
316
  sellerId: { canFilter: true, canSort: false },
317
+ storeId: { canFilter: true, canSort: false },
317
318
  sellerName: { canFilter: true, canSort: true },
318
319
  featured: { canFilter: true, canSort: false },
319
320
  isAuction: { canFilter: true, canSort: false },
@@ -343,6 +344,7 @@ ProductRepository.SIEVE_FIELDS = {
343
344
  features: { canFilter: true, canSort: false },
344
345
  insurance: { canFilter: true, canSort: false },
345
346
  currency: { canFilter: true, canSort: false },
347
+ freeShipping: { canFilter: true, canSort: false },
346
348
  };
347
349
  export class ProductsRepository extends ProductRepository {
348
350
  }
@@ -213,10 +213,13 @@ export declare const productItemSchema: z.ZodObject<{
213
213
  condition?: "new" | "used" | "refurbished" | "broken" | "like_new" | "good" | "fair" | "poor" | undefined;
214
214
  inStock?: boolean | undefined;
215
215
  brand?: string | undefined;
216
+ currentBid?: number | undefined;
217
+ startingBid?: number | undefined;
218
+ auctionEndDate?: string | Date | undefined;
219
+ preOrderDeliveryDate?: string | Date | undefined;
216
220
  originalPrice?: number | undefined;
217
221
  mainImage?: string | undefined;
218
222
  isPromoted?: boolean | undefined;
219
- currentBid?: number | undefined;
220
223
  availableQuantity?: number | undefined;
221
224
  sellerAvatar?: string | undefined;
222
225
  listingType?: "fixed" | "standard" | "auction" | "pre-order" | undefined;
@@ -240,16 +243,13 @@ export declare const productItemSchema: z.ZodObject<{
240
243
  discountPercent: number;
241
244
  quantity: number;
242
245
  }[] | undefined;
243
- startingBid?: number | undefined;
244
246
  bidCount?: number | undefined;
245
- auctionEndDate?: string | Date | undefined;
246
247
  reservePrice?: number | undefined;
247
248
  buyNowPrice?: number | undefined;
248
249
  minBidIncrement?: number | undefined;
249
250
  autoExtendable?: boolean | undefined;
250
251
  auctionExtensionMinutes?: number | undefined;
251
252
  auctionShippingPaidBy?: "seller" | "winner" | undefined;
252
- preOrderDeliveryDate?: string | Date | undefined;
253
253
  preOrderDepositPercent?: number | undefined;
254
254
  preOrderDepositAmount?: number | undefined;
255
255
  preOrderMaxQuantity?: number | undefined;
@@ -304,10 +304,13 @@ export declare const productItemSchema: z.ZodObject<{
304
304
  condition?: "new" | "used" | "refurbished" | "broken" | "like_new" | "good" | "fair" | "poor" | undefined;
305
305
  inStock?: boolean | undefined;
306
306
  brand?: string | undefined;
307
+ currentBid?: number | undefined;
308
+ startingBid?: number | undefined;
309
+ auctionEndDate?: string | Date | undefined;
310
+ preOrderDeliveryDate?: string | Date | undefined;
307
311
  originalPrice?: number | undefined;
308
312
  mainImage?: string | undefined;
309
313
  isPromoted?: boolean | undefined;
310
- currentBid?: number | undefined;
311
314
  availableQuantity?: number | undefined;
312
315
  sellerAvatar?: string | undefined;
313
316
  listingType?: "fixed" | "standard" | "auction" | "pre-order" | undefined;
@@ -331,16 +334,13 @@ export declare const productItemSchema: z.ZodObject<{
331
334
  discountPercent: number;
332
335
  quantity: number;
333
336
  }[] | undefined;
334
- startingBid?: number | undefined;
335
337
  bidCount?: number | undefined;
336
- auctionEndDate?: string | Date | undefined;
337
338
  reservePrice?: number | undefined;
338
339
  buyNowPrice?: number | undefined;
339
340
  minBidIncrement?: number | undefined;
340
341
  autoExtendable?: boolean | undefined;
341
342
  auctionExtensionMinutes?: number | undefined;
342
343
  auctionShippingPaidBy?: "seller" | "winner" | undefined;
343
- preOrderDeliveryDate?: string | Date | undefined;
344
344
  preOrderDepositPercent?: number | undefined;
345
345
  preOrderDepositAmount?: number | undefined;
346
346
  preOrderMaxQuantity?: number | undefined;
@@ -115,9 +115,21 @@ export interface ProductListParams {
115
115
  maxPrice?: number;
116
116
  inStock?: boolean;
117
117
  isAuction?: boolean;
118
+ isPreOrder?: boolean;
118
119
  sellerId?: string;
120
+ storeId?: string;
119
121
  sort?: string;
120
122
  page?: number;
121
123
  perPage?: number;
122
124
  featured?: boolean;
125
+ /** Auction-specific: current bid range */
126
+ minBid?: number;
127
+ maxBid?: number;
128
+ /** Auction: end date range / Pre-order: delivery date range */
129
+ dateFrom?: string;
130
+ dateTo?: string;
131
+ /** Pre-order production status */
132
+ preOrderStatus?: string;
133
+ /** Shipping / free shipping */
134
+ freeShipping?: boolean;
123
135
  }
@@ -0,0 +1,9 @@
1
+ export interface CouponsIndexListingProps {
2
+ /** Pre-fetched coupons to show on first render */
3
+ initialCoupons?: any[];
4
+ /** If set, only show coupons from this store */
5
+ storeSlug?: string;
6
+ /** If set, only show coupons from this seller */
7
+ sellerId?: string;
8
+ }
9
+ export declare function CouponsIndexListing({ initialCoupons, storeSlug, sellerId, }: CouponsIndexListingProps): import("react/jsx-runtime").JSX.Element;