@mohasinac/appkit 2.7.10 → 2.7.12

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 (148) hide show
  1. package/dist/_internal/server/jobs/core/assignSpinPrize.js +8 -7
  2. package/dist/_internal/server/jobs/core/bundleStockSync.js +11 -6
  3. package/dist/_internal/server/jobs/core/countersReconcile.js +3 -2
  4. package/dist/_internal/server/jobs/core/onProductStockChange.js +12 -7
  5. package/dist/_internal/server/jobs/core/onProductWrite.js +3 -2
  6. package/dist/_internal/server/jobs/core/prizeRevealClose.js +7 -5
  7. package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +11 -6
  8. package/dist/_internal/server/jobs/core/prizeRevealOpen.js +13 -7
  9. package/dist/_internal/server/jobs/core/prizeRevealReminder.js +9 -4
  10. package/dist/_internal/server/jobs/core/triggerEventRaffle.js +3 -2
  11. package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
  12. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
  13. package/dist/_internal/shared/features/events/schema.d.ts +4 -4
  14. package/dist/_internal/shared/features/products/schema.d.ts +20 -20
  15. package/dist/_internal/shared/features/promotions/schema.d.ts +2 -2
  16. package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
  17. package/dist/configs/next.js +17 -1
  18. package/dist/constants/field-names.d.ts +813 -0
  19. package/dist/constants/field-names.js +918 -0
  20. package/dist/constants/sort.d.ts +7 -0
  21. package/dist/constants/sort.js +5 -0
  22. package/dist/constants/table-keys.d.ts +48 -0
  23. package/dist/constants/table-keys.js +46 -0
  24. package/dist/features/account/components/AddressesIndexListing.js +9 -6
  25. package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -1
  26. package/dist/features/admin/components/AdminBidsView.js +4 -1
  27. package/dist/features/admin/components/AdminBlogView.js +9 -2
  28. package/dist/features/admin/components/AdminBrandsView.js +9 -2
  29. package/dist/features/admin/components/AdminCarouselView.js +4 -1
  30. package/dist/features/admin/components/AdminCartsView.js +4 -1
  31. package/dist/features/admin/components/AdminCategoriesView.js +9 -2
  32. package/dist/features/admin/components/AdminContactView.js +4 -1
  33. package/dist/features/admin/components/AdminCouponsView.js +4 -1
  34. package/dist/features/admin/components/AdminFaqsView.js +5 -1
  35. package/dist/features/admin/components/AdminFeaturesView.js +5 -2
  36. package/dist/features/admin/components/AdminHistoryView.js +5 -1
  37. package/dist/features/admin/components/AdminNewsletterView.js +4 -1
  38. package/dist/features/admin/components/AdminNotificationsView.js +4 -1
  39. package/dist/features/admin/components/AdminOrdersView.js +14 -12
  40. package/dist/features/admin/components/AdminPayoutsView.js +4 -1
  41. package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -1
  42. package/dist/features/admin/components/AdminProductsView.js +14 -3
  43. package/dist/features/admin/components/AdminReturnRequestsView.js +4 -1
  44. package/dist/features/admin/components/AdminReviewsView.js +4 -1
  45. package/dist/features/admin/components/AdminScammersView.js +4 -1
  46. package/dist/features/admin/components/AdminSessionsView.js +4 -1
  47. package/dist/features/admin/components/AdminStoreAddressesView.js +5 -1
  48. package/dist/features/admin/components/AdminStoresView.js +10 -3
  49. package/dist/features/admin/components/AdminSublistingCategoriesView.js +4 -1
  50. package/dist/features/admin/components/AdminSupportTicketsView.js +4 -1
  51. package/dist/features/admin/components/AdminTeamView.js +4 -1
  52. package/dist/features/admin/components/AdminUsersView.js +9 -12
  53. package/dist/features/admin/components/AdminViewCards.d.ts +12 -0
  54. package/dist/features/admin/components/AdminViewCards.js +53 -0
  55. package/dist/features/admin/components/AdminWishlistsView.js +4 -1
  56. package/dist/features/auctions/repository/bid.repository.js +1 -6
  57. package/dist/features/auctions/schemas/index.d.ts +4 -4
  58. package/dist/features/blog/actions/blog-actions.d.ts +16 -16
  59. package/dist/features/blog/components/BlogFilters.d.ts +28 -28
  60. package/dist/features/blog/components/BlogFilters.js +18 -15
  61. package/dist/features/blog/components/BlogIndexListing.js +24 -13
  62. package/dist/features/blog/schemas/index.d.ts +10 -10
  63. package/dist/features/categories/components/CategoriesIndexListing.js +30 -19
  64. package/dist/features/categories/schemas/index.d.ts +2 -2
  65. package/dist/features/events/components/AdminEventsView.js +5 -1
  66. package/dist/features/events/components/EventFilters.d.ts +24 -24
  67. package/dist/features/events/components/EventFilters.js +16 -13
  68. package/dist/features/events/components/EventStatusBadge.js +1 -0
  69. package/dist/features/events/components/EventsIndexListing.js +36 -24
  70. package/dist/features/events/components/EventsListPageView.js +13 -10
  71. package/dist/features/events/schemas/firestore.d.ts +1 -0
  72. package/dist/features/events/schemas/firestore.js +1 -0
  73. package/dist/features/events/schemas/index.d.ts +1 -1
  74. package/dist/features/events/types/index.d.ts +1 -1
  75. package/dist/features/faq/actions/faq-actions.d.ts +10 -10
  76. package/dist/features/faq/schemas/index.d.ts +4 -4
  77. package/dist/features/layout/TitleBarLayout.js +1 -1
  78. package/dist/features/orders/components/OrderFilters.d.ts +20 -20
  79. package/dist/features/orders/components/OrderFilters.js +33 -30
  80. package/dist/features/orders/repository/orders.repository.js +1 -15
  81. package/dist/features/orders/schemas/index.d.ts +2 -2
  82. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +44 -36
  83. package/dist/features/pre-orders/schemas/index.d.ts +3 -3
  84. package/dist/features/products/components/AuctionsIndexListing.js +63 -30
  85. package/dist/features/products/components/BidHistory.d.ts +0 -1
  86. package/dist/features/products/components/BidHistory.js +1 -1
  87. package/dist/features/products/components/PrizeDrawsIndexListing.js +44 -37
  88. package/dist/features/products/components/ProductDetailPageView.js +1 -1
  89. package/dist/features/products/components/ProductFilters.d.ts +20 -20
  90. package/dist/features/products/components/ProductFilters.js +62 -59
  91. package/dist/features/products/components/ProductsIndexListing.js +37 -29
  92. package/dist/features/products/components/ProductsIndexPageView.js +18 -10
  93. package/dist/features/products/hooks/useProducts.js +2 -0
  94. package/dist/features/products/repository/products.repository.js +2 -22
  95. package/dist/features/products/schemas/index.d.ts +28 -28
  96. package/dist/features/products/schemas/product-features.validators.d.ts +12 -12
  97. package/dist/features/products/types/index.d.ts +2 -0
  98. package/dist/features/promotions/components/CouponsIndexListing.js +21 -17
  99. package/dist/features/promotions/schemas/index.d.ts +4 -4
  100. package/dist/features/reviews/components/ReviewDetailShell.js +6 -5
  101. package/dist/features/reviews/components/ReviewFilters.d.ts +16 -16
  102. package/dist/features/reviews/components/ReviewFilters.js +10 -7
  103. package/dist/features/reviews/components/ReviewsIndexListing.js +25 -14
  104. package/dist/features/reviews/components/ReviewsList.d.ts +5 -2
  105. package/dist/features/reviews/components/ReviewsList.js +11 -5
  106. package/dist/features/reviews/schemas/firestore.d.ts +1 -0
  107. package/dist/features/reviews/schemas/index.d.ts +8 -8
  108. package/dist/features/reviews/types/index.d.ts +1 -0
  109. package/dist/features/search/schemas/index.d.ts +2 -2
  110. package/dist/features/seller/components/SellerAuctionsView.js +5 -1
  111. package/dist/features/seller/components/SellerBidsView.js +4 -1
  112. package/dist/features/seller/components/SellerCouponsView.js +4 -1
  113. package/dist/features/seller/components/SellerOffersView.js +5 -1
  114. package/dist/features/seller/components/SellerOrdersView.js +4 -1
  115. package/dist/features/seller/components/SellerPayoutsView.js +5 -1
  116. package/dist/features/seller/components/SellerPreOrdersView.js +4 -1
  117. package/dist/features/seller/components/SellerPrizeDrawsView.js +4 -1
  118. package/dist/features/seller/components/SellerProductsView.js +4 -1
  119. package/dist/features/seller/schemas/index.d.ts +10 -10
  120. package/dist/features/stores/api/route.js +10 -1
  121. package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
  122. package/dist/features/stores/components/StorePreOrdersListing.js +2 -1
  123. package/dist/features/stores/components/StoreReviewsListing.js +1 -1
  124. package/dist/features/stores/components/StoresIndexListing.js +45 -27
  125. package/dist/features/stores/schemas/firestore.d.ts +1 -0
  126. package/dist/features/stores/schemas/index.d.ts +4 -4
  127. package/dist/index.d.ts +8 -1
  128. package/dist/index.js +12 -1
  129. package/dist/react/hooks/useUrlTable.js +2 -1
  130. package/dist/seed/bids-seed-data.js +45 -44
  131. package/dist/seed/categories-seed-data.js +41 -40
  132. package/dist/seed/coupons-seed-data.js +41 -40
  133. package/dist/seed/products-auctions-seed-data.js +54 -53
  134. package/dist/seed/products-preorders-seed-data.js +25 -24
  135. package/dist/seed/products-prize-draws-seed-data.js +24 -23
  136. package/dist/seed/products-standard-seed-data.js +294 -293
  137. package/dist/seed/reviews-seed-data.js +61 -60
  138. package/dist/seed/scammers-seed-data.js +3 -2
  139. package/dist/seed/stores-seed-data.js +9 -8
  140. package/dist/seed/support-tickets-seed-data.js +13 -12
  141. package/dist/seed/users-seed-data.js +28 -27
  142. package/dist/tailwind-utilities.css +1 -1
  143. package/dist/ui/components/ListingToolbar.d.ts +7 -4
  144. package/dist/ui/components/ListingToolbar.js +4 -3
  145. package/dist/ui/components/SiteLogo.js +1 -1
  146. package/dist/utils/sieve-builder.d.ts +41 -0
  147. package/dist/utils/sieve-builder.js +66 -0
  148. package/package.json +1 -1
@@ -7,14 +7,25 @@ import { Pagination, ListingToolbar } from "../../../ui";
7
7
  import { ReviewCard } from "./ReviewsList";
8
8
  import { ReviewFilters, REVIEW_PUBLIC_SORT_OPTIONS } from "./ReviewFilters";
9
9
  import { useReviews } from "../hooks/useReviews";
10
+ import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
11
+ import { sortBy } from "../../../constants/sort";
12
+ import { REVIEW_FIELDS } from "../../../constants/field-names";
10
13
  const PAGE_SIZE = 12;
11
- const FILTER_KEYS = ["rating", "dateFrom", "dateTo", "minVotes", "maxVotes"];
14
+ const DEFAULT_SORT = sortBy(REVIEW_FIELDS.CREATED_AT);
15
+ const FILTER_KEYS = ["rating", TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO, "minVotes", "maxVotes"];
12
16
  export function ReviewsIndexListing({ initialData, variant = "public", }) {
13
- const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: "-createdAt" } });
14
- const [searchInput, setSearchInput] = useState(table.get("q") || "");
17
+ const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
18
+ const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
15
19
  const [filterOpen, setFilterOpen] = useState(false);
16
- const sort = table.get("sort") || "-createdAt";
17
- const currentPage = table.getNumber("page", 1);
20
+ const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
21
+ const handleViewToggle = (next) => {
22
+ if (next === VIEW_MODE.TABLE)
23
+ return;
24
+ setView(next);
25
+ table.set(TABLE_KEYS.VIEW, next);
26
+ };
27
+ const sort = table.get(TABLE_KEYS.SORT) || DEFAULT_SORT;
28
+ const currentPage = table.getNumber(TABLE_KEYS.PAGE, 1);
18
29
  // Pending filter state — buffered until "Apply Filters" clicked
19
30
  const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
20
31
  const pendingTable = useMemo(() => ({
@@ -57,24 +68,24 @@ export function ReviewsIndexListing({ initialData, variant = "public", }) {
57
68
  setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
58
69
  }, []);
59
70
  const resetAll = useCallback(() => {
60
- const updates = { q: "", sort: "" };
71
+ const updates = { [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" };
61
72
  for (const k of FILTER_KEYS)
62
73
  updates[k] = "";
63
74
  table.setMany(updates);
64
75
  setSearchInput("");
65
76
  }, [table]);
66
77
  const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
67
- const hasActiveState = !!table.get("q") ||
68
- table.get("sort") !== "-createdAt" ||
78
+ const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
79
+ table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
69
80
  activeFilterCount > 0;
70
81
  const commitSearch = useCallback(() => {
71
- table.set("q", searchInput.trim());
82
+ table.set(TABLE_KEYS.QUERY, searchInput.trim());
72
83
  }, [searchInput, table]);
73
84
  const { reviews, total, totalPages, isLoading } = useReviews({
74
- q: table.get("q") || undefined,
75
- rating: table.get("rating") || undefined,
76
- dateFrom: table.get("dateFrom") || undefined,
77
- dateTo: table.get("dateTo") || undefined,
85
+ q: table.get(TABLE_KEYS.QUERY) || undefined,
86
+ rating: table.get(TABLE_KEYS.RATING) || undefined,
87
+ dateFrom: table.get(TABLE_KEYS.DATE_FROM) || undefined,
88
+ dateTo: table.get(TABLE_KEYS.DATE_TO) || undefined,
78
89
  minVotes: table.get("minVotes") ? Number(table.get("minVotes")) : undefined,
79
90
  maxVotes: table.get("maxVotes") ? Number(table.get("maxVotes")) : undefined,
80
91
  sort,
@@ -85,5 +96,5 @@ export function ReviewsIndexListing({ initialData, variant = "public", }) {
85
96
  value: opt.value,
86
97
  label: opt.key,
87
98
  }));
88
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: 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) => (_jsx("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: _jsxs("div", { className: "p-4 space-y-3", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] }) }, i))) })) : reviews.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No reviews found." })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ReviewFilters, { table: pendingTable, variant: variant }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
99
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: 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) => (_jsx("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: _jsxs("div", { className: "p-4 space-y-3", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] }) }, i))) })) : reviews.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No reviews found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ReviewFilters, { table: pendingTable, variant: variant }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
89
100
  }
@@ -1,15 +1,18 @@
1
1
  import type { Review } from "../types";
2
+ export type ReviewCardContext = "listing" | "store" | "general";
2
3
  export interface ReviewCardProps {
3
4
  review: Review;
5
+ context?: ReviewCardContext;
4
6
  className?: string;
5
7
  }
6
- export declare function ReviewCard({ review, className }: ReviewCardProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function ReviewCard({ review, context, className }: ReviewCardProps): import("react/jsx-runtime").JSX.Element;
7
9
  export interface ReviewsListProps {
8
10
  reviews: Review[];
11
+ context?: ReviewCardContext;
9
12
  isLoading?: boolean;
10
13
  totalPages?: number;
11
14
  currentPage?: number;
12
15
  onPageChange?: (page: number) => void;
13
16
  emptyLabel?: string;
14
17
  }
15
- export declare function ReviewsList({ reviews, isLoading, totalPages, currentPage, onPageChange, emptyLabel, }: ReviewsListProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function ReviewsList({ reviews, context, isLoading, totalPages, currentPage, onPageChange, emptyLabel, }: ReviewsListProps): import("react/jsx-runtime").JSX.Element;
@@ -7,7 +7,7 @@ import { maskName } from "../../../security";
7
7
  import { getDefaultLocale } from "../../../core/baseline-resolver";
8
8
  import { normalizeRichTextHtml } from "../../../utils/string.formatter";
9
9
  import { ROUTES } from "../../../next";
10
- export function ReviewCard({ review, className = "" }) {
10
+ export function ReviewCard({ review, context = "general", className = "" }) {
11
11
  const date = review.createdAt
12
12
  ? new Date(review.createdAt).toLocaleDateString(getDefaultLocale(), {
13
13
  year: "numeric",
@@ -21,15 +21,21 @@ export function ReviewCard({ review, className = "" }) {
21
21
  const productHref = review.productId
22
22
  ? String(ROUTES.PUBLIC.PRODUCT_DETAIL(review.productId))
23
23
  : null;
24
- const hasFooter = !!(review.storeSlug && review.storeName) || !!productHref;
25
- return (_jsxs(Div, { className: `group flex flex-col h-full rounded-xl border border-neutral-200 bg-white p-5 dark:border-zinc-700 dark:bg-zinc-900 transition-shadow hover:shadow-md ${className}`, children: [_jsxs(Link, { href: reviewHref, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Div, { className: "flex items-start gap-3", children: [review.userAvatar ? (_jsx(Div, { role: "img", "aria-label": displayName, className: "h-9 w-9 flex-shrink-0 rounded-full bg-center bg-cover", style: { backgroundImage: `url(${review.userAvatar})` } })) : (_jsx(Div, { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-neutral-200 text-sm font-medium text-neutral-600 dark:bg-zinc-700 dark:text-zinc-300", children: initials })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Span, { className: "font-medium text-neutral-900 dark:text-white", children: displayName }), review.verified && (_jsx(Span, { className: "rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-medium text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400", children: "Verified" })), date && (_jsx(Span, { className: "text-xs text-neutral-400 dark:text-zinc-500", children: date }))] }), _jsx(Div, { className: "mt-1", children: _jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }) })] })] }), review.title && (_jsx(Heading, { level: 4, className: "mt-3 font-semibold text-neutral-900 dark:text-white", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "mt-2 text-sm leading-relaxed text-neutral-600 dark:text-zinc-400" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mt-3", children: review.images.map((img, i) => (_jsx(Div, { role: "img", "aria-label": `Review image ${i + 1}`, className: "h-16 w-16 rounded-lg bg-center bg-cover border border-neutral-100 dark:border-zinc-700", style: { backgroundImage: `url(${img.thumbnailUrl ?? img.url})` } }, i))) })), (review.helpfulCount ?? 0) > 0 && (_jsxs(Text, { className: "mt-3 text-xs text-neutral-400 dark:text-zinc-500", children: [review.helpfulCount, " found this helpful"] }))] }), hasFooter && (_jsxs(Div, { className: "mt-3 pt-3 border-t border-neutral-100 dark:border-zinc-800 flex flex-col gap-1.5", children: [review.storeSlug && review.storeName && (_jsxs(Link, { href: String(ROUTES.PUBLIC.STORE_DETAIL(review.storeSlug)), className: "flex items-center gap-1.5 text-xs font-medium text-primary hover:underline", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83C\uDFEA" }), _jsx("span", { className: THEME_CONSTANTS.utilities.textClamp1, children: review.storeName })] })), productHref && (_jsxs(Link, { href: productHref, className: "flex items-center gap-1.5 text-xs font-medium text-neutral-500 dark:text-zinc-400 hover:text-neutral-700 dark:hover:text-zinc-200", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83D\uDCE6" }), _jsx("span", { className: THEME_CONSTANTS.utilities.textClamp1, children: review.productTitle ?? "View Product" }), _jsx("span", { "aria-hidden": "true", className: "ml-auto text-primary group-hover:translate-x-0.5 transition-transform", children: "\u2192" })] }))] }))] }));
24
+ const profileHref = !review.isAnonymous && review.userId
25
+ ? String(ROUTES.PUBLIC.PROFILE(review.userId))
26
+ : null;
27
+ const showStoreLink = context !== "store" && !!(review.storeSlug && review.storeName);
28
+ const showProductLink = context !== "listing" && !!productHref;
29
+ const showProfileLink = !!profileHref;
30
+ const hasFooter = showStoreLink || showProductLink || showProfileLink;
31
+ return (_jsxs(Div, { className: `group flex flex-col h-full rounded-xl border border-neutral-200 bg-white p-5 dark:border-zinc-700 dark:bg-zinc-900 transition-shadow hover:shadow-md ${className}`, children: [_jsxs(Link, { href: reviewHref, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Div, { className: "flex items-start gap-3", children: [review.userAvatar ? (_jsx(Div, { role: "img", "aria-label": displayName, className: "h-9 w-9 flex-shrink-0 rounded-full bg-center bg-cover", style: { backgroundImage: `url(${review.userAvatar})` } })) : (_jsx(Div, { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-neutral-200 text-sm font-medium text-neutral-600 dark:bg-zinc-700 dark:text-zinc-300", children: initials })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Span, { className: "font-medium text-neutral-900 dark:text-white", children: displayName }), review.verified && (_jsx(Span, { className: "rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-medium text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400", children: "Verified" })), date && (_jsx(Span, { className: "text-xs text-neutral-400 dark:text-zinc-500", children: date }))] }), _jsx(Div, { className: "mt-1", children: _jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }) })] })] }), review.title && (_jsx(Heading, { level: 4, className: "mt-3 font-semibold text-neutral-900 dark:text-white", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "mt-2 text-sm leading-relaxed text-neutral-600 dark:text-zinc-400" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mt-3", children: review.images.map((img, i) => (_jsx(Div, { role: "img", "aria-label": `Review image ${i + 1}`, className: "h-16 w-16 rounded-lg bg-center bg-cover border border-neutral-100 dark:border-zinc-700", style: { backgroundImage: `url(${img.thumbnailUrl ?? img.url})` } }, i))) })), (review.helpfulCount ?? 0) > 0 && (_jsxs(Text, { className: "mt-3 text-xs text-neutral-400 dark:text-zinc-500", children: [review.helpfulCount, " found this helpful"] }))] }), hasFooter && (_jsxs(Div, { className: "mt-3 pt-3 border-t border-neutral-100 dark:border-zinc-800 flex flex-col gap-1.5", children: [showStoreLink && (_jsxs(Link, { href: String(ROUTES.PUBLIC.STORE_DETAIL(review.storeSlug)), className: "flex items-center gap-1.5 text-xs font-medium text-primary hover:underline", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83C\uDFEA" }), _jsx("span", { className: THEME_CONSTANTS.utilities.textClamp1, children: review.storeName })] })), showProductLink && (_jsxs(Link, { href: productHref, className: "flex items-center gap-1.5 text-xs font-medium text-neutral-500 dark:text-zinc-400 hover:text-neutral-700 dark:hover:text-zinc-200", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83D\uDCE6" }), _jsx("span", { className: THEME_CONSTANTS.utilities.textClamp1, children: review.productTitle ?? "View Product" }), _jsx("span", { "aria-hidden": "true", className: "ml-auto text-primary group-hover:translate-x-0.5 transition-transform", children: "\u2192" })] })), showProfileLink && (_jsxs(Link, { href: profileHref, className: "flex items-center gap-1.5 text-xs font-medium text-neutral-500 dark:text-zinc-400 hover:text-neutral-700 dark:hover:text-zinc-200", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83D\uDC64" }), _jsx("span", { className: THEME_CONSTANTS.utilities.textClamp1, children: displayName })] }))] }))] }));
26
32
  }
27
- export function ReviewsList({ reviews, isLoading, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No reviews yet", }) {
33
+ export function ReviewsList({ reviews, context = "general", isLoading, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No reviews yet", }) {
28
34
  if (isLoading) {
29
35
  return (_jsx(Stack, { gap: "md", children: Array.from({ length: 4 }).map((_, i) => (_jsxs(Div, { className: "rounded-xl border border-neutral-200 dark:border-zinc-700 p-5", children: [_jsxs(Div, { className: "flex gap-3", children: [_jsx(Skeleton, { className: "h-9 w-9 rounded-full" }), _jsxs(Stack, { gap: "sm", className: "flex-1", children: [_jsx(Skeleton, { className: "h-4 w-24 rounded" }), _jsx(Skeleton, { className: "h-3 w-16 rounded" })] })] }), _jsxs(Stack, { gap: "xs", className: "mt-3", children: [_jsx(Skeleton, { className: "h-3 w-full rounded" }), _jsx(Skeleton, { className: "h-3 w-3/4 rounded" })] })] }, i))) }));
30
36
  }
31
37
  if (reviews.length === 0) {
32
38
  return (_jsx(Text, { className: "py-12 text-center text-sm text-neutral-500 dark:text-zinc-500", children: emptyLabel }));
33
39
  }
34
- return (_jsxs(Stack, { gap: "lg", children: [_jsx(Stack, { gap: "md", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) }), totalPages > 1 && onPageChange && (_jsx(Div, { className: "flex justify-center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: onPageChange }) }))] }));
40
+ return (_jsxs(Stack, { gap: "lg", children: [_jsx(Stack, { gap: "md", children: reviews.map((review) => (_jsx(ReviewCard, { review: review, context: context }, review.id))) }), totalPages > 1 && onPageChange && (_jsx(Div, { className: "flex justify-center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: onPageChange }) }))] }));
35
41
  }
@@ -37,6 +37,7 @@ export interface ReviewDocument {
37
37
  reportCount: number;
38
38
  verified: boolean;
39
39
  featured?: boolean;
40
+ isAnonymous?: boolean;
40
41
  sellerReply?: string;
41
42
  sellerRepliedAt?: Date;
42
43
  createdAt: Date;
@@ -109,18 +109,18 @@ export declare const reviewSchema: z.ZodObject<{
109
109
  duration?: number | undefined;
110
110
  } | undefined;
111
111
  productTitle?: string | undefined;
112
+ featured?: boolean | undefined;
113
+ storeName?: string | undefined;
112
114
  images?: {
113
115
  url: string;
114
116
  thumbnailUrl?: string | undefined;
115
117
  }[] | undefined;
116
- storeName?: string | undefined;
117
- featured?: boolean | undefined;
118
118
  storeSlug?: string | undefined;
119
- verified?: boolean | undefined;
120
119
  userAvatar?: string | undefined;
121
120
  comment?: string | undefined;
122
121
  helpfulCount?: number | undefined;
123
122
  reportCount?: number | undefined;
123
+ verified?: boolean | undefined;
124
124
  }, {
125
125
  id: string;
126
126
  status: "pending" | "approved" | "rejected";
@@ -139,18 +139,18 @@ export declare const reviewSchema: z.ZodObject<{
139
139
  duration?: number | undefined;
140
140
  } | undefined;
141
141
  productTitle?: string | undefined;
142
+ featured?: boolean | undefined;
143
+ storeName?: string | undefined;
142
144
  images?: {
143
145
  url: string;
144
146
  thumbnailUrl?: string | undefined;
145
147
  }[] | undefined;
146
- storeName?: string | undefined;
147
- featured?: boolean | undefined;
148
148
  storeSlug?: string | undefined;
149
- verified?: boolean | undefined;
150
149
  userAvatar?: string | undefined;
151
150
  comment?: string | undefined;
152
151
  helpfulCount?: number | undefined;
153
152
  reportCount?: number | undefined;
153
+ verified?: boolean | undefined;
154
154
  }>;
155
155
  /** Base schema for list-query parameters. */
156
156
  export declare const reviewListParamsSchema: z.ZodObject<{
@@ -170,9 +170,9 @@ export declare const reviewListParamsSchema: z.ZodObject<{
170
170
  status?: "pending" | "approved" | "rejected" | undefined;
171
171
  page?: number | undefined;
172
172
  productId?: string | undefined;
173
+ featured?: boolean | undefined;
173
174
  rating?: number | undefined;
174
175
  userId?: string | undefined;
175
- featured?: boolean | undefined;
176
176
  }, {
177
177
  sort?: string | undefined;
178
178
  perPage?: number | undefined;
@@ -180,7 +180,7 @@ export declare const reviewListParamsSchema: z.ZodObject<{
180
180
  status?: "pending" | "approved" | "rejected" | undefined;
181
181
  page?: number | undefined;
182
182
  productId?: string | undefined;
183
+ featured?: boolean | undefined;
183
184
  rating?: number | undefined;
184
185
  userId?: string | undefined;
185
- featured?: boolean | undefined;
186
186
  }>;
@@ -29,6 +29,7 @@ export interface Review {
29
29
  reportCount?: number;
30
30
  verified?: boolean;
31
31
  featured?: boolean;
32
+ isAnonymous?: boolean;
32
33
  createdAt?: string;
33
34
  updatedAt?: string;
34
35
  }
@@ -27,8 +27,8 @@ export declare const searchProductItemSchema: z.ZodObject<{
27
27
  currency?: string | undefined;
28
28
  status?: string | undefined;
29
29
  price?: number | undefined;
30
- listingType?: "auction" | "standard" | "pre-order" | "prize-draw" | "bundle" | undefined;
31
30
  featured?: boolean | undefined;
31
+ listingType?: "auction" | "standard" | "pre-order" | "prize-draw" | "bundle" | undefined;
32
32
  isPromoted?: boolean | undefined;
33
33
  mainImage?: string | undefined;
34
34
  }, {
@@ -38,8 +38,8 @@ export declare const searchProductItemSchema: z.ZodObject<{
38
38
  currency?: string | undefined;
39
39
  status?: string | undefined;
40
40
  price?: number | undefined;
41
- listingType?: "auction" | "standard" | "pre-order" | "prize-draw" | "bundle" | undefined;
42
41
  featured?: boolean | undefined;
42
+ listingType?: "auction" | "standard" | "pre-order" | "prize-draw" | "bundle" | undefined;
43
43
  isPromoted?: boolean | undefined;
44
44
  mainImage?: string | undefined;
45
45
  }>;
@@ -3,6 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import React, { useState, useCallback } from "react";
4
4
  import { X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
+ import { AdminViewCards } from "../../admin/components/AdminViewCards";
6
8
  import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
7
9
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
10
  import { SELLER_AUCTION_STATUS_TABS } from "../../admin/constants/filter-tabs";
@@ -20,6 +22,7 @@ const SORT_OPTIONS = [
20
22
  const STATUS_OPTIONS = SELLER_AUCTION_STATUS_TABS;
21
23
  export function SellerAuctionsView({ renderHeader, children, ...props }) {
22
24
  const hasChildren = React.Children.count(children) > 0;
25
+ const [view, setView] = useState("table");
23
26
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
24
27
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
25
28
  const [filterOpen, setFilterOpen] = useState(false);
@@ -74,8 +77,9 @@ export function SellerAuctionsView({ renderHeader, children, ...props }) {
74
77
  });
75
78
  const currentPage = table.getNumber("page", 1);
76
79
  const totalPages = Math.ceil(total / PAGE_SIZE);
80
+ const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
77
81
  if (hasChildren) {
78
82
  return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
79
83
  }
80
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions by product name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No auctions found" })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
84
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions by product name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No auctions found" })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No auctions found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
81
85
  }
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useState, useCallback } from "react";
4
4
  import { X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
6
7
  import { Badge, Div, FilterChipGroup, ListingToolbar, Pagination } from "../../../ui";
7
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
9
  import { SELLER_BID_STATUS_TABS } from "../../admin/constants/filter-tabs";
@@ -34,6 +35,7 @@ const STATUS_BADGE = {
34
35
  // ---------------------------------------------------------------------------
35
36
  export function SellerBidsView({ endpoint = SELLER_ENDPOINTS.BIDS }) {
36
37
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
38
+ const [view, setView] = useState("table");
37
39
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
38
40
  const [filterOpen, setFilterOpen] = useState(false);
39
41
  const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
@@ -119,5 +121,6 @@ export function SellerBidsView({ endpoint = SELLER_ENDPOINTS.BIDS }) {
119
121
  render: (row) => _jsx("span", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: row.bidDate }),
120
122
  },
121
123
  ];
122
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by bidder name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No bids found for your auctions" })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Bid Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })), allId: "" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
124
+ const selection = useBulkSelection({ items: rows ?? [], keyExtractor: (r) => r.id });
125
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by bidder name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No bids found for your auctions" })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Bid Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })), allId: "" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
123
126
  }
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import React, { useState, useCallback } from "react";
4
4
  import { X, Plus } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
6
7
  import { Button, Div, ListingToolbar, Pagination, ListingViewShell, Text } from "../../../ui";
7
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
9
  import { toRecordArray, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
@@ -30,6 +31,7 @@ function getIsActive(item) {
30
31
  // ---------------------------------------------------------------------------
31
32
  export function SellerCouponsView({ onCreateClick, onEditClick, onToggle, onDelete, children, ...props }) {
32
33
  const hasChildren = React.Children.count(children) > 0;
34
+ const [view, setView] = useState("table");
33
35
  const [togglingId, setTogglingId] = useState(null);
34
36
  const [deletingId, setDeletingId] = useState(null);
35
37
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
@@ -104,10 +106,11 @@ export function SellerCouponsView({ onCreateClick, onEditClick, onToggle, onDele
104
106
  setDeletingId(null);
105
107
  }
106
108
  }, [onDelete, refetch]);
109
+ const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
107
110
  if (hasChildren) {
108
111
  return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
109
112
  }
110
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by coupon code", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState, extra: onCreateClick
113
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by coupon code", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: onCreateClick
111
114
  ? (_jsxs(Button, { size: "sm", onClick: onCreateClick, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "Add Coupon" })] }))
112
115
  : undefined }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), isLoading ? (_jsx(Div, { className: "fluid-grid-card gap-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Div, { className: "rounded-xl border-2 border-zinc-100 dark:border-slate-700 p-4 animate-pulse space-y-3", children: [_jsx(Div, { className: "h-6 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx(Div, { className: "h-4 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))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No coupons found \u2014 create your first coupon" }) })) : (_jsx(Div, { className: "fluid-grid-card gap-3", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: onEditClick ? handleEdit : undefined, onToggleActive: onToggle ? handleToggle : undefined, onDelete: onDelete ? handleDelete : undefined, className: togglingId === row.id || deletingId === row.id ? "pointer-events-none opacity-60" : undefined }, row.id))) }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
113
116
  ? "bg-[var(--appkit-color-primary)] text-white border-[var(--appkit-color-primary)]"
@@ -3,6 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import React, { useState, useCallback } from "react";
4
4
  import { X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
+ import { AdminViewCards } from "../../admin/components/AdminViewCards";
6
8
  import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
7
9
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
10
  import { SELLER_OFFER_STATUS_TABS } from "../../admin/constants/filter-tabs";
@@ -18,6 +20,7 @@ const SORT_OPTIONS = [
18
20
  const STATUS_OPTIONS = SELLER_OFFER_STATUS_TABS;
19
21
  export function SellerOffersView({ children, ...props }) {
20
22
  const hasChildren = React.Children.count(children) > 0;
23
+ const [view, setView] = useState("table");
21
24
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
22
25
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
23
26
  const [filterOpen, setFilterOpen] = useState(false);
@@ -72,8 +75,9 @@ export function SellerOffersView({ children, ...props }) {
72
75
  });
73
76
  const currentPage = table.getNumber("page", 1);
74
77
  const totalPages = Math.ceil(total / PAGE_SIZE);
78
+ const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
75
79
  if (hasChildren) {
76
80
  return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
77
81
  }
78
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search offers by product or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No offers received" })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
82
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search offers by product or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No offers received" })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No offers received", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
79
83
  }
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import React, { useState, useCallback } from "react";
4
4
  import { X, Eye } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
6
7
  import { Badge, Button, Div, FilterChipGroup, Heading, Input, ListingToolbar, Pagination, ListingViewShell, Select, SideDrawer, Stack, Text } from "../../../ui";
7
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
9
  import { SELLER_ORDER_STATUS_TABS } from "../../admin/constants/filter-tabs";
@@ -108,6 +109,7 @@ function OrderDetailDrawer({ orderId, apiBase, onClose, }) {
108
109
  // ---------------------------------------------------------------------------
109
110
  export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS, children, ...props }) {
110
111
  const hasChildren = React.Children.count(children) > 0;
112
+ const [view, setView] = useState("table");
111
113
  const [selectedOrderId, setSelectedOrderId] = useState(null);
112
114
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
113
115
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
@@ -190,8 +192,9 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
190
192
  },
191
193
  ];
192
194
  const renderRowActions = useCallback((row) => (_jsx("button", { type: "button", onClick: () => setSelectedOrderId(row.id), title: "View order details", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 hover:text-zinc-700 dark:hover:text-zinc-300 transition-colors", children: _jsx(Eye, { className: "h-4 w-4" }) })), []);
195
+ const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
193
196
  if (hasChildren) {
194
197
  return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
195
198
  }
196
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by order ID or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No orders yet", renderRowActions: renderRowActions })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), selectedOrderId && (_jsx(OrderDetailDrawer, { orderId: selectedOrderId, apiBase: orderDetailApiBase, onClose: () => setSelectedOrderId(null) }))] }));
199
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by order ID or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No orders yet", renderRowActions: renderRowActions })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), selectedOrderId && (_jsx(OrderDetailDrawer, { orderId: selectedOrderId, apiBase: orderDetailApiBase, onClose: () => setSelectedOrderId(null) }))] }));
197
200
  }
@@ -3,6 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import React, { useState, useCallback } from "react";
4
4
  import { X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
+ import { AdminViewCards } from "../../admin/components/AdminViewCards";
6
8
  import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
7
9
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
10
  import { ADMIN_PAYOUT_STATUS_TABS } from "../../admin/constants/filter-tabs";
@@ -18,6 +20,7 @@ const SORT_OPTIONS = [
18
20
  const STATUS_OPTIONS = ADMIN_PAYOUT_STATUS_TABS;
19
21
  export function SellerPayoutsView({ children, ...props }) {
20
22
  const hasChildren = React.Children.count(children) > 0;
23
+ const [view, setView] = useState("table");
21
24
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
22
25
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
23
26
  const [filterOpen, setFilterOpen] = useState(false);
@@ -72,8 +75,9 @@ export function SellerPayoutsView({ children, ...props }) {
72
75
  });
73
76
  const currentPage = table.getNumber("page", 1);
74
77
  const totalPages = Math.ceil(total / PAGE_SIZE);
78
+ const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
75
79
  if (hasChildren) {
76
80
  return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
77
81
  }
78
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search payouts by payout # or amount", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No payouts found" })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
82
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search payouts by payout # or amount", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No payouts found" })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No payouts found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
79
83
  }
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import React, { useState, useCallback } from "react";
4
4
  import { Pencil, X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
6
7
  import { Alert, Badge, Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
7
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
9
  import { SELLER_PRE_ORDER_STATUS_TABS } from "../../admin/constants/filter-tabs";
@@ -70,6 +71,7 @@ const PRE_ORDER_COLUMNS = [
70
71
  ];
71
72
  export function SellerPreOrdersView({ children, ...props }) {
72
73
  const hasChildren = React.Children.count(children) > 0;
74
+ const [view, setView] = useState("table");
73
75
  const dispatch = useActionDispatch();
74
76
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
75
77
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
@@ -131,10 +133,11 @@ export function SellerPreOrdersView({ children, ...props }) {
131
133
  });
132
134
  const currentPage = table.getNumber("page", 1);
133
135
  const totalPages = Math.ceil(total / PAGE_SIZE);
136
+ const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
134
137
  if (hasChildren) {
135
138
  return (_jsx(ListingViewShell, { portal: "seller", ...props, children: children }));
136
139
  }
137
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders by name\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky z-10 flex justify-center bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-1.5", style: { top: "calc(var(--header-height, 0px) + 44px)" }, children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), _jsx(DataTable, { columns: PRE_ORDER_COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: "No pre-orders listed yet", getRowHref: (row) => String(ROUTES.STORE.PRE_ORDERS_EDIT(row.id)), renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => {
140
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders by name\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky z-10 flex justify-center bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-1.5", style: { top: "calc(var(--header-height, 0px) + 44px)" }, children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), _jsx(DataTable, { columns: PRE_ORDER_COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: "No pre-orders listed yet", getRowHref: (row) => String(ROUTES.STORE.PRE_ORDERS_EDIT(row.id)), renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => {
138
141
  e.stopPropagation();
139
142
  void dispatch({ type: "NAVIGATE", href: String(ROUTES.STORE.PRE_ORDERS_EDIT(row.id)) });
140
143
  }, "aria-label": "Edit", children: _jsx(Pencil, { className: "w-4 h-4" }) })) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-[var(--appkit-color-surface)] shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-error)] transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-[var(--appkit-color-border)] px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white hover:opacity-90 transition-opacity active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import React, { useState, useCallback } from "react";
4
4
  import { Pencil, X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
6
7
  import { Alert, Badge, Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
7
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
8
9
  import { SELLER_PRIZE_DRAW_STATUS_TABS } from "../../admin/constants/filter-tabs";
@@ -67,6 +68,7 @@ const PRIZE_DRAW_COLUMNS = [
67
68
  ];
68
69
  export function SellerPrizeDrawsView({ children, ...props }) {
69
70
  const hasChildren = React.Children.count(children) > 0;
71
+ const [view, setView] = useState("table");
70
72
  const dispatch = useActionDispatch();
71
73
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
72
74
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
@@ -128,10 +130,11 @@ export function SellerPrizeDrawsView({ children, ...props }) {
128
130
  });
129
131
  const currentPage = table.getNumber("page", 1);
130
132
  const totalPages = Math.ceil(total / PAGE_SIZE);
133
+ const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
131
134
  if (hasChildren) {
132
135
  return (_jsx(ListingViewShell, { portal: "seller", ...props, children: children }));
133
136
  }
134
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search prize draws by name\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky z-10 flex justify-center bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-1.5", style: { top: "calc(var(--header-height, 0px) + 44px)" }, children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), _jsx(DataTable, { columns: PRIZE_DRAW_COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: "No prize draws listed yet", getRowHref: (row) => String(ROUTES.STORE.PRIZE_DRAWS_EDIT(row.id)), renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => {
137
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search prize draws by name\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky z-10 flex justify-center bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-1.5", style: { top: "calc(var(--header-height, 0px) + 44px)" }, children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), _jsx(DataTable, { columns: PRIZE_DRAW_COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: "No prize draws listed yet", getRowHref: (row) => String(ROUTES.STORE.PRIZE_DRAWS_EDIT(row.id)), renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => {
135
138
  e.stopPropagation();
136
139
  void dispatch({ type: "NAVIGATE", href: String(ROUTES.STORE.PRIZE_DRAWS_EDIT(row.id)) });
137
140
  }, "aria-label": "Edit", children: _jsx(Pencil, { className: "w-4 h-4" }) })) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-[var(--appkit-color-surface)] shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-error)] transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-[var(--appkit-color-border)] px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white hover:opacity-90 transition-opacity active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));