@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
@@ -1,29 +1,39 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ 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 { Container, Div, Grid, Input, Pagination, Section, SlottedListingView, SortDropdown, Stack, Text, } from "../../../ui";
7
+ import { Pagination, SortDropdown } from "../../../ui";
6
8
  import { ROUTES } from "../../../next";
7
- import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
8
- import { ProductFilters } from "../../products/components/ProductFilters";
9
+ import { ProductGrid } from "../../products/components/ProductGrid";
10
+ import { ProductFilters, PRODUCT_PUBLIC_SORT_OPTIONS } from "../../products/components/ProductFilters";
9
11
  export function StoreProductsListing({ sellerId, initialData }) {
10
12
  const table = useUrlTable({ defaults: { pageSize: "24", sort: "-createdAt" } });
13
+ const [searchInput, setSearchInput] = useState(table.get("q") || "");
14
+ const [filterOpen, setFilterOpen] = useState(false);
15
+ const [view, setView] = useState(table.get("view") || "card");
11
16
  const params = {
12
17
  q: table.get("q") || undefined,
13
18
  condition: table.get("condition") || undefined,
19
+ brand: table.get("brand") || undefined,
14
20
  minPrice: table.get("minPrice") ? Number(table.get("minPrice")) : undefined,
15
21
  maxPrice: table.get("maxPrice") ? Number(table.get("maxPrice")) : undefined,
16
- sort: table.get("sort") || undefined,
22
+ sort: table.get("sort") || "-createdAt",
17
23
  page: table.getNumber("page", 1),
18
24
  perPage: table.getNumber("pageSize", 24),
19
25
  sellerId,
20
26
  isAuction: false,
21
27
  };
22
28
  const { products, total, totalPages, page, isLoading } = useProducts(params, { initialData });
23
- return (_jsx(Section, { children: _jsx(Container, { size: "xl", 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 store products...", className: "max-w-sm" })), renderSort: (value, onChange) => (_jsx(SortDropdown, { value: value, onChange: onChange, options: [
24
- { value: "-createdAt", label: "Newest First" },
25
- { value: "-price", label: "Price: High to Low" },
26
- { value: "price", label: "Price: Low to High" },
27
- { value: "title", label: "Title A-Z" },
28
- ] })), renderFilters: () => _jsx(ProductFilters, { table: table }), renderTable: () => products.length === 0 && !isLoading ? (_jsxs(Stack, { align: "center", gap: "3", className: "justify-center py-24 text-center", children: [_jsx(Text, { className: "text-xl font-medium text-zinc-900 dark:text-zinc-50", children: "No products yet" }), _jsx(Text, { className: "text-sm text-zinc-500", children: "This store has not listed any products yet." })] })) : (_jsx(Div, { children: _jsx(Grid, { cols: "productCards", gap: "md", children: products.map((p) => (_jsx(InteractiveProductCard, { product: p, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug ?? p.id)) }, p.id))) }) })), renderPagination: () => (_jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) })), total: total, isLoading: isLoading }) }) }));
29
+ const commitSearch = useCallback(() => {
30
+ table.set("q", searchInput.trim());
31
+ table.setPage(1);
32
+ }, [searchInput, table]);
33
+ const handleViewToggle = (next) => {
34
+ setView(next);
35
+ table.set("view", next);
36
+ };
37
+ const closeFilters = () => setFilterOpen(false);
38
+ return (_jsxs("div", { className: "min-h-[200px]", 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: (e) => e.key === "Enter" && commitSearch(), placeholder: "Search store 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" ? "bg-primary text-white" : "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" ? "bg-primary text-white" : "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 xl:grid-cols-5 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, emptyLabel: "This store has no products yet." })), 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" }) }), _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" }) })] })] }))] }));
29
39
  }
@@ -16,13 +16,8 @@ const PAGE_SIZE = 12;
16
16
  export function StoreReviewsListing({ storeSlug }) {
17
17
  const [ratingFilter, setRatingFilter] = useState(0);
18
18
  const [page, setPage] = useState(1);
19
- const { reviews, averageRating, totalReviews, isLoading } = useStoreReviews(storeSlug);
20
- const filtered = ratingFilter > 0
21
- ? reviews.filter((r) => Math.round(r.rating) === ratingFilter)
22
- : reviews;
23
- const totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE));
24
- const start = (page - 1) * PAGE_SIZE;
25
- const paginated = filtered.slice(start, start + PAGE_SIZE);
19
+ const { reviews, averageRating, totalReviews, totalPages, isLoading } = useStoreReviews(storeSlug, { rating: ratingFilter || undefined, page, pageSize: PAGE_SIZE });
20
+ const paginated = reviews;
26
21
  return (_jsx(Section, { children: _jsxs(Container, { size: "xl", children: [totalReviews > 0 && (_jsxs(Row, { align: "center", gap: "sm", className: "mb-6", children: [_jsx(Span, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-100", children: averageRating.toFixed(1) }), _jsxs(Span, { className: "text-zinc-500 text-sm", children: ["/ 5 \u00B7 ", totalReviews, " reviews"] })] })), _jsx(Row, { gap: "xs", className: "mb-6 flex-wrap", children: STAR_OPTIONS.map((opt) => (_jsx("button", { type: "button", onClick: () => { setRatingFilter(opt.value); setPage(1); }, className: [
27
22
  "rounded-full border px-3 py-1 text-sm font-medium transition-colors",
28
23
  ratingFilter === opt.value
@@ -1,22 +1,56 @@
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, X } from "lucide-react";
3
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
4
6
  import { useStores } from "../hooks/useStores";
5
- import { Div, Grid, Input, Pagination, SlottedListingView, SortDropdown, } from "../../../ui";
7
+ import { Pagination, SortDropdown } from "../../../ui";
6
8
  import { ROUTES } from "../../../next";
7
9
  import { InteractiveStoreCard } from "./InteractiveStoreCard";
10
+ import { StoreFilters } from "./StoreFilters";
11
+ const STORE_SORT_OPTIONS = [
12
+ { value: "-createdAt", label: "Newest First" },
13
+ { value: "storeName", label: "Name A–Z" },
14
+ { value: "-itemsSold", label: "Most Sales" },
15
+ { value: "-averageRating", label: "Top Rated" },
16
+ ];
8
17
  export function StoresIndexListing({ initialData }) {
9
18
  const table = useUrlTable({ defaults: { pageSize: "24", sort: "-createdAt" } });
19
+ const [searchInput, setSearchInput] = useState(table.get("q") || "");
20
+ const [filterOpen, setFilterOpen] = useState(false);
21
+ const commitSearch = useCallback(() => {
22
+ table.set("q", searchInput.trim());
23
+ table.setPage(1);
24
+ }, [searchInput, table]);
25
+ const clearSearch = useCallback(() => {
26
+ setSearchInput("");
27
+ table.set("q", "");
28
+ }, [table]);
29
+ // Build sieve filters from URL params for rating, product count, featured
30
+ const ratingRaw = table.get("rating");
31
+ const minProductCount = table.get("minProductCount");
32
+ const maxProductCount = table.get("maxProductCount");
33
+ const featured = table.get("featured");
34
+ const filterParts = [];
35
+ if (ratingRaw) {
36
+ const ratings = ratingRaw.split("|").filter(Boolean);
37
+ if (ratings.length === 1)
38
+ filterParts.push(`averageRating>=${ratings[0]}`);
39
+ }
40
+ if (minProductCount)
41
+ filterParts.push(`stats.totalProducts>=${minProductCount}`);
42
+ if (maxProductCount)
43
+ filterParts.push(`stats.totalProducts<=${maxProductCount}`);
44
+ if (featured === "true")
45
+ filterParts.push("isFeatured==true");
10
46
  const { stores, total, totalPages, isLoading } = useStores({
47
+ q: table.get("q") || undefined,
11
48
  page: table.getNumber("page", 1),
12
49
  pageSize: table.getNumber("pageSize", 24),
13
50
  sort: table.get("sort") || undefined,
14
51
  category: table.get("category") || undefined,
52
+ filters: filterParts.length > 0 ? filterParts.join(",") : undefined,
15
53
  }, { initialData });
16
- 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 stores...", className: "max-w-sm" })), renderSort: (value, onChange) => (_jsx(SortDropdown, { value: value, onChange: onChange, options: [
17
- { value: "-createdAt", label: "Newest First" },
18
- { value: "storeName", label: "A-Z" },
19
- { value: "-itemsSold", label: "Most Sales" },
20
- { value: "-averageRating", label: "Top Rated" },
21
- ] })), renderTable: () => (_jsx(Grid, { cols: 3, gap: "md", children: stores.map((store) => (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(store.storeSlug ?? store.id)) }, store.storeSlug ?? store.id))) })), renderPagination: () => (_jsx(Pagination, { currentPage: table.getNumber("page", 1), totalPages: totalPages, onPageChange: (p) => table.setPage(p) })), total: total, isLoading: isLoading }) }));
54
+ const closeFilters = () => setFilterOpen(false);
55
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsxs("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(Search, { className: "ml-3 h-4 w-4 flex-shrink-0 text-zinc-400" }), _jsx("input", { type: "text", value: searchInput, onChange: (e) => setSearchInput(e.target.value), onKeyDown: (e) => e.key === "Enter" && commitSearch(), placeholder: "Search stores\u2026", className: "min-w-0 flex-1 bg-transparent px-2.5 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none" }), searchInput && (_jsx("button", { type: "button", onClick: clearSearch, className: "mr-2 flex-shrink-0 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300", "aria-label": "Clear search", children: _jsx(X, { className: "h-3.5 w-3.5" }) })), _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: STORE_SORT_OPTIONS })] })] }), !isLoading && (_jsxs("p", { className: "mt-1.5 text-xs text-zinc-400 dark:text-zinc-500", children: [total, " store", total !== 1 ? "s" : ""] }))] }), _jsxs("div", { className: "py-6", children: [isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-4 space-y-2.5", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsx("div", { className: "h-10 w-10 rounded-lg bg-zinc-200 dark:bg-slate-700" }) }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] })] }, i))) })) : stores.length === 0 ? (_jsx("p", { className: "py-16 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No stores found." })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: stores.map((store) => (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(store.storeSlug ?? store.id)) }, store.storeSlug ?? store.id))) })), totalPages > 1 && (_jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Pagination, { currentPage: table.getNumber("page", 1), 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(StoreFilters, { table: table }) }), _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" }) })] })] }))] }));
22
56
  }
@@ -1 +1,6 @@
1
- export declare function StoresIndexPageView(): Promise<import("react/jsx-runtime").JSX.Element>;
1
+ type SearchParams = Record<string, string | string[]>;
2
+ export interface StoresIndexPageViewProps {
3
+ searchParams?: SearchParams;
4
+ }
5
+ export declare function StoresIndexPageView({ searchParams }: StoresIndexPageViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
6
+ export {};
@@ -3,9 +3,16 @@ import { storeRepository } from "../../../repositories";
3
3
  import { Container, Heading, Main, Section } from "../../../ui";
4
4
  import { AdSlot } from "../../homepage/components/AdSlot";
5
5
  import { StoresIndexListing } from "./StoresIndexListing";
6
- export async function StoresIndexPageView() {
6
+ function sp(params, key) {
7
+ const v = params[key];
8
+ return Array.isArray(v) ? v[0] ?? "" : v ?? "";
9
+ }
10
+ export async function StoresIndexPageView({ searchParams = {} }) {
11
+ const sort = sp(searchParams, "sort") || "-createdAt";
12
+ const page = Number(sp(searchParams, "page")) || 1;
13
+ const pageSize = Number(sp(searchParams, "pageSize")) || 24;
7
14
  const result = await storeRepository
8
- .listStores({ page: 1, pageSize: 24, sorts: "-createdAt" }, true)
15
+ .listStores({ page, pageSize, sorts: sort }, true)
9
16
  .catch(() => null);
10
17
  return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8 text-3xl font-semibold text-zinc-900 dark:text-zinc-50", children: "Stores" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(StoresIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
11
18
  }
@@ -22,3 +22,6 @@ export { StoreAuctionsListing } from "./StoreAuctionsListing";
22
22
  export type { StoreAuctionsListingProps } from "./StoreAuctionsListing";
23
23
  export { StoreReviewsListing } from "./StoreReviewsListing";
24
24
  export type { StoreReviewsListingProps } from "./StoreReviewsListing";
25
+ export { StorePreOrdersListing } from "./StorePreOrdersListing";
26
+ export type { StorePreOrdersListingProps } from "./StorePreOrdersListing";
27
+ export type { StorePreOrdersPageViewProps } from "./StorePreOrdersPageView";
@@ -11,3 +11,4 @@ export { StoresIndexListing } from "./StoresIndexListing";
11
11
  export { StoreProductsListing } from "./StoreProductsListing";
12
12
  export { StoreAuctionsListing } from "./StoreAuctionsListing";
13
13
  export { StoreReviewsListing } from "./StoreReviewsListing";
14
+ export { StorePreOrdersListing } from "./StorePreOrdersListing";
@@ -44,13 +44,19 @@ export declare function useStoreAuctions(storeSlug: string, params?: string, opt
44
44
  error: string | null;
45
45
  refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<StoreAuctionsResponse, Error>>;
46
46
  };
47
- export declare function useStoreReviews(storeSlug: string, opts?: {
47
+ export declare function useStoreReviews(storeSlug: string, params?: {
48
+ rating?: number;
49
+ page?: number;
50
+ pageSize?: number;
51
+ }, opts?: {
48
52
  enabled?: boolean;
49
53
  endpoint?: string;
50
54
  }): {
51
55
  reviews: import("..").StoreReview[];
52
56
  averageRating: number;
53
57
  totalReviews: number;
58
+ totalFiltered: number;
59
+ totalPages: number;
54
60
  ratingDistribution: Record<number, number>;
55
61
  isLoading: boolean;
56
62
  error: string | null;
@@ -3,6 +3,8 @@ import { apiClient } from "../../../http";
3
3
  import { STORE_ENDPOINTS } from "../../../constants/api-endpoints";
4
4
  export function useStores(params = {}, opts) {
5
5
  const sp = new URLSearchParams();
6
+ if (params.q)
7
+ sp.set("q", params.q);
6
8
  if (params.category)
7
9
  sp.set("category", params.category);
8
10
  if (params.page)
@@ -77,10 +79,19 @@ export function useStoreAuctions(storeSlug, params, opts) {
77
79
  refetch,
78
80
  };
79
81
  }
80
- export function useStoreReviews(storeSlug, opts) {
81
- const endpoint = opts?.endpoint ?? STORE_ENDPOINTS.REVIEWS(storeSlug);
82
+ export function useStoreReviews(storeSlug, params, opts) {
83
+ const sp = new URLSearchParams();
84
+ if (params?.rating)
85
+ sp.set("rating", String(params.rating));
86
+ if (params?.page && params.page > 1)
87
+ sp.set("page", String(params.page));
88
+ if (params?.pageSize)
89
+ sp.set("pageSize", String(params.pageSize));
90
+ const qs = sp.toString();
91
+ const baseEndpoint = opts?.endpoint ?? STORE_ENDPOINTS.REVIEWS(storeSlug);
92
+ const endpoint = qs ? `${baseEndpoint}?${qs}` : baseEndpoint;
82
93
  const { data, isLoading, error, refetch } = useQuery({
83
- queryKey: ["store-reviews", storeSlug],
94
+ queryKey: ["store-reviews", storeSlug, qs],
84
95
  queryFn: () => apiClient.get(endpoint),
85
96
  enabled: (opts?.enabled ?? true) && !!storeSlug,
86
97
  });
@@ -88,6 +99,8 @@ export function useStoreReviews(storeSlug, opts) {
88
99
  reviews: data?.reviews ?? [],
89
100
  averageRating: data?.averageRating ?? 0,
90
101
  totalReviews: data?.totalReviews ?? 0,
102
+ totalFiltered: data?.totalFiltered ?? 0,
103
+ totalPages: data?.totalPages ?? 1,
91
104
  ratingDistribution: data?.ratingDistribution ?? {},
92
105
  isLoading,
93
106
  error: error instanceof Error ? error.message : null,
@@ -43,9 +43,9 @@ export declare const storeListItemSchema: z.ZodObject<{
43
43
  totalProducts?: number | undefined;
44
44
  averageRating?: number | undefined;
45
45
  totalReviews?: number | undefined;
46
- storeLogoURL?: string | undefined;
47
46
  storeDescription?: string | undefined;
48
47
  storeCategory?: string | undefined;
48
+ storeLogoURL?: string | undefined;
49
49
  storeBannerURL?: string | undefined;
50
50
  }, {
51
51
  id: string;
@@ -59,9 +59,9 @@ export declare const storeListItemSchema: z.ZodObject<{
59
59
  totalProducts?: number | undefined;
60
60
  averageRating?: number | undefined;
61
61
  totalReviews?: number | undefined;
62
- storeLogoURL?: string | undefined;
63
62
  storeDescription?: string | undefined;
64
63
  storeCategory?: string | undefined;
64
+ storeLogoURL?: string | undefined;
65
65
  storeBannerURL?: string | undefined;
66
66
  }>;
67
67
  export declare const storeListParamsSchema: z.ZodObject<{
@@ -69,6 +69,7 @@ export interface StoreProductsResponse {
69
69
  hasMore: boolean;
70
70
  }
71
71
  export interface StoreListParams {
72
+ q?: string;
72
73
  category?: string;
73
74
  page?: number;
74
75
  pageSize?: number;
@@ -98,5 +99,7 @@ export interface StoreReviewsData {
98
99
  reviews: StoreReview[];
99
100
  averageRating: number;
100
101
  totalReviews: number;
102
+ totalFiltered: number;
103
+ totalPages: number;
101
104
  ratingDistribution: Record<number, number>;
102
105
  }
@@ -0,0 +1,20 @@
1
+ import { type GuestWishlistItem } from "../utils/guest-wishlist";
2
+ /**
3
+ * useGuestWishlist
4
+ *
5
+ * React state wrapper around guest wishlist storage.
6
+ */
7
+ export declare function useGuestWishlist(): {
8
+ items: GuestWishlistItem[];
9
+ count: number;
10
+ countByType: (type: "product" | "auction" | "preorder" | "category" | "store") => number;
11
+ add: (itemId: string, type: "product" | "auction" | "preorder" | "category" | "store", snapshot?: {
12
+ title?: string;
13
+ image?: string;
14
+ }) => void;
15
+ remove: (itemId: string, type: "product" | "auction" | "preorder" | "category" | "store") => void;
16
+ isInWishlist: (itemId: string, type: "product" | "auction" | "preorder" | "category" | "store") => boolean;
17
+ clear: () => void;
18
+ getByType: (type: "product" | "auction" | "preorder" | "category" | "store") => GuestWishlistItem[];
19
+ isInitialized: boolean;
20
+ };
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { useState, useEffect, useCallback } from "react";
3
+ import { getGuestWishlistItems, addToGuestWishlist, removeFromGuestWishlist, isInGuestWishlist, clearGuestWishlist, getGuestWishlistByType, } from "../utils/guest-wishlist";
4
+ /**
5
+ * useGuestWishlist
6
+ *
7
+ * React state wrapper around guest wishlist storage.
8
+ */
9
+ export function useGuestWishlist() {
10
+ const [items, setItems] = useState([]);
11
+ const [isInitialized, setIsInitialized] = useState(false);
12
+ useEffect(() => {
13
+ setItems(getGuestWishlistItems());
14
+ setIsInitialized(true);
15
+ }, []);
16
+ const add = useCallback((itemId, type, snapshot) => {
17
+ const updated = addToGuestWishlist(itemId, type, snapshot);
18
+ setItems(updated);
19
+ }, []);
20
+ const remove = useCallback((itemId, type) => {
21
+ const updated = removeFromGuestWishlist(itemId, type);
22
+ setItems(updated);
23
+ }, []);
24
+ const isInWishlist = useCallback((itemId, type) => {
25
+ return isInGuestWishlist(itemId, type);
26
+ }, []);
27
+ const clear = useCallback(() => {
28
+ clearGuestWishlist();
29
+ setItems([]);
30
+ }, []);
31
+ const getByType = useCallback((type) => {
32
+ return getGuestWishlistByType(type);
33
+ }, []);
34
+ const count = items.length;
35
+ const countByType = (type) => {
36
+ return items.filter((i) => i.type === type).length;
37
+ };
38
+ return {
39
+ items,
40
+ count,
41
+ countByType,
42
+ add,
43
+ remove,
44
+ isInWishlist,
45
+ clear,
46
+ getByType,
47
+ isInitialized,
48
+ };
49
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * useWishlistCount
3
+ *
4
+ * Returns a live wishlist count, using API for authenticated users
5
+ * and falling back to guest-wishlist storage for unauthenticated users.
6
+ */
7
+ export declare function useWishlistCount(): number;
@@ -0,0 +1,31 @@
1
+ "use client";
2
+ import { useQuery } from "@tanstack/react-query";
3
+ import { useAuth } from "../../auth/hooks/useAuth";
4
+ import { useGuestWishlist } from "../hooks/useGuestWishlist";
5
+ import { apiClient } from "../../../http";
6
+ import { WISHLIST_ENDPOINTS } from "../../../constants/api-endpoints";
7
+ /**
8
+ * useWishlistCount
9
+ *
10
+ * Returns a live wishlist count, using API for authenticated users
11
+ * and falling back to guest-wishlist storage for unauthenticated users.
12
+ */
13
+ export function useWishlistCount() {
14
+ const { user } = useAuth();
15
+ const { count: guestCount } = useGuestWishlist();
16
+ // Query for authenticated users
17
+ const { data } = useQuery({
18
+ queryKey: ["wishlist", "count"],
19
+ queryFn: () => {
20
+ if (!user?.uid)
21
+ return null;
22
+ return apiClient.get(WISHLIST_ENDPOINTS.BY_USER(user.uid));
23
+ },
24
+ enabled: !!user?.uid,
25
+ });
26
+ // Return authenticated count or fall back to guest count
27
+ if (user?.uid && data) {
28
+ return data.total;
29
+ }
30
+ return guestCount;
31
+ }
@@ -0,0 +1,56 @@
1
+ import type { WishlistResponse } from "../types";
2
+ interface UseWishlistWithGuestOptions {
3
+ initialData?: WishlistResponse;
4
+ enabled?: boolean;
5
+ endpoint?: string;
6
+ }
7
+ /**
8
+ * useWishlistWithGuest
9
+ *
10
+ * Unified wishlist hook that uses API for authenticated users
11
+ * and localStorage for guest users.
12
+ */
13
+ export declare function useWishlistWithGuest(userId: string | null | undefined, opts?: UseWishlistWithGuestOptions): {
14
+ items: import("..").WishlistItem[];
15
+ total: number;
16
+ wishlistedIds: Set<string>;
17
+ isWishlisted: (productId: string) => boolean;
18
+ isLoading: boolean;
19
+ error: Error | null;
20
+ refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<WishlistResponse, Error>>;
21
+ isGuest: boolean;
22
+ guestWishlist?: undefined;
23
+ } | {
24
+ items: {
25
+ id: string;
26
+ productId: string;
27
+ productTitle: string | undefined;
28
+ productImage: string | undefined;
29
+ productPrice: number;
30
+ productSlug: string;
31
+ addedAt: string | undefined;
32
+ type: "category" | "product" | "preorder" | "auction" | "store";
33
+ }[];
34
+ total: number;
35
+ wishlistedIds: Set<string>;
36
+ isWishlisted: (productId: string) => boolean;
37
+ isLoading: boolean;
38
+ error: null;
39
+ refetch: () => Promise<void>;
40
+ isGuest: boolean;
41
+ guestWishlist: {
42
+ items: import("..").GuestWishlistItem[];
43
+ count: number;
44
+ countByType: (type: "product" | "auction" | "preorder" | "category" | "store") => number;
45
+ add: (itemId: string, type: "product" | "auction" | "preorder" | "category" | "store", snapshot?: {
46
+ title?: string;
47
+ image?: string;
48
+ }) => void;
49
+ remove: (itemId: string, type: "product" | "auction" | "preorder" | "category" | "store") => void;
50
+ isInWishlist: (itemId: string, type: "product" | "auction" | "preorder" | "category" | "store") => boolean;
51
+ clear: () => void;
52
+ getByType: (type: "product" | "auction" | "preorder" | "category" | "store") => import("..").GuestWishlistItem[];
53
+ isInitialized: boolean;
54
+ };
55
+ };
56
+ export {};
@@ -0,0 +1,57 @@
1
+ "use client";
2
+ import { useWishlist } from "./useWishlist";
3
+ import { useGuestWishlist } from "./useGuestWishlist";
4
+ /**
5
+ * useWishlistWithGuest
6
+ *
7
+ * Unified wishlist hook that uses API for authenticated users
8
+ * and localStorage for guest users.
9
+ */
10
+ export function useWishlistWithGuest(userId, opts) {
11
+ const isAuthenticated = !!userId;
12
+ // For authenticated users
13
+ const authenticatedWishlist = useWishlist(userId ?? "", {
14
+ ...opts,
15
+ enabled: opts?.enabled !== false && isAuthenticated,
16
+ });
17
+ // For guest users
18
+ const guestWishlist = useGuestWishlist();
19
+ // Return appropriate wishlist based on authentication status
20
+ if (isAuthenticated) {
21
+ return {
22
+ items: authenticatedWishlist.items,
23
+ total: authenticatedWishlist.total,
24
+ wishlistedIds: authenticatedWishlist.wishlistedIds,
25
+ isWishlisted: authenticatedWishlist.isWishlisted,
26
+ isLoading: authenticatedWishlist.isLoading,
27
+ error: authenticatedWishlist.error,
28
+ refetch: authenticatedWishlist.refetch,
29
+ isGuest: false,
30
+ };
31
+ }
32
+ else {
33
+ // Convert guest items to wishlist items format
34
+ const items = guestWishlist.items.map((item) => ({
35
+ id: `${item.type}-${item.itemId}`,
36
+ productId: item.type === "product" ? item.itemId : "",
37
+ productTitle: item.title,
38
+ productImage: item.image,
39
+ productPrice: 0,
40
+ productSlug: item.itemId,
41
+ addedAt: item.addedAt,
42
+ type: item.type,
43
+ }));
44
+ const ids = new Set(items.map((i) => i.productId).filter(Boolean));
45
+ return {
46
+ items,
47
+ total: guestWishlist.count,
48
+ wishlistedIds: ids,
49
+ isWishlisted: (productId) => ids.has(productId),
50
+ isLoading: !guestWishlist.isInitialized,
51
+ error: null,
52
+ refetch: async () => { },
53
+ isGuest: true,
54
+ guestWishlist, // Expose guest wishlist for add/remove operations
55
+ };
56
+ }
57
+ }
@@ -4,6 +4,9 @@ export * from "./columns";
4
4
  export * from "./hooks/useWishlist";
5
5
  export * from "./hooks/useUserWishlist";
6
6
  export * from "./hooks/useWishlistToggle";
7
+ export * from "./hooks/useGuestWishlist";
8
+ export * from "./hooks/useWishlistWithGuest";
9
+ export * from "./utils/guest-wishlist";
7
10
  export * from "./components";
8
11
  export type { UserWishlistItem } from "./repository/user-wishlist.repository";
9
12
  export { manifest } from "./manifest";
@@ -4,5 +4,8 @@ export * from "./columns";
4
4
  export * from "./hooks/useWishlist";
5
5
  export * from "./hooks/useUserWishlist";
6
6
  export * from "./hooks/useWishlistToggle";
7
+ export * from "./hooks/useGuestWishlist";
8
+ export * from "./hooks/useWishlistWithGuest";
9
+ export * from "./utils/guest-wishlist";
7
10
  export * from "./components";
8
11
  export { manifest } from "./manifest";
@@ -0,0 +1,22 @@
1
+ export interface GuestWishlistItem {
2
+ itemId: string;
3
+ type: "product" | "auction" | "preorder" | "category" | "store";
4
+ title?: string;
5
+ image?: string;
6
+ addedAt?: string;
7
+ }
8
+ export interface GuestWishlistStorage {
9
+ getItem(key: string): string | null;
10
+ setItem(key: string, value: string): void;
11
+ removeItem(key: string): void;
12
+ }
13
+ export declare function getGuestWishlistItems(storage?: GuestWishlistStorage | null, key?: string): GuestWishlistItem[];
14
+ export declare function addToGuestWishlist(itemId: string, type: "product" | "auction" | "preorder" | "category" | "store", snapshot?: {
15
+ title?: string;
16
+ image?: string;
17
+ }, storage?: GuestWishlistStorage | null, key?: string): GuestWishlistItem[];
18
+ export declare function removeFromGuestWishlist(itemId: string, type: "product" | "auction" | "preorder" | "category" | "store", storage?: GuestWishlistStorage | null, key?: string): GuestWishlistItem[];
19
+ export declare function isInGuestWishlist(itemId: string, type: "product" | "auction" | "preorder" | "category" | "store", storage?: GuestWishlistStorage | null, key?: string): boolean;
20
+ export declare function clearGuestWishlist(storage?: GuestWishlistStorage | null, key?: string): void;
21
+ export declare function getGuestWishlistCount(storage?: GuestWishlistStorage | null, key?: string): number;
22
+ export declare function getGuestWishlistByType(type: "product" | "auction" | "preorder" | "category" | "store", storage?: GuestWishlistStorage | null, key?: string): GuestWishlistItem[];