@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.
- package/dist/_internal/server/features/bundles/actions.d.ts +15 -0
- package/dist/_internal/server/features/bundles/actions.js +48 -0
- package/dist/_internal/server/features/bundles/index.d.ts +1 -0
- package/dist/_internal/server/features/bundles/index.js +1 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +4 -0
- package/dist/_internal/server/features/checkout/actions.js +23 -12
- package/dist/_internal/server/features/payments/data.d.ts +5 -3
- package/dist/_internal/server/features/payments/data.js +13 -8
- package/dist/_internal/server/features/payments/index.d.ts +1 -1
- package/dist/_internal/server/features/payments/index.js +1 -1
- package/dist/_internal/shared/features/categories/bundle-config.d.ts +24 -0
- package/dist/_internal/shared/features/categories/bundle-config.js +28 -0
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +7 -0
- package/dist/_internal/shared/features/categories/bundle-copy.js +9 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +69 -19
- package/dist/_internal/shared/features/categories/bundle-schemas.js +20 -2
- package/dist/_internal/shared/features/checkout/config.d.ts +1 -1
- package/dist/_internal/shared/features/checkout/config.js +1 -1
- package/dist/_internal/shared/features/payments/config.d.ts +2 -1
- package/dist/_internal/shared/features/payments/config.js +2 -1
- package/dist/_internal/shared/features/products/schema.d.ts +8 -8
- package/dist/constants/field-names.d.ts +8 -1
- package/dist/constants/field-names.js +9 -2
- package/dist/constants/table-keys.d.ts +3 -0
- package/dist/constants/table-keys.js +3 -0
- package/dist/features/admin/components/AdminSiteSettingsView.js +17 -15
- package/dist/features/admin/components/DashboardStats.js +20 -12
- package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +18 -2
- package/dist/features/admin/components/analytics/AdminStatCard.d.ts +7 -1
- package/dist/features/admin/components/analytics/AdminStatCard.js +13 -2
- package/dist/features/admin/schemas/firestore.d.ts +18 -4
- package/dist/features/admin/schemas/firestore.js +11 -1
- package/dist/features/categories/components/BundleBuyNowCta.d.ts +3 -1
- package/dist/features/categories/components/BundleBuyNowCta.js +3 -3
- package/dist/features/categories/components/BundleDynamicRuleEditor.d.ts +1 -1
- package/dist/features/categories/components/BundleDynamicRuleEditor.js +2 -1
- package/dist/features/categories/components/BundlesListView.d.ts +4 -1
- package/dist/features/categories/components/BundlesListView.js +2 -2
- package/dist/features/categories/components/CategoryBundlesListing.d.ts +4 -1
- package/dist/features/categories/components/CategoryBundlesListing.js +9 -8
- package/dist/features/categories/schemas/firestore.d.ts +29 -1
- package/dist/features/checkout/actions/checkout-actions.d.ts +11 -0
- package/dist/features/checkout/actions/checkout-actions.js +26 -0
- package/dist/features/filters/FilterFacetSection.d.ts +6 -1
- package/dist/features/filters/FilterFacetSection.js +25 -2
- package/dist/features/homepage/components/FeaturedBundlesSection.d.ts +4 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +9 -8
- package/dist/features/orders/actions/refund-actions.js +10 -7
- package/dist/features/orders/schemas/firestore.d.ts +16 -13
- package/dist/features/orders/schemas/firestore.js +6 -5
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -1
- package/dist/features/products/components/AuctionsIndexListing.js +2 -1
- package/dist/features/products/components/ProductDetailPageView.js +3 -1
- package/dist/features/products/components/ProductDetailView.d.ts +3 -1
- package/dist/features/products/components/ProductDetailView.js +2 -2
- package/dist/features/products/components/ProductFilters.d.ts +10 -4
- package/dist/features/products/components/ProductFilters.js +20 -16
- package/dist/features/products/components/ProductGrid.js +2 -2
- package/dist/features/products/components/ProductsIndexListing.js +2 -1
- package/dist/features/products/constants/product-features.constants.js +5 -1
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/products/schemas/product-features.d.ts +1 -1
- package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
- package/dist/features/products/schemas/product-features.validators.js +4 -0
- package/dist/features/seller/components/SellerCouponEditorView.js +2 -2
- package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +10 -4
- package/dist/features/stores/components/StoreBundlesPageView.d.ts +4 -1
- package/dist/features/stores/components/StoreBundlesPageView.js +2 -2
- package/dist/features/stores/components/StoresIndexListing.js +2 -1
- package/dist/providers/db-firebase/admin-app-lite.js +8 -2
- package/dist/providers/db-firebase/admin.js +8 -2
- package/dist/seed/addresses-seed-data.js +24 -7
- package/dist/seed/cart-seed-data.d.ts +10 -10
- package/dist/seed/cart-seed-data.js +39 -2
- package/dist/seed/grouped-listings-seed-data.js +47 -0
- package/dist/seed/orders-seed-data.js +150 -0
- package/dist/seed/products-standard-seed-data.js +42 -0
- package/dist/seed/site-settings-seed-data.js +11 -4
- package/dist/seed/users-seed-data.js +1 -1
- package/dist/seed/wishlists-seed-data.js +7 -0
- package/dist/server-entry.d.ts +2 -1
- package/dist/server-entry.js +2 -1
- package/dist/server.d.ts +2 -1
- package/dist/server.js +3 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/SideDrawer.style.css +49 -11
- 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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: [
|
|
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: [
|
|
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
|
-
})) })),
|
|
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
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
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
|
|
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 && (
|
|
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: [
|
|
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
|
|
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>;
|
|
@@ -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(
|
|
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)]
|
|
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 {
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
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
|
}
|