@mohasinac/appkit 2.7.17 → 2.7.18

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 (88) hide show
  1. package/dist/_internal/server/features/bundles/actions.d.ts +15 -0
  2. package/dist/_internal/server/features/bundles/actions.js +48 -0
  3. package/dist/_internal/server/features/bundles/index.d.ts +1 -0
  4. package/dist/_internal/server/features/bundles/index.js +1 -0
  5. package/dist/_internal/server/features/checkout/actions.d.ts +4 -0
  6. package/dist/_internal/server/features/checkout/actions.js +23 -12
  7. package/dist/_internal/server/features/payments/data.d.ts +5 -3
  8. package/dist/_internal/server/features/payments/data.js +13 -8
  9. package/dist/_internal/server/features/payments/index.d.ts +1 -1
  10. package/dist/_internal/server/features/payments/index.js +1 -1
  11. package/dist/_internal/shared/features/categories/bundle-config.d.ts +24 -0
  12. package/dist/_internal/shared/features/categories/bundle-config.js +28 -0
  13. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +7 -0
  14. package/dist/_internal/shared/features/categories/bundle-copy.js +9 -0
  15. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +69 -19
  16. package/dist/_internal/shared/features/categories/bundle-schemas.js +20 -2
  17. package/dist/_internal/shared/features/checkout/config.d.ts +1 -1
  18. package/dist/_internal/shared/features/checkout/config.js +1 -1
  19. package/dist/_internal/shared/features/payments/config.d.ts +2 -1
  20. package/dist/_internal/shared/features/payments/config.js +2 -1
  21. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  22. package/dist/constants/field-names.d.ts +8 -1
  23. package/dist/constants/field-names.js +9 -2
  24. package/dist/constants/table-keys.d.ts +3 -0
  25. package/dist/constants/table-keys.js +3 -0
  26. package/dist/features/admin/components/AdminSiteSettingsView.js +17 -15
  27. package/dist/features/admin/components/DashboardStats.js +20 -12
  28. package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +18 -2
  29. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +7 -1
  30. package/dist/features/admin/components/analytics/AdminStatCard.js +13 -2
  31. package/dist/features/admin/schemas/firestore.d.ts +18 -4
  32. package/dist/features/admin/schemas/firestore.js +11 -1
  33. package/dist/features/categories/components/BundleBuyNowCta.d.ts +3 -1
  34. package/dist/features/categories/components/BundleBuyNowCta.js +3 -3
  35. package/dist/features/categories/components/BundleDynamicRuleEditor.d.ts +1 -1
  36. package/dist/features/categories/components/BundleDynamicRuleEditor.js +2 -1
  37. package/dist/features/categories/components/BundlesListView.d.ts +4 -1
  38. package/dist/features/categories/components/BundlesListView.js +2 -2
  39. package/dist/features/categories/components/CategoryBundlesListing.d.ts +4 -1
  40. package/dist/features/categories/components/CategoryBundlesListing.js +9 -8
  41. package/dist/features/categories/schemas/firestore.d.ts +29 -1
  42. package/dist/features/checkout/actions/checkout-actions.d.ts +11 -0
  43. package/dist/features/checkout/actions/checkout-actions.js +26 -0
  44. package/dist/features/filters/FilterFacetSection.d.ts +6 -1
  45. package/dist/features/filters/FilterFacetSection.js +25 -2
  46. package/dist/features/homepage/components/FeaturedBundlesSection.d.ts +4 -1
  47. package/dist/features/homepage/components/FeaturedBundlesSection.js +9 -8
  48. package/dist/features/orders/actions/refund-actions.js +10 -7
  49. package/dist/features/orders/schemas/firestore.d.ts +16 -13
  50. package/dist/features/orders/schemas/firestore.js +6 -5
  51. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
  52. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -1
  53. package/dist/features/products/components/AuctionsIndexListing.js +2 -1
  54. package/dist/features/products/components/ProductDetailPageView.js +3 -1
  55. package/dist/features/products/components/ProductDetailView.d.ts +3 -1
  56. package/dist/features/products/components/ProductDetailView.js +2 -2
  57. package/dist/features/products/components/ProductFilters.d.ts +10 -4
  58. package/dist/features/products/components/ProductFilters.js +20 -16
  59. package/dist/features/products/components/ProductGrid.js +2 -2
  60. package/dist/features/products/components/ProductsIndexListing.js +2 -1
  61. package/dist/features/products/constants/product-features.constants.js +5 -1
  62. package/dist/features/products/schemas/index.d.ts +2 -2
  63. package/dist/features/products/schemas/product-features.d.ts +1 -1
  64. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  65. package/dist/features/products/schemas/product-features.validators.js +4 -0
  66. package/dist/features/seller/components/SellerCouponEditorView.js +2 -2
  67. package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +10 -4
  68. package/dist/features/stores/components/StoreBundlesPageView.d.ts +4 -1
  69. package/dist/features/stores/components/StoreBundlesPageView.js +2 -2
  70. package/dist/features/stores/components/StoresIndexListing.js +2 -1
  71. package/dist/providers/db-firebase/admin-app-lite.js +8 -2
  72. package/dist/providers/db-firebase/admin.js +8 -2
  73. package/dist/seed/addresses-seed-data.js +24 -7
  74. package/dist/seed/cart-seed-data.d.ts +10 -10
  75. package/dist/seed/cart-seed-data.js +39 -2
  76. package/dist/seed/grouped-listings-seed-data.js +47 -0
  77. package/dist/seed/orders-seed-data.js +150 -0
  78. package/dist/seed/products-standard-seed-data.js +42 -0
  79. package/dist/seed/site-settings-seed-data.js +11 -4
  80. package/dist/seed/users-seed-data.js +1 -1
  81. package/dist/seed/wishlists-seed-data.js +7 -0
  82. package/dist/server-entry.d.ts +2 -1
  83. package/dist/server-entry.js +2 -1
  84. package/dist/server.d.ts +2 -1
  85. package/dist/server.js +3 -1
  86. package/dist/tailwind-utilities.css +1 -1
  87. package/dist/ui/components/SideDrawer.style.css +49 -11
  88. package/package.json +1 -1
@@ -2,12 +2,12 @@
2
2
  * Orders Firestore Document Types & Constants
3
3
  */
4
4
  import { generateOrderId } from "../../../utils/id-generators";
5
- /** Runtime-accessible shipping method values — use instead of bare string literals. */
5
+ /** Runtime-accessible shipping method values â€" use instead of bare string literals. */
6
6
  export const ShippingMethodValues = {
7
7
  CUSTOM: "custom",
8
8
  SHIPROCKET: "shiprocket",
9
9
  };
10
- /** Runtime-accessible order status values — use instead of bare string literals. */
10
+ /** Runtime-accessible order status values â€" use instead of bare string literals. */
11
11
  export const OrderStatusValues = {
12
12
  PENDING: "pending",
13
13
  CONFIRMED: "confirmed",
@@ -19,7 +19,7 @@ export const OrderStatusValues = {
19
19
  RETURN_REQUESTED: "return_requested",
20
20
  RETURNED: "returned",
21
21
  };
22
- /** Runtime-accessible payment status values — use instead of bare string literals. */
22
+ /** Runtime-accessible payment status values â€" use instead of bare string literals. */
23
23
  export const PaymentStatusValues = {
24
24
  PENDING: "pending",
25
25
  PROCESSING: "processing",
@@ -28,14 +28,15 @@ export const PaymentStatusValues = {
28
28
  REFUNDED: "refunded",
29
29
  PARTIAL_REFUND: "partial_refund",
30
30
  };
31
- /** Runtime-accessible payment method values — use instead of bare string literals. */
31
+ /** Runtime-accessible payment method values â€" use instead of bare string literals. */
32
32
  export const PaymentMethodValues = {
33
33
  COD: "cod",
34
34
  ONLINE: "online",
35
35
  UPI_MANUAL: "upi_manual",
36
36
  RAZORPAY: "razorpay",
37
+ ADMIN_BYPASS: "admin_bypass",
37
38
  };
38
- /** Runtime-accessible refund status values — use instead of bare string literals. */
39
+ /** Runtime-accessible refund status values â€" use instead of bare string literals. */
39
40
  export const RefundStatusValues = {
40
41
  PENDING: "pending",
41
42
  PROCESSING: "processing",
@@ -51,7 +51,7 @@ export function MarketplacePreorderCard({ product, className = "", variant = "gr
51
51
  // The hook rolls back optimistic state when the mutation fails.
52
52
  }
53
53
  }, [toggle]);
54
- return (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: detailHref, className: "absolute inset-0 block", children: _jsx(MediaImage, { src: product.mainImage, alt: product.title, size: "card", fallback: "Product image" }) }), _jsxs(Div, { className: "absolute right-2 top-2 flex flex-col items-end gap-1", children: [_jsx(Span, { className: "inline-flex items-center rounded-full bg-cobalt px-2 py-0.5 text-xs font-medium text-white", children: mergedLabels.preOrderBadge }), product.featured && (_jsx(Span, { className: "inline-flex items-center rounded-full bg-amber-500 px-2 py-0.5 text-xs font-medium text-white", children: mergedLabels.featuredBadge }))] }), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
54
+ return (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: detailHref, className: "absolute inset-0 block", children: _jsx(MediaImage, { src: product.mainImage, alt: product.title, size: "card", fallback: "Product image" }) }), _jsxs(Div, { className: "absolute right-2 top-2 flex flex-col items-end gap-1", children: [_jsx(Span, { className: "inline-flex items-center rounded-full bg-cobalt px-2 py-0.5 text-xs font-medium text-white", children: mergedLabels.preOrderBadge }), product.featured && (_jsx(Span, { className: "inline-flex items-center rounded-full bg-amber-500 px-2 py-0.5 text-xs font-medium text-white", children: mergedLabels.featuredBadge }))] }), _jsxs(Div, { className: "absolute left-2 top-2 flex flex-col gap-1 pointer-events-none", children: [product.partOfBundleIds && product.partOfBundleIds.length > 0 && (_jsx(Span, { className: "rounded-full bg-teal-600 px-2 py-0.5 text-xs font-bold text-white", title: product.partOfBundleTitles?.[0] ? `In bundle: ${product.partOfBundleTitles[0]}` : "In a bundle", children: "Bundled" })), product.groupId && (_jsx(Span, { className: "rounded-full bg-indigo-600 px-2 py-0.5 text-xs font-bold text-white", title: product.groupTitle ? `Part of set: ${product.groupTitle}` : "Part of a set", children: product.isGroupParent ? "Set Parent" : "In Set" })), product.sublistingCategoryId && (_jsx(Span, { className: "rounded-full bg-sky-600 px-2 py-0.5 text-xs font-bold text-white", title: "Has variants or sub-listings", children: "Has Variants" }))] }), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
55
55
  event.stopPropagation();
56
56
  onSelect(product.id, !isSelected);
57
57
  }, className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" }))] }), _jsx(BaseListingCard.Info, { variant: variant, children: variant === "list" ? (
@@ -92,6 +92,7 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
92
92
  setSearchInput("");
93
93
  }, [table]);
94
94
  const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
95
+ const pendingFilterCount = FILTER_KEYS.filter((k) => !!pendingFilters[k]).length;
95
96
  const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
96
97
  table.get(TABLE_KEYS.SHOW_CLOSED) === "true" ||
97
98
  table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
@@ -200,5 +201,5 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
200
201
  ] }), 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: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No pre-orders found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) : (_jsx("div", { className: gridClass, children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "preorder", onClose: () => {
201
202
  setCompareIds([]);
202
203
  selection.clearSelection();
203
- }, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== 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: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _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(PreOrderFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _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})` : ""] }) })] })] }))] }));
204
+ }, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== 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: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [pendingFilterCount > 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(PreOrderFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _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", pendingFilterCount > 0 ? ` (${pendingFilterCount})` : ""] }) })] })] }))] }));
204
205
  }
@@ -86,6 +86,7 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
86
86
  setSearchInput("");
87
87
  }, [table]);
88
88
  const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
89
+ const pendingFilterCount = FILTER_KEYS.filter((k) => !!pendingFilters[k]).length;
89
90
  const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
90
91
  table.get(TABLE_KEYS.SHOW_ENDED) === "true" ||
91
92
  table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
@@ -165,5 +166,5 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
165
166
  selection.clearSelection();
166
167
  },
167
168
  },
168
- ] }), 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: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-4" : gridClass, wishlistActions: wishlistActions })) }), 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: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _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(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _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})` : ""] }) })] })] }))] }));
169
+ ] }), 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: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-4" : gridClass, wishlistActions: wishlistActions })) }), 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: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [pendingFilterCount > 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(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _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", pendingFilterCount > 0 ? ` (${pendingFilterCount})` : ""] }) })] })] }))] }));
169
170
  }
@@ -263,5 +263,7 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
263
263
  id: s.id,
264
264
  label: s.title,
265
265
  content: _jsx(CustomSectionTabContent, { section: s }),
266
- })) })), renderRelated: () => relatedItems.length > 0 ? (_jsx(RelatedProductsCarousel, { items: relatedItems })) : null }), _jsxs(BuyBar, { children: [formattedPrice && (_jsx(Span, { className: "mr-auto text-sm font-bold text-zinc-900 dark:text-zinc-50", children: formattedPrice })), _jsx(Button, { variant: "secondary", size: "sm", className: "shrink-0", disabled: !inStock, children: "Add to Cart" }), _jsx(Button, { variant: "primary", size: "sm", className: "flex-1", disabled: !inStock, children: inStock ? "Buy Now" : "Out of Stock" })] })] })] }));
266
+ })) })), renderBundleSection: bundleMemberships.length > 0
267
+ ? () => (_jsxs("div", { className: "rounded-xl border border-teal-200 dark:border-teal-800/60 bg-teal-50/60 dark:bg-teal-900/20 p-5", children: [_jsxs("div", { className: "flex items-center gap-2 mb-3", children: [_jsx(Span, { className: "text-teal-600 dark:text-teal-400 text-base", "aria-hidden": "true", children: "\uD83D\uDCE6" }), _jsxs(Text, { className: "text-sm font-semibold text-teal-800 dark:text-teal-200", children: ["Part of ", bundleMemberships.length === 1 ? "a bundle" : `${bundleMemberships.length} bundles`] })] }), _jsx("div", { className: "flex flex-col gap-3", children: bundleMemberships.map((b) => (_jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border border-teal-200 dark:border-teal-800/50 bg-white dark:bg-teal-900/30 px-4 py-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Text, { className: "text-xs text-teal-600 dark:text-teal-400 font-medium uppercase tracking-wide mb-0.5", children: "Included in bundle" }), _jsx(Text, { className: "text-sm font-semibold text-zinc-800 dark:text-zinc-100 truncate", children: b.title })] }), _jsx(Link, { href: String(ROUTES.PUBLIC.BUNDLE_DETAIL(b.id)), className: "flex-shrink-0 rounded-lg bg-teal-600 hover:bg-teal-700 px-3 py-1.5 text-xs font-semibold text-white transition-colors", children: "View Bundle \u2192" })] }, b.id))) })] }))
268
+ : undefined, renderRelated: () => relatedItems.length > 0 ? (_jsx(RelatedProductsCarousel, { items: relatedItems })) : null }), _jsxs(BuyBar, { children: [formattedPrice && (_jsx(Span, { className: "mr-auto text-sm font-bold text-zinc-900 dark:text-zinc-50", children: formattedPrice })), _jsx(Button, { variant: "secondary", size: "sm", className: "shrink-0", disabled: !inStock, children: "Add to Cart" }), _jsx(Button, { variant: "primary", size: "sm", className: "flex-1", disabled: !inStock, children: inStock ? "Buy Now" : "Out of Stock" })] })] })] }));
267
269
  }
@@ -13,6 +13,8 @@ export interface ProductDetailViewProps extends Omit<DetailViewShellProps, "main
13
13
  /** Rendered below the sub-listing section and above the tabs (e.g. group carousel). */
14
14
  renderGroupSection?: () => React.ReactNode;
15
15
  renderTabs?: () => React.ReactNode;
16
+ /** Bundle membership section rendered below tabs and above related products. */
17
+ renderBundleSection?: () => React.ReactNode;
16
18
  renderRelated?: () => React.ReactNode;
17
19
  /**
18
20
  * When true (default), the action rail (col 3) becomes sticky on desktop so
@@ -25,4 +27,4 @@ export interface ProductDetailViewProps extends Omit<DetailViewShellProps, "main
25
27
  */
26
28
  stickyRailOffset?: string;
27
29
  }
28
- export declare function ProductDetailView({ renderGallery, renderInfo, renderActions, renderSublistingSection, renderGroupSection, renderTabs, renderRelated, isLoading, stickyActionRail, stickyRailOffset, ...rest }: ProductDetailViewProps): import("react/jsx-runtime").JSX.Element;
30
+ export declare function ProductDetailView({ renderGallery, renderInfo, renderActions, renderSublistingSection, renderGroupSection, renderTabs, renderBundleSection, renderRelated, isLoading, stickyActionRail, stickyRailOffset, ...rest }: ProductDetailViewProps): import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { DetailViewShell } from "../../../ui";
4
- export function ProductDetailView({ renderGallery, renderInfo, renderActions, renderSublistingSection, renderGroupSection, renderTabs, renderRelated, isLoading = false, stickyActionRail = true, stickyRailOffset = "top-20", ...rest }) {
4
+ export function ProductDetailView({ renderGallery, renderInfo, renderActions, renderSublistingSection, renderGroupSection, renderTabs, renderBundleSection, renderRelated, isLoading = false, stickyActionRail = true, stickyRailOffset = "top-20", ...rest }) {
5
5
  const sublistingNode = renderSublistingSection?.();
6
6
  const groupNode = renderGroupSection?.();
7
7
  const afterMainNode = sublistingNode || groupNode ? (_jsxs(React.Fragment, { children: [sublistingNode, groupNode] })) : undefined;
@@ -9,5 +9,5 @@ export function ProductDetailView({ renderGallery, renderInfo, renderActions, re
9
9
  renderGallery?.(isLoading),
10
10
  renderInfo?.(isLoading),
11
11
  renderActions?.(),
12
- ], afterMain: afterMainNode, belowFold: [renderTabs?.(), renderRelated?.()] }));
12
+ ], afterMain: afterMainNode, belowFold: [renderTabs?.(), renderBundleSection?.(), renderRelated?.()] }));
13
13
  }
@@ -3,9 +3,9 @@ import type { UrlTable } from "../../filters/FilterPanel";
3
3
  export type { FacetOption, UrlTable };
4
4
  export type ProductFilterVariant = "admin" | "seller" | "public";
5
5
  export declare const PRODUCT_FILTER_KEYS: {
6
- readonly admin: readonly ["category", "condition", "minPrice", "maxPrice", "brand", "seller", "tags", "status"];
7
- readonly seller: readonly ["category", "condition", "minPrice", "maxPrice", "brand", "tags", "status"];
8
- readonly public: readonly ["category", "condition", "minPrice", "maxPrice", "brand", "storeId", "tags"];
6
+ readonly admin: readonly ["category", "condition", "minPrice", "maxPrice", "brand", "seller", "tags", "status", "sublistingCategory", "isPartOfBundle", "features"];
7
+ readonly seller: readonly ["category", "condition", "minPrice", "maxPrice", "brand", "tags", "status", "sublistingCategory", "isPartOfBundle", "features"];
8
+ readonly public: readonly ["category", "condition", "minPrice", "maxPrice", "brand", "storeId", "tags", "sublistingCategory", "isPartOfBundle", "features"];
9
9
  };
10
10
  export declare const PRODUCT_ADMIN_SORT_OPTIONS: readonly [{
11
11
  readonly value: string;
@@ -81,11 +81,17 @@ export interface ProductFiltersProps {
81
81
  /** @deprecated use storeOptions */
82
82
  sellerOptions?: FacetOption[];
83
83
  tagOptions?: FacetOption[];
84
+ /** Sublisting category options — separate from main category filter */
85
+ sublistingCategoryOptions?: FacetOption[];
86
+ /** Feature badge options for filtering by product features */
87
+ featureOptions?: FacetOption[];
84
88
  showStatus?: boolean;
85
89
  variant?: ProductFilterVariant;
86
90
  statusOptions?: FacetOption[];
87
91
  currencyPrefix?: string;
88
92
  /** Show free-shipping toggle */
89
93
  showShipping?: boolean;
94
+ /** Show "part of a bundle" switch filter (default true) */
95
+ showBundleFilter?: boolean;
90
96
  }
91
- export declare function ProductFilters({ table, categoryOptions, brandOptions, storeOptions, sellerOptions, tagOptions, showStatus, variant, statusOptions, currencyPrefix, showShipping, }: ProductFiltersProps): import("react/jsx-runtime").JSX.Element;
97
+ export declare function ProductFilters({ table, categoryOptions, brandOptions, storeOptions, sellerOptions, tagOptions, sublistingCategoryOptions, featureOptions, showStatus, variant, statusOptions, currencyPrefix, showShipping, showBundleFilter, }: ProductFiltersProps): import("react/jsx-runtime").JSX.Element;
@@ -18,6 +18,9 @@ export const PRODUCT_FILTER_KEYS = {
18
18
  TABLE_KEYS.SELLER,
19
19
  TABLE_KEYS.TAGS,
20
20
  TABLE_KEYS.STATUS,
21
+ TABLE_KEYS.SUBLISTING_CATEGORY,
22
+ TABLE_KEYS.IS_PART_OF_BUNDLE,
23
+ TABLE_KEYS.FEATURES,
21
24
  ],
22
25
  seller: [
23
26
  TABLE_KEYS.CATEGORY,
@@ -27,6 +30,9 @@ export const PRODUCT_FILTER_KEYS = {
27
30
  TABLE_KEYS.BRAND,
28
31
  TABLE_KEYS.TAGS,
29
32
  TABLE_KEYS.STATUS,
33
+ TABLE_KEYS.SUBLISTING_CATEGORY,
34
+ TABLE_KEYS.IS_PART_OF_BUNDLE,
35
+ TABLE_KEYS.FEATURES,
30
36
  ],
31
37
  public: [
32
38
  TABLE_KEYS.CATEGORY,
@@ -36,6 +42,9 @@ export const PRODUCT_FILTER_KEYS = {
36
42
  TABLE_KEYS.BRAND,
37
43
  TABLE_KEYS.STORE_ID,
38
44
  TABLE_KEYS.TAGS,
45
+ TABLE_KEYS.SUBLISTING_CATEGORY,
46
+ TABLE_KEYS.IS_PART_OF_BUNDLE,
47
+ TABLE_KEYS.FEATURES,
39
48
  ],
40
49
  };
41
50
  export const PRODUCT_ADMIN_SORT_OPTIONS = [
@@ -78,7 +87,7 @@ export function getProductSortOptions(variant) {
78
87
  return PRODUCT_PUBLIC_SORT_OPTIONS;
79
88
  }
80
89
  }
81
- export function ProductFilters({ table, categoryOptions = [], brandOptions = [], storeOptions = [], sellerOptions, tagOptions = [], showStatus = false, variant, statusOptions, currencyPrefix = "", showShipping = true, }) {
90
+ export function ProductFilters({ table, categoryOptions = [], brandOptions = [], storeOptions = [], sellerOptions, tagOptions = [], sublistingCategoryOptions = [], featureOptions = [], showStatus = false, variant, statusOptions, currencyPrefix = "", showShipping = true, showBundleFilter = true, }) {
82
91
  const resolvedStoreOptions = storeOptions.length > 0 ? storeOptions : (sellerOptions ?? []);
83
92
  const t = useTranslations("filters");
84
93
  const conditionOptions = [
@@ -92,21 +101,16 @@ export function ProductFilters({ table, categoryOptions = [], brandOptions = [],
92
101
  { value: PRODUCT_FIELDS.STATUS_VALUES.DRAFT, label: t("statusDraft") },
93
102
  { value: PRODUCT_FIELDS.STATUS_VALUES.OUT_OF_STOCK, label: t("statusOutOfStock") },
94
103
  ];
95
- const selectedCategories = table.get(TABLE_KEYS.CATEGORY)
96
- ? table.get(TABLE_KEYS.CATEGORY).split("|").filter(Boolean)
97
- : [];
98
- const selectedBrands = table.get(TABLE_KEYS.BRAND) ? [table.get(TABLE_KEYS.BRAND)] : [];
99
- const selectedSellers = table.get(TABLE_KEYS.STORE_ID) ? [table.get(TABLE_KEYS.STORE_ID)] : [];
100
- const selectedConditions = table.get(TABLE_KEYS.CONDITION)
101
- ? table.get(TABLE_KEYS.CONDITION).split("|").filter(Boolean)
102
- : [];
103
- const selectedTags = table.get(TABLE_KEYS.TAGS)
104
- ? table.get(TABLE_KEYS.TAGS).split("|").filter(Boolean)
105
- : [];
106
- const selectedStatuses = table.get(TABLE_KEYS.STATUS)
107
- ? table.get(TABLE_KEYS.STATUS).split("|").filter(Boolean)
108
- : [];
104
+ const splitPipe = (key) => table.get(key) ? table.get(key).split("|").filter(Boolean) : [];
105
+ const selectedCategories = splitPipe(TABLE_KEYS.CATEGORY);
106
+ const selectedBrands = splitPipe(TABLE_KEYS.BRAND);
107
+ const selectedSellers = splitPipe(TABLE_KEYS.STORE_ID);
108
+ const selectedConditions = splitPipe(TABLE_KEYS.CONDITION);
109
+ const selectedTags = splitPipe(TABLE_KEYS.TAGS);
110
+ const selectedStatuses = splitPipe(TABLE_KEYS.STATUS);
111
+ const selectedSublistings = splitPipe(TABLE_KEYS.SUBLISTING_CATEGORY);
112
+ const selectedFeatures = splitPipe(TABLE_KEYS.FEATURES);
109
113
  const resolvedVariant = variant ?? (showStatus ? "admin" : "public");
110
114
  const shouldShowStatus = resolvedVariant !== "public" || showStatus;
111
- return (_jsxs(Div, { children: [categoryOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("category"), options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set(TABLE_KEYS.CATEGORY, vals.join("|")), searchable: true, defaultCollapsed: categoryOptions.length > 6 })), _jsx(FilterFacetSection, { title: t("condition"), options: conditionOptions, selected: selectedConditions, onChange: (vals) => table.set(TABLE_KEYS.CONDITION, vals.join("|")), searchable: false, defaultCollapsed: false }), _jsx(RangeFilter, { title: t("priceRange"), minValue: table.get(TABLE_KEYS.MIN_PRICE), maxValue: table.get(TABLE_KEYS.MAX_PRICE), onMinChange: (v) => table.set(TABLE_KEYS.MIN_PRICE, v), onMaxChange: (v) => table.set(TABLE_KEYS.MAX_PRICE, v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 500, minPlaceholder: t("minPrice"), maxPlaceholder: t("maxPrice"), defaultCollapsed: false }), brandOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("brand"), options: brandOptions, selected: selectedBrands, onChange: (vals) => table.set(TABLE_KEYS.BRAND, vals[0] ?? ""), searchable: brandOptions.length > 4, defaultCollapsed: brandOptions.length > 6 })), resolvedStoreOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("store"), options: resolvedStoreOptions, selected: selectedSellers, onChange: (vals) => table.set(TABLE_KEYS.STORE_ID, vals[0] ?? ""), searchable: resolvedStoreOptions.length > 4, defaultCollapsed: resolvedStoreOptions.length > 6 })), showShipping && (_jsx(SwitchFilter, { title: t("shipping"), label: t("freeShippingOnly"), checked: table.get(TABLE_KEYS.FREE_SHIPPING) === "true", onChange: (v) => table.set(TABLE_KEYS.FREE_SHIPPING, v ? "true" : ""), defaultCollapsed: false })), tagOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("tags"), options: tagOptions, selected: selectedTags, onChange: (vals) => table.set(TABLE_KEYS.TAGS, vals.join("|")), searchable: tagOptions.length > 4, defaultCollapsed: tagOptions.length > 6 })), shouldShowStatus && (_jsx(FilterFacetSection, { title: t("status"), options: statusOptions ?? defaultStatusOptions, selected: selectedStatuses, onChange: (vals) => table.set(TABLE_KEYS.STATUS, vals.join("|")), searchable: false, defaultCollapsed: false }))] }));
115
+ return (_jsxs(Div, { children: [categoryOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("category"), options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set(TABLE_KEYS.CATEGORY, vals.join("|")), searchable: true, defaultCollapsed: categoryOptions.length > 6 })), _jsx(FilterFacetSection, { title: t("condition"), options: conditionOptions, selected: selectedConditions, onChange: (vals) => table.set(TABLE_KEYS.CONDITION, vals.join("|")), searchable: false, defaultCollapsed: false }), _jsx(RangeFilter, { title: t("priceRange"), minValue: table.get(TABLE_KEYS.MIN_PRICE), maxValue: table.get(TABLE_KEYS.MAX_PRICE), onMinChange: (v) => table.set(TABLE_KEYS.MIN_PRICE, v), onMaxChange: (v) => table.set(TABLE_KEYS.MAX_PRICE, v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 500, minPlaceholder: t("minPrice"), maxPlaceholder: t("maxPrice"), defaultCollapsed: false }), brandOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("brand"), options: brandOptions, selected: selectedBrands, onChange: (vals) => table.set(TABLE_KEYS.BRAND, vals.join("|")), searchable: brandOptions.length > 4, defaultCollapsed: brandOptions.length > 6 })), resolvedStoreOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("store"), options: resolvedStoreOptions, selected: selectedSellers, onChange: (vals) => table.set(TABLE_KEYS.STORE_ID, vals.join("|")), searchable: resolvedStoreOptions.length > 4, defaultCollapsed: resolvedStoreOptions.length > 6 })), showShipping && (_jsx(SwitchFilter, { title: t("shipping"), label: t("freeShippingOnly"), checked: table.get(TABLE_KEYS.FREE_SHIPPING) === "true", onChange: (v) => table.set(TABLE_KEYS.FREE_SHIPPING, v ? "true" : ""), defaultCollapsed: false })), tagOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("tags"), options: tagOptions, selected: selectedTags, onChange: (vals) => table.set(TABLE_KEYS.TAGS, vals.join("|")), searchable: tagOptions.length > 4, defaultCollapsed: tagOptions.length > 6 })), shouldShowStatus && (_jsx(FilterFacetSection, { title: t("status"), options: statusOptions ?? defaultStatusOptions, selected: selectedStatuses, onChange: (vals) => table.set(TABLE_KEYS.STATUS, vals.join("|")), searchable: false, defaultCollapsed: false })), sublistingCategoryOptions.length > 0 && (_jsx(FilterFacetSection, { title: "Sublisting Type", options: sublistingCategoryOptions, selected: selectedSublistings, onChange: (vals) => table.set(TABLE_KEYS.SUBLISTING_CATEGORY, vals.join("|")), searchable: sublistingCategoryOptions.length > 4, defaultCollapsed: false })), showBundleFilter && (_jsx(SwitchFilter, { title: "Bundles", label: "Part of a bundle only", checked: table.get(TABLE_KEYS.IS_PART_OF_BUNDLE) === "true", onChange: (v) => table.set(TABLE_KEYS.IS_PART_OF_BUNDLE, v ? "true" : ""), defaultCollapsed: false })), featureOptions.length > 0 && (_jsx(FilterFacetSection, { title: "Features", options: featureOptions, selected: selectedFeatures, onChange: (vals) => table.set(TABLE_KEYS.FEATURES, vals.join("|")), searchable: featureOptions.length > 4, defaultCollapsed: featureOptions.length > 4 }))] }));
112
116
  }
@@ -49,7 +49,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
49
49
  .filter(Boolean)
50
50
  .join(" "), children: [_jsxs(Div, { className: "relative overflow-hidden bg-zinc-100 dark:bg-slate-800 aspect-square", children: [product.mainImage ? (_jsx(MediaImage, { src: product.mainImage, alt: product.title, size: "card", className: "transition-transform duration-500 group-hover:scale-105" })) : (_jsx(Div, { className: "h-full w-full flex items-center justify-center bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700", children: _jsx(Span, { className: "text-4xl opacity-30", children: "\uD83D\uDECD\uFE0F" }) })), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(product.id); }, label: isSelected ? "Deselect" : "Select", position: "top-2 left-2", className: selectionMode || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsx(Div, { className: `absolute top-2 flex flex-col gap-1 ${onSelect ? "left-10" : "left-2"}`, children: !selectionMode && !isSelected && (_jsxs(_Fragment, { children: [discount && (_jsxs(Span, { className: "rounded-full bg-rose-500 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", children: ["-", discount, "%"] })), typeBadge && (_jsx(Span, { className: `rounded-full px-2 py-0.5 text-[10px] font-bold shadow-sm ${typeBadge.cls}`, children: typeBadge.label })), product.partOfBundleIds && product.partOfBundleIds.length > 0 && (_jsx(Span, { className: "rounded-full bg-teal-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: product.partOfBundleTitles && product.partOfBundleTitles.length > 0
51
51
  ? `In bundle: ${product.partOfBundleTitles[0]}`
52
- : "In a bundle", children: "Bundled" }))] })) }), onAddToWishlist && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => {
52
+ : "In a bundle", children: "Bundled" })), product.groupId && (_jsx(Span, { className: "rounded-full bg-indigo-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: product.groupTitle ? `Part of set: ${product.groupTitle}` : "Part of a set", children: product.isGroupParent ? "Set Parent" : "In Set" })), product.sublistingCategoryId && (_jsx(Span, { className: "rounded-full bg-sky-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: "Has variants or sub-listings", children: "Has Variants" }))] })) }), onAddToWishlist && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => {
53
53
  e.stopPropagation();
54
54
  e.preventDefault();
55
55
  onAddToWishlist(product.id);
@@ -59,7 +59,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
59
59
  isWishlisted
60
60
  ? "bg-rose-500 text-white hover:bg-rose-600"
61
61
  : "bg-white/90 dark:bg-slate-800/90 text-zinc-500 dark:text-zinc-400 hover:text-rose-500 hover:bg-white dark:hover:bg-slate-800",
62
- ].join(" "), children: _jsx("svg", { className: "h-4 w-4", fill: isWishlisted ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" }) }) }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3 pt-2.5", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-900 dark:text-white leading-snug`, children: product.title }), (product.categoryName || product.brand) && (_jsxs(Row, { className: "mt-1 gap-1 flex-wrap", children: [product.categoryName && (_jsx(Span, { className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:text-zinc-400 truncate max-w-[90px]", children: product.categoryName })), product.brand && (_jsx(Span, { className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:text-zinc-400 truncate max-w-[80px]", children: product.brand }))] })), (() => {
62
+ ].join(" "), children: _jsx("svg", { className: "h-4 w-4", fill: isWishlisted ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" }) }) }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3 pt-2.5", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-900 dark:text-white leading-snug`, children: product.title }), (product.categoryName || product.brand) && (_jsxs(Row, { className: "mt-1 gap-1 flex-wrap", children: [product.categoryName && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:text-zinc-400 truncate max-w-[100px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 7a2 2 0 012-2h14a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V7z" }) }), product.categoryName] })), product.brand && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:text-zinc-400 truncate max-w-[90px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" }) }), product.brand] }))] })), (() => {
63
63
  const seller = safeDisplayName(product.storeName, "");
64
64
  return seller ? (_jsxs(Text, { className: "mt-0.5 text-[11px] text-zinc-400 dark:text-zinc-500 truncate", children: ["by ", seller] })) : null;
65
65
  })(), product.rating !== undefined && (_jsxs(Row, { className: "mt-1 gap-1 items-center", children: [_jsx(Span, { className: "text-[11px] text-amber-400", children: "\u2605" }), _jsxs(Span, { className: "text-[11px] text-zinc-500 dark:text-zinc-400", children: [product.rating.toFixed(1), product.reviewCount ? ` (${product.reviewCount})` : ""] })] })), _jsxs(Div, { className: "mt-auto pt-2", children: [_jsxs(Row, { className: "items-baseline gap-2", children: [_jsx(Span, { className: "text-base font-bold text-primary dark:text-primary-400", children: formatCurrency(product.price, getDefaultCurrency()) }), product.originalPrice && product.originalPrice > product.price && (_jsx(Span, { className: "text-xs text-zinc-400 line-through dark:text-zinc-500", children: formatCurrency(product.originalPrice, getDefaultCurrency()) }))] }), (() => {
@@ -79,6 +79,7 @@ export function ProductsIndexListing({ initialData }) {
79
79
  setSearchInput("");
80
80
  }, [table]);
81
81
  const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
82
+ const pendingFilterCount = FILTER_KEYS.filter((k) => !!pendingFilters[k]).length;
82
83
  const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
83
84
  table.get(TABLE_KEYS.SHOW_SOLD) === "true" ||
84
85
  table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
@@ -196,5 +197,5 @@ export function ProductsIndexListing({ initialData }) {
196
197
  ] }), 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: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4", children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: view === "grid" ? "card" : "list", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart, onBuyNow: handleBuyNow, selectionMode: selection.isSelecting, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "product", onClose: () => {
197
198
  setCompareIds([]);
198
199
  selection.clearSelection();
199
- }, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== 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: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _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(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions }) }), _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})` : ""] }) })] })] }))] }));
200
+ }, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== 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: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [pendingFilterCount > 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(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions }) }), _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", pendingFilterCount > 0 ? ` (${pendingFilterCount})` : ""] }) })] })] }))] }));
200
201
  }
@@ -18,10 +18,14 @@ export const PRODUCT_FEATURE_CATEGORY_OPTIONS = [
18
18
  { value: "custom", label: "Custom" },
19
19
  ];
20
20
  export const PRODUCT_FEATURE_PRODUCT_TYPE_OPTIONS = [
21
- { value: "all", label: "All product types" },
21
+ { value: "all", label: "All listing types" },
22
22
  { value: "product", label: "Standard product" },
23
23
  { value: "auction", label: "Auction" },
24
24
  { value: "preorder", label: "Pre-order" },
25
+ { value: "prize-draw", label: "Prize draw" },
26
+ { value: "classified", label: "Classified" },
27
+ { value: "digital-code", label: "Digital code" },
28
+ { value: "live", label: "Live item" },
25
29
  ];
26
30
  export const PRODUCT_FEATURE_SCOPE_OPTIONS = [
27
31
  { value: "platform", label: "Platform (all stores)" },
@@ -315,6 +315,7 @@ export declare const productItemSchema: z.ZodObject<{
315
315
  preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
316
316
  prizeRevealStatus?: "pending" | "closed" | "open" | undefined;
317
317
  categorySlug?: string | undefined;
318
+ features?: string[] | undefined;
318
319
  images?: string[] | undefined;
319
320
  description?: string | undefined;
320
321
  isPromoted?: boolean | undefined;
@@ -328,7 +329,6 @@ export declare const productItemSchema: z.ZodObject<{
328
329
  name: string;
329
330
  unit?: string | undefined;
330
331
  }[] | undefined;
331
- features?: string[] | undefined;
332
332
  shippingInfo?: string | undefined;
333
333
  returnPolicy?: string | undefined;
334
334
  insurance?: boolean | undefined;
@@ -444,6 +444,7 @@ export declare const productItemSchema: z.ZodObject<{
444
444
  preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
445
445
  prizeRevealStatus?: "pending" | "closed" | "open" | undefined;
446
446
  categorySlug?: string | undefined;
447
+ features?: string[] | undefined;
447
448
  images?: string[] | undefined;
448
449
  description?: string | undefined;
449
450
  isPromoted?: boolean | undefined;
@@ -457,7 +458,6 @@ export declare const productItemSchema: z.ZodObject<{
457
458
  name: string;
458
459
  unit?: string | undefined;
459
460
  }[] | undefined;
460
- features?: string[] | undefined;
461
461
  shippingInfo?: string | undefined;
462
462
  returnPolicy?: string | undefined;
463
463
  insurance?: boolean | undefined;
@@ -20,7 +20,7 @@ export declare const MAX_STORE_CUSTOM_FEATURES = 20;
20
20
  export declare const MAX_FEATURES_PER_PRODUCT = 10;
21
21
  export type ProductFeatureScope = "platform" | "store";
22
22
  export type ProductFeatureCategory = "shipping" | "seller" | "condition" | "platform" | "auction" | "preorder" | "custom";
23
- export type ProductFeatureProductType = "product" | "auction" | "preorder" | "all";
23
+ export type ProductFeatureProductType = "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live" | "all";
24
24
  /**
25
25
  * Either an appkit icon-set name key (e.g. "truck") or a raw SVG path-d string.
26
26
  * Path strings must start with "M " — see isFeatureIconPath().
@@ -6,7 +6,7 @@
6
6
  * of hand-rolling parallel schemas.
7
7
  */
8
8
  import { z } from "zod";
9
- export declare const PRODUCT_FEATURE_PRODUCT_TYPE_ENUM: z.ZodEnum<["product", "auction", "preorder", "all"]>;
9
+ export declare const PRODUCT_FEATURE_PRODUCT_TYPE_ENUM: z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>;
10
10
  export declare const PRODUCT_FEATURE_CATEGORY_ENUM: z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>;
11
11
  export declare const PRODUCT_FEATURE_SCOPE_ENUM: z.ZodEnum<["platform", "store"]>;
12
12
  /** Shape for POST /api/admin/features — admin creates either scope. */
@@ -17,7 +17,7 @@ export declare const productFeatureAdminCreateSchema: z.ZodObject<{
17
17
  iconColor: z.ZodOptional<z.ZodString>;
18
18
  category: z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>;
19
19
  scope: z.ZodEnum<["platform", "store"]>;
20
- productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "all"]>, "many">;
20
+ productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>, "many">;
21
21
  storeId: z.ZodOptional<z.ZodString>;
22
22
  isActive: z.ZodBoolean;
23
23
  displayOrder: z.ZodNumber;
@@ -28,7 +28,7 @@ export declare const productFeatureAdminCreateSchema: z.ZodObject<{
28
28
  isActive: boolean;
29
29
  category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
30
30
  displayOrder: number;
31
- productTypes: ("all" | "product" | "auction" | "preorder")[];
31
+ productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
32
32
  storeId?: string | undefined;
33
33
  description?: string | undefined;
34
34
  iconColor?: string | undefined;
@@ -39,7 +39,7 @@ export declare const productFeatureAdminCreateSchema: z.ZodObject<{
39
39
  isActive: boolean;
40
40
  category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
41
41
  displayOrder: number;
42
- productTypes: ("all" | "product" | "auction" | "preorder")[];
42
+ productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
43
43
  storeId?: string | undefined;
44
44
  description?: string | undefined;
45
45
  iconColor?: string | undefined;
@@ -51,7 +51,7 @@ export declare const productFeatureStoreCreateSchema: z.ZodObject<{
51
51
  icon: z.ZodString;
52
52
  iconColor: z.ZodOptional<z.ZodString>;
53
53
  category: z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>;
54
- productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "all"]>, "many">;
54
+ productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>, "many">;
55
55
  isActive: z.ZodBoolean;
56
56
  displayOrder: z.ZodNumber;
57
57
  }, "strip", z.ZodTypeAny, {
@@ -60,7 +60,7 @@ export declare const productFeatureStoreCreateSchema: z.ZodObject<{
60
60
  isActive: boolean;
61
61
  category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
62
62
  displayOrder: number;
63
- productTypes: ("all" | "product" | "auction" | "preorder")[];
63
+ productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
64
64
  description?: string | undefined;
65
65
  iconColor?: string | undefined;
66
66
  }, {
@@ -69,7 +69,7 @@ export declare const productFeatureStoreCreateSchema: z.ZodObject<{
69
69
  isActive: boolean;
70
70
  category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
71
71
  displayOrder: number;
72
- productTypes: ("all" | "product" | "auction" | "preorder")[];
72
+ productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
73
73
  description?: string | undefined;
74
74
  iconColor?: string | undefined;
75
75
  }>;
@@ -80,7 +80,7 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
80
80
  icon: z.ZodOptional<z.ZodString>;
81
81
  iconColor: z.ZodOptional<z.ZodString>;
82
82
  category: z.ZodOptional<z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>>;
83
- productTypes: z.ZodOptional<z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "all"]>, "many">>;
83
+ productTypes: z.ZodOptional<z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>, "many">>;
84
84
  isActive: z.ZodOptional<z.ZodBoolean>;
85
85
  displayOrder: z.ZodOptional<z.ZodNumber>;
86
86
  }, "strip", z.ZodTypeAny, {
@@ -91,7 +91,7 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
91
91
  description?: string | undefined;
92
92
  iconColor?: string | undefined;
93
93
  displayOrder?: number | undefined;
94
- productTypes?: ("all" | "product" | "auction" | "preorder")[] | undefined;
94
+ productTypes?: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[] | undefined;
95
95
  }, {
96
96
  label?: string | undefined;
97
97
  icon?: string | undefined;
@@ -100,7 +100,7 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
100
100
  description?: string | undefined;
101
101
  iconColor?: string | undefined;
102
102
  displayOrder?: number | undefined;
103
- productTypes?: ("all" | "product" | "auction" | "preorder")[] | undefined;
103
+ productTypes?: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[] | undefined;
104
104
  }>;
105
105
  export type ProductFeatureAdminCreatePayload = z.infer<typeof productFeatureAdminCreateSchema>;
106
106
  export type ProductFeatureStoreCreatePayload = z.infer<typeof productFeatureStoreCreateSchema>;
@@ -10,6 +10,10 @@ export const PRODUCT_FEATURE_PRODUCT_TYPE_ENUM = z.enum([
10
10
  "product",
11
11
  "auction",
12
12
  "preorder",
13
+ "prize-draw",
14
+ "classified",
15
+ "digital-code",
16
+ "live",
13
17
  "all",
14
18
  ]);
15
19
  export const PRODUCT_FEATURE_CATEGORY_ENUM = z.enum([
@@ -61,7 +61,7 @@ export function SellerCouponEditorView({ couponId, initial, onSave, onCancel, })
61
61
  }
62
62
  };
63
63
  const isEdit = Boolean(couponId);
64
- return (_jsx("form", { onSubmit: handleSubmit, noValidate: true, children: _jsxs(Stack, { gap: "none", className: "max-w-lg mx-auto", children: [_jsx(Div, { className: "border-b border-[var(--appkit-color-border,#e5e7eb)] dark:border-[var(--appkit-color-border-dark,#374151)] px-6 py-5", children: _jsxs(Row, { className: "items-center justify-between gap-3", children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-[var(--appkit-color-text-primary)]", children: isEdit ? "Edit Coupon" : "Create Coupon" }), isEdit && (_jsx(Badge, { variant: draft.isActive ? "success" : "default", children: draft.isActive ? "Active" : "Inactive" }))] }) }), _jsxs(Stack, { gap: "5", className: "px-6 py-6", children: [error && (_jsx(Div, { className: "rounded-lg 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: error })), _jsx(Input, { label: "Coupon Code", value: draft.code, onChange: (e) => set("code", e.target.value.toUpperCase().replace(/\s+/g, "")), placeholder: "e.g. WELCOME10", required: true, disabled: isEdit, helperText: isEdit ? "Code cannot be changed after creation" : "Customers enter this at checkout" }), _jsx(Select, { label: "Discount Type", value: draft.type, options: TYPE_OPTIONS, onChange: (e) => set("type", e.target.value) }), draft.type !== "free_shipping" && (_jsx(Input, { label: draft.type === "percentage" ? "Discount Percentage (%)" : "Discount Amount (₹)", type: "number", min: 1, max: draft.type === "percentage" ? 100 : undefined, value: draft.value, onChange: (e) => set("value", e.target.value), placeholder: draft.type === "percentage" ? "e.g. 10" : "e.g. 50", required: true, helperText: draft.type === "percentage"
64
+ return (_jsx("form", { onSubmit: handleSubmit, noValidate: true, children: _jsxs(Stack, { gap: "none", className: "max-w-lg mx-auto rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] shadow-sm overflow-hidden", children: [_jsx("div", { className: "h-[3px] w-full", style: { background: "linear-gradient(to right,var(--appkit-color-primary-700,#1343de) 0%,var(--appkit-color-cobalt,#3570fc) 55%,var(--appkit-color-secondary-400,#84e122) 100%)" }, "aria-hidden": "true" }), _jsx(Div, { className: "border-b border-[var(--appkit-color-border)] px-6 py-5", children: _jsxs(Row, { className: "items-center justify-between gap-3", children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-[var(--appkit-color-text)]", children: isEdit ? "Edit Coupon" : "Create Coupon" }), isEdit && (_jsx(Badge, { variant: draft.isActive ? "success" : "default", children: draft.isActive ? "Active" : "Inactive" }))] }) }), _jsxs(Stack, { gap: "5", className: "px-6 py-6", children: [error && (_jsx(Div, { className: "rounded-lg 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: error })), _jsx(Input, { label: "Coupon Code", value: draft.code, onChange: (e) => set("code", e.target.value.toUpperCase().replace(/\s+/g, "")), placeholder: "e.g. WELCOME10", required: true, disabled: isEdit, helperText: isEdit ? "Code cannot be changed after creation" : "Customers enter this at checkout" }), _jsx(Select, { label: "Discount Type", value: draft.type, options: TYPE_OPTIONS, onChange: (e) => set("type", e.target.value) }), draft.type !== "free_shipping" && (_jsx(Input, { label: draft.type === "percentage" ? "Discount Percentage (%)" : "Discount Amount (₹)", type: "number", min: 1, max: draft.type === "percentage" ? 100 : undefined, value: draft.value, onChange: (e) => set("value", e.target.value), placeholder: draft.type === "percentage" ? "e.g. 10" : "e.g. 50", required: true, helperText: draft.type === "percentage"
65
65
  ? "Enter a value between 1 and 100"
66
- : "Fixed rupee discount applied to the order" })), draft.type === "percentage" && (_jsx(Input, { label: "Max Discount Cap (\u20B9, optional)", type: "number", min: 0, value: draft.maxDiscount, onChange: (e) => set("maxDiscount", e.target.value), placeholder: "Leave blank for no cap", helperText: "Maximum rupee discount regardless of percentage" })), _jsx(Input, { label: "Minimum Order Amount (\u20B9, optional)", type: "number", min: 0, value: draft.minPurchase, onChange: (e) => set("minPurchase", e.target.value), placeholder: "Leave blank for no minimum" }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Usage Limits" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Total Uses", type: "number", min: 0, value: draft.totalLimit, onChange: (e) => set("totalLimit", e.target.value), placeholder: "0 = unlimited" }), _jsx(Input, { label: "Per Customer", type: "number", min: 0, value: draft.perUserLimit, onChange: (e) => set("perUserLimit", e.target.value), placeholder: "0 = unlimited" })] })] }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Validity Period" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Start Date", type: "date", value: draft.startDate, onChange: (e) => set("startDate", e.target.value), required: true }), _jsx(Input, { label: "End Date", type: "date", value: draft.endDate, onChange: (e) => set("endDate", e.target.value), required: true })] })] }), _jsx(Div, { className: "flex items-center gap-3 rounded-lg border border-[var(--appkit-color-border)] dark:border-[var(--appkit-color-border-dark)] px-4 py-3", children: _jsxs("label", { className: "flex items-center gap-3 cursor-pointer w-full", children: [_jsx("input", { type: "checkbox", checked: draft.isActive, onChange: (e) => set("isActive", e.target.checked), className: "h-4 w-4 rounded border-zinc-300 dark:border-slate-600 text-[var(--appkit-color-primary)] focus:ring-[var(--appkit-color-primary)]" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-primary)]", children: "Active" }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: "Customers can apply this coupon at checkout" })] })] }) })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)] dark:border-[var(--appkit-color-border-dark)] px-6 py-4", children: _jsxs(Row, { className: "items-center justify-end gap-3", children: [onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: saving, children: "Cancel" })), _jsx(Button, { type: "submit", isLoading: saving, disabled: saving, children: isEdit ? "Save Changes" : "Create Coupon" })] }) })] }) }));
66
+ : "Fixed rupee discount applied to the order" })), draft.type === "percentage" && (_jsx(Input, { label: "Max Discount Cap (\u20B9, optional)", type: "number", min: 0, value: draft.maxDiscount, onChange: (e) => set("maxDiscount", e.target.value), placeholder: "Leave blank for no cap", helperText: "Maximum rupee discount regardless of percentage" })), _jsx(Input, { label: "Minimum Order Amount (\u20B9, optional)", type: "number", min: 0, value: draft.minPurchase, onChange: (e) => set("minPurchase", e.target.value), placeholder: "Leave blank for no minimum" }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Usage Limits" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Total Uses", type: "number", min: 0, value: draft.totalLimit, onChange: (e) => set("totalLimit", e.target.value), placeholder: "0 = unlimited" }), _jsx(Input, { label: "Per Customer", type: "number", min: 0, value: draft.perUserLimit, onChange: (e) => set("perUserLimit", e.target.value), placeholder: "0 = unlimited" })] })] }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Validity Period" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Start Date", type: "date", value: draft.startDate, onChange: (e) => set("startDate", e.target.value), required: true }), _jsx(Input, { label: "End Date", type: "date", value: draft.endDate, onChange: (e) => set("endDate", e.target.value), required: true })] })] }), _jsx(Div, { className: "flex items-center gap-3 rounded-lg border border-[var(--appkit-color-border)] dark:border-[var(--appkit-color-border-dark)] px-4 py-3", children: _jsxs("label", { className: "flex items-center gap-3 cursor-pointer w-full", children: [_jsx("input", { type: "checkbox", checked: draft.isActive, onChange: (e) => set("isActive", e.target.checked), className: "h-4 w-4 rounded border-zinc-300 dark:border-slate-600 text-[var(--appkit-color-primary)] focus:ring-[var(--appkit-color-primary)]" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-primary)]", children: "Active" }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: "Customers can apply this coupon at checkout" })] })] }) })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)] px-6 py-4", children: _jsxs(Row, { className: "items-center justify-end gap-3", children: [onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: saving, children: "Cancel" })), _jsx(Button, { type: "submit", isLoading: saving, disabled: saving, children: isEdit ? "Save Changes" : "Create Coupon" })] }) })] }) }));
67
67
  }
@@ -1,8 +1,14 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Div, Heading, Span, Text } from "../../../../ui";
3
- function SellerAnalyticsStatCard({ label, value, icon, colorClassName, }) {
4
- return (_jsx(Div, { className: "bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800 p-4 sm:p-5", children: _jsxs(Div, { className: "flex items-start justify-between gap-3", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 font-medium uppercase tracking-wide", children: label }), _jsx(Heading, { level: 3, className: `mt-1 text-2xl font-bold ${colorClassName}`, children: value })] }), _jsx(Span, { className: "text-2xl sm:text-3xl", "aria-hidden": true, children: icon })] }) }));
3
+ import { TrendingUp, ShoppingBag, Package, CheckCircle } from "lucide-react";
4
+ import { Div } from "../../../../ui";
5
+ const GREEN_GRAD = "linear-gradient(135deg,var(--appkit-color-cobalt,#3570fc) 0%,var(--appkit-color-secondary-400,#84e122) 100%)";
6
+ const BRAND_GRAD = "linear-gradient(135deg,var(--appkit-color-primary-700,#1343de) 0%,var(--appkit-color-cobalt,#3570fc) 55%,var(--appkit-color-secondary-400,#84e122) 100%)";
7
+ const AMBER_GRAD = "linear-gradient(135deg,var(--appkit-color-amber-500,#f59e0b) 0%,var(--appkit-color-amber-600,#d97706) 100%)";
8
+ const BLUE_GRAD = "linear-gradient(135deg,var(--appkit-color-primary-700,#1343de) 0%,var(--appkit-color-cobalt,#3570fc) 100%)";
9
+ function StatCard({ label, value, gradient, Icon }) {
10
+ return (_jsxs(Div, { className: "relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow", children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]", style: { background: gradient }, "aria-hidden": "true" }), _jsxs(Div, { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]", children: label }), _jsx("div", { className: "mt-2 text-2xl font-bold text-[var(--appkit-color-text)] tabular-nums leading-none", children: value })] }), _jsx("div", { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center", style: { background: gradient }, children: _jsx(Icon, { className: "w-5 h-5 text-white" }) })] })] }));
5
11
  }
6
12
  export function SellerAnalyticsStats({ summary, labels = {}, formatRevenue = (amount) => String(amount), className = "", }) {
7
- return (_jsxs(Div, { className: `grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-4 gap-4 ${className}`, children: [_jsx(SellerAnalyticsStatCard, { label: labels.totalRevenue ?? "Total Revenue", value: formatRevenue(summary.totalRevenue), icon: "\uD83D\uDCB0", colorClassName: "text-emerald-600 dark:text-emerald-400" }), _jsx(SellerAnalyticsStatCard, { label: labels.totalOrders ?? "Total Orders", value: String(summary.totalOrders), icon: "\uD83D\uDCE6", colorClassName: "text-blue-600 dark:text-blue-400" }), _jsx(SellerAnalyticsStatCard, { label: labels.totalProducts ?? "Total Products", value: String(summary.totalProducts), icon: "\uD83D\uDECD\uFE0F", colorClassName: "text-violet-600 dark:text-violet-400" }), _jsx(SellerAnalyticsStatCard, { label: labels.publishedProducts ?? "Published Products", value: String(summary.publishedProducts), icon: "\u2705", colorClassName: "text-amber-600 dark:text-amber-400" })] }));
13
+ return (_jsxs(Div, { className: `grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4 ${className}`, children: [_jsx(StatCard, { label: labels.totalRevenue ?? "Total Revenue", value: formatRevenue(summary.totalRevenue), gradient: GREEN_GRAD, Icon: TrendingUp }), _jsx(StatCard, { label: labels.totalOrders ?? "Total Orders", value: String(summary.totalOrders), gradient: BRAND_GRAD, Icon: ShoppingBag }), _jsx(StatCard, { label: labels.totalProducts ?? "Total Products", value: String(summary.totalProducts), gradient: AMBER_GRAD, Icon: Package }), _jsx(StatCard, { label: labels.publishedProducts ?? "Published", value: String(summary.publishedProducts), gradient: BLUE_GRAD, Icon: CheckCircle })] }));
8
14
  }
@@ -1,5 +1,8 @@
1
1
  export interface StoreBundlesPageViewProps {
2
2
  storeSlug: string;
3
+ onBuyNow?: (input: {
4
+ bundleSlug: string;
5
+ }) => Promise<unknown>;
3
6
  }
4
7
  /**
5
8
  * Public store → Bundles tab.
@@ -9,4 +12,4 @@ export interface StoreBundlesPageViewProps {
9
12
  * the seller's active bundle categories and hands them to
10
13
  * `CategoryBundlesListing`. Mirrors the StorePrizeDrawsPageView pattern.
11
14
  */
12
- export declare function StoreBundlesPageView({ storeSlug, }: StoreBundlesPageViewProps): Promise<import("react/jsx-runtime").JSX.Element | null>;
15
+ export declare function StoreBundlesPageView({ storeSlug, onBuyNow, }: StoreBundlesPageViewProps): Promise<import("react/jsx-runtime").JSX.Element | null>;
@@ -10,7 +10,7 @@ import { CategoryBundlesListing } from "../../categories/components/CategoryBund
10
10
  * the seller's active bundle categories and hands them to
11
11
  * `CategoryBundlesListing`. Mirrors the StorePrizeDrawsPageView pattern.
12
12
  */
13
- export async function StoreBundlesPageView({ storeSlug, }) {
13
+ export async function StoreBundlesPageView({ storeSlug, onBuyNow, }) {
14
14
  const store = await getStoreBySlug(storeSlug);
15
15
  const storeId = store?.id;
16
16
  if (!storeId) {
@@ -20,5 +20,5 @@ export async function StoreBundlesPageView({ storeSlug, }) {
20
20
  .listByType("bundle", { activeOnly: true, limit: 50 })
21
21
  .then((rows) => rows.filter((c) => c.createdByStoreId === storeId))
22
22
  .catch(() => []);
23
- return _jsx(CategoryBundlesListing, { initialBundles: bundles });
23
+ return _jsx(CategoryBundlesListing, { initialBundles: bundles, onBuyNow: onBuyNow });
24
24
  }