@mohasinac/appkit 2.7.16 → 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/auctions/actions.js +1 -1
- package/dist/_internal/server/features/blog/actions.js +2 -2
- package/dist/_internal/server/features/brands/actions.js +2 -2
- 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/cart/actions.js +3 -3
- 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/events/actions.js +3 -3
- package/dist/_internal/server/features/orders/actions.js +4 -4
- 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/server/features/pre-orders/actions.js +1 -1
- package/dist/_internal/server/features/products/actions.js +6 -6
- package/dist/_internal/server/features/promotions/actions.js +3 -3
- 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 +3 -3
- 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/shell/FormShell.js +1 -1
- 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/features/wishlist/hooks/useWishlistCount.d.ts +2 -2
- package/dist/features/wishlist/hooks/useWishlistCount.js +5 -8
- 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/Breadcrumb.style.css +1 -1
- package/dist/ui/components/ListingLayout.style.css +1 -1
- package/dist/ui/components/Select.style.css +1 -1
- package/dist/ui/components/SideDrawer.style.css +55 -12
- package/dist/ui/components/Typography.style.css +2 -2
- package/dist/ui/rich-text/RichText.style.css +4 -5
- package/package.json +1 -1
|
@@ -94,4 +94,30 @@ export async function grantCheckoutConsentViaSms(userId, userPhone, addressId) {
|
|
|
94
94
|
});
|
|
95
95
|
serverLogger.info(`Checkout consent granted via SMS: uid=${userId} addressId=${addressId}`);
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Grants checkout consent for an admin test order, bypassing the normal OTP flow.
|
|
99
|
+
*
|
|
100
|
+
* The resulting Firestore doc is identical to a verified consent OTP so that
|
|
101
|
+
* `createCheckoutOrderAction` can proceed without modification. The
|
|
102
|
+
* `verifiedVia: "admin_bypass"` marker prevents bypass-credit grants.
|
|
103
|
+
*
|
|
104
|
+
* Security: callers MUST verify the requesting user is an admin AND that
|
|
105
|
+
* `siteSettings.featureFlags.adminCheckoutBypass === true` before calling this.
|
|
106
|
+
*/
|
|
107
|
+
export async function grantAdminCheckoutBypass(userId, addressId, bypassingAdminUid) {
|
|
108
|
+
const address = await findUserAddress(userId, addressId);
|
|
109
|
+
if (!address)
|
|
110
|
+
throw new ValidationError("Address not found.");
|
|
111
|
+
await saveConsentOtp(userId, addressId, {
|
|
112
|
+
codeHash: "",
|
|
113
|
+
expiresAt: new Date(Date.now() + CONSENT_OTP_EXPIRY_MS),
|
|
114
|
+
attempts: 0,
|
|
115
|
+
verified: true,
|
|
116
|
+
verifiedVia: "admin_bypass",
|
|
117
|
+
adminBypassBy: bypassingAdminUid,
|
|
118
|
+
addressId,
|
|
119
|
+
createdAt: new Date(),
|
|
120
|
+
});
|
|
121
|
+
serverLogger.info(`Admin checkout bypass granted: uid=${userId} addressId=${addressId} bypassedBy=${bypassingAdminUid}`);
|
|
122
|
+
}
|
|
97
123
|
export { userRepository };
|
|
@@ -15,10 +15,15 @@ export interface FilterFacetSectionProps {
|
|
|
15
15
|
onToggle?: () => void;
|
|
16
16
|
onClear?: () => void;
|
|
17
17
|
className?: string;
|
|
18
|
+
/**
|
|
19
|
+
* "accordion" (default) — collapsible inline section inside a filter drawer.
|
|
20
|
+
* "dropdown" — compact pill button with a floating panel; use in horizontal toolbars.
|
|
21
|
+
*/
|
|
22
|
+
displayAs?: "accordion" | "dropdown";
|
|
18
23
|
}
|
|
19
24
|
/**
|
|
20
25
|
* FilterFacetSection — collapsible checkbox/radio filter section.
|
|
21
26
|
* Supports single-select and multi-select modes.
|
|
22
27
|
* When searchable, the search input is always visible and typing auto-expands.
|
|
23
28
|
*/
|
|
24
|
-
export declare function FilterFacetSection({ title, options, selected, onChange, selectionMode, defaultCollapsed, searchable, isOpen: controlledOpen, onToggle, onClear, className, }: FilterFacetSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
export declare function FilterFacetSection({ title, options, selected, onChange, selectionMode, defaultCollapsed, searchable, isOpen: controlledOpen, onToggle, onClear, className, displayAs, }: FilterFacetSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { Button, Div, Input, Row, Span, Text } from "../../ui";
|
|
5
5
|
import { cn } from "./filterUtils";
|
|
6
6
|
/**
|
|
@@ -8,10 +8,23 @@ import { cn } from "./filterUtils";
|
|
|
8
8
|
* Supports single-select and multi-select modes.
|
|
9
9
|
* When searchable, the search input is always visible and typing auto-expands.
|
|
10
10
|
*/
|
|
11
|
-
export function FilterFacetSection({ title, options, selected, onChange, selectionMode = "multi", defaultCollapsed = true, searchable = false, isOpen: controlledOpen, onToggle, onClear, className = "", }) {
|
|
11
|
+
export function FilterFacetSection({ title, options, selected, onChange, selectionMode = "multi", defaultCollapsed = true, searchable = false, isOpen: controlledOpen, onToggle, onClear, className = "", displayAs = "accordion", }) {
|
|
12
12
|
const isControlled = controlledOpen !== undefined;
|
|
13
13
|
const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
|
|
14
14
|
const [search, setSearch] = useState("");
|
|
15
|
+
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
16
|
+
const dropdownRef = useRef(null);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (displayAs !== "dropdown")
|
|
19
|
+
return;
|
|
20
|
+
function handleOutsideClick(e) {
|
|
21
|
+
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
22
|
+
setDropdownOpen(false);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
document.addEventListener("mousedown", handleOutsideClick);
|
|
26
|
+
return () => document.removeEventListener("mousedown", handleOutsideClick);
|
|
27
|
+
}, [displayAs]);
|
|
15
28
|
const isCollapsed = isControlled ? !controlledOpen : internalCollapsed;
|
|
16
29
|
const handleToggle = () => {
|
|
17
30
|
if (onToggle)
|
|
@@ -44,6 +57,16 @@ export function FilterFacetSection({ title, options, selected, onChange, selecti
|
|
|
44
57
|
const selectedLabels = selected
|
|
45
58
|
.map((v) => options.find((o) => o.value === v)?.label ?? v)
|
|
46
59
|
.slice(0, 3);
|
|
60
|
+
// ── Dropdown variant ────────────────────────────────────────────────────
|
|
61
|
+
if (displayAs === "dropdown") {
|
|
62
|
+
return (_jsxs(Div, { ref: dropdownRef, className: cn("relative inline-block", className), children: [_jsxs(Button, { type: "button", onClick: () => setDropdownOpen((o) => !o), variant: "ghost", className: cn("inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-sm font-medium transition-colors", hasValue
|
|
63
|
+
? "border-emerald-500 bg-emerald-50 text-emerald-700 dark:border-emerald-500/60 dark:bg-emerald-900/20 dark:text-emerald-300"
|
|
64
|
+
: "border-zinc-200 bg-white text-zinc-700 hover:border-zinc-400 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-200 dark:hover:border-slate-500"), children: [title, hasValue && (_jsx(Span, { className: "inline-flex h-4 w-4 items-center justify-center rounded-full bg-emerald-600 text-[10px] font-bold text-white", children: selected.length })), _jsx("svg", { className: cn("h-3.5 w-3.5 text-zinc-400 transition-transform", dropdownOpen ? "rotate-180" : ""), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), dropdownOpen && (_jsxs(Div, { className: "absolute left-0 top-full z-[var(--appkit-z-dropdown,50)] mt-1 min-w-[180px] max-w-[260px] rounded-xl border border-zinc-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-900", children: [searchable && (_jsx(Div, { className: "border-b border-zinc-100 p-2 dark:border-slate-800", children: _jsx(Input, { type: "search", placeholder: "Search\u2026", value: search, onChange: (e) => handleSearch(e.target.value), className: "w-full rounded-md border border-zinc-200 bg-white px-2 py-1 text-xs text-zinc-900 focus:outline-none dark:border-slate-700 dark:bg-slate-800/60 dark:text-zinc-100" }) })), _jsxs(Div, { className: "max-h-56 overflow-y-auto py-1", children: [filtered.map((option) => {
|
|
65
|
+
const isSelected = selected.includes(option.value);
|
|
66
|
+
return (_jsxs(Row, { gap: "xs", className: "cursor-pointer rounded-lg px-3 py-1.5 hover:bg-zinc-50 dark:hover:bg-slate-800 mx-1", onClick: () => toggle(option.value), children: [_jsx("input", { type: selectionMode === "single" ? "radio" : "checkbox", checked: isSelected, onChange: () => toggle(option.value), "aria-label": option.label, className: "h-3.5 w-3.5 flex-shrink-0 cursor-pointer rounded border-zinc-300 text-primary-600 dark:border-slate-600 dark:text-secondary-500" }), _jsx(Span, { className: "flex-1 truncate text-xs text-zinc-700 dark:text-zinc-300", children: option.label }), option.count !== undefined && (_jsx(Span, { className: "flex-shrink-0 text-[10px] tabular-nums text-zinc-400 dark:text-zinc-500", children: option.count }))] }, option.value));
|
|
67
|
+
}), filtered.length === 0 && (_jsx(Text, { className: "px-3 py-2 text-xs text-zinc-400 dark:text-zinc-500", children: "No results" }))] }), hasValue && (_jsx(Div, { className: "border-t border-zinc-100 p-1 dark:border-slate-800", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "w-full py-1 text-xs text-zinc-400 transition-colors hover:text-red-600", onClick: () => { onChange([]); onClear?.(); setDropdownOpen(false); }, children: "Clear" }) }))] }))] }));
|
|
68
|
+
}
|
|
69
|
+
// ── Accordion variant (default) ─────────────────────────────────────────
|
|
47
70
|
return (_jsxs(Div, { className: cn("p-4 border-b border-zinc-200 dark:border-slate-700 last:border-b-0", className), children: [_jsxs(Row, { gap: "xs", children: [_jsxs(Button, { type: "button", onClick: handleToggle, variant: "ghost", size: "sm", className: "flex flex-1 items-center justify-between text-sm font-semibold text-zinc-900 dark:text-zinc-50 py-1 hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsxs(Row, { as: Span, gap: "xs", children: [title, hasValue && (_jsx(Span, { className: "inline-flex items-center justify-center w-5 h-5 text-xs rounded-full bg-emerald-50 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300 ring-1 ring-emerald-600/20 dark:ring-emerald-400/20", children: selected.length }))] }), _jsx("svg", { className: cn("w-4 h-4 text-zinc-500 dark:text-zinc-400 transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), onClear && hasValue && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: "inline-flex items-center justify-center w-5 h-5 p-0 text-zinc-500 dark:text-zinc-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-zinc-100 dark:hover:bg-slate-700 transition-colors rounded-full", "aria-label": "Clear filter", children: _jsx("svg", { className: "w-3 h-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), isCollapsed && hasValue && (_jsxs(Div, { className: "mt-2 flex flex-wrap gap-1", children: [selectedLabels.map((label) => (_jsx(Span, { className: "inline-flex items-center px-2 py-0.5 rounded-full text-xs bg-zinc-100 dark:bg-slate-700 text-zinc-700 dark:text-zinc-300 border border-zinc-200 dark:border-slate-600", children: label }, label))), selected.length > 3 && (_jsxs(Span, { className: "inline-flex items-center px-2 py-0.5 rounded-full text-xs text-zinc-400 dark:text-zinc-500", children: ["+", selected.length - 3, " more"] }))] })), searchable && (_jsx(Div, { className: "mt-2", children: _jsx(Input, { type: "search", placeholder: "Search\u2026", value: search, onChange: (e) => handleSearch(e.target.value), className: "w-full rounded-md border border-zinc-200 dark:border-slate-700 bg-white dark:bg-slate-800/60 px-2.5 py-1.5 text-xs text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-primary-500/20 dark:focus:ring-secondary-400/20" }) })), !isCollapsed && (_jsxs(Div, { className: "mt-3 space-y-1", children: [filtered.map((option) => {
|
|
48
71
|
const isSelected = selected.includes(option.value);
|
|
49
72
|
return (_jsxs(Row, { gap: "2.5", className: "py-1 cursor-pointer group", children: [selectionMode === "single" ? (_jsx("input", { type: "radio", checked: isSelected, onChange: () => toggle(option.value), "aria-label": option.label, className: cn("flex-shrink-0 border-zinc-300 dark:border-slate-600 cursor-pointer", "w-4 h-4 rounded-full text-primary-600 dark:text-secondary-500", "focus:ring-primary-500/30 dark:focus:ring-secondary-400/30") })) : (_jsx("input", { type: "checkbox", checked: isSelected, onChange: () => toggle(option.value), "aria-label": option.label, className: cn("flex-shrink-0 border-zinc-300 dark:border-slate-600 cursor-pointer", "w-4 h-4 rounded text-primary-600 dark:text-secondary-500 checked:bg-primary-600 dark:checked:bg-secondary-500", "focus:ring-primary-500/30 dark:focus:ring-secondary-400/30") })), _jsx(Span, { className: "flex-1 text-sm text-zinc-700 dark:text-zinc-300 group-hover:text-zinc-900 dark:group-hover:text-zinc-100 transition-colors truncate", children: option.label }), option.count !== undefined && (_jsx(Span, { className: "text-xs text-zinc-400 dark:text-zinc-500 tabular-nums flex-shrink-0", children: option.count }))] }, option.value));
|
|
@@ -12,5 +12,8 @@ export interface FeaturedBundlesSectionProps {
|
|
|
12
12
|
description?: string;
|
|
13
13
|
initialItems?: CategoryDocument[];
|
|
14
14
|
className?: string;
|
|
15
|
+
onBuyNow?: (input: {
|
|
16
|
+
bundleSlug: string;
|
|
17
|
+
}) => Promise<unknown>;
|
|
15
18
|
}
|
|
16
|
-
export declare function FeaturedBundlesSection({ title, description, initialItems, className, }: FeaturedBundlesSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare function FeaturedBundlesSection({ title, description, initialItems, className, onBuyNow, }: FeaturedBundlesSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,19 +4,20 @@ import { Badge, Div, Heading, Row, Section, Stack, Text } from "../../../ui";
|
|
|
4
4
|
import { ROUTES } from "../../../next/routing/route-map";
|
|
5
5
|
import { formatCurrency } from "../../../utils/number.formatter";
|
|
6
6
|
import { BUNDLE_COPY, BUNDLE_STOCK_VARIANT, } from "../../../_internal/shared/features/categories/bundle-copy";
|
|
7
|
+
import { BundleBuyNowCta } from "../../categories/components/BundleBuyNowCta";
|
|
7
8
|
const PLACEHOLDER_EMOJI = "📦";
|
|
8
|
-
export function FeaturedBundlesSection({ title = BUNDLE_COPY.featuredDefaultTitle, description, initialItems = [], className = "", }) {
|
|
9
|
+
export function FeaturedBundlesSection({ title = BUNDLE_COPY.featuredDefaultTitle, description, initialItems = [], className = "", onBuyNow, }) {
|
|
9
10
|
const items = initialItems.filter((c) => c.categoryType === "bundle");
|
|
10
|
-
return (_jsx(Section, { className: `py-10 ${className}`, children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "sm", align: "center", justify: "between", className: "flex-wrap", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, className: "text-2xl font-semibold text-zinc-900 dark:text-zinc-100", children: title }), description && (_jsx(Text, { size: "sm", color: "muted", children: description }))] }), _jsx(Link, { href: String(ROUTES.PUBLIC.BUNDLES ?? "/bundles"), className: "text-sm font-medium text-[var(--appkit-color-primary)] hover:underline", children: BUNDLE_COPY.featured.viewAll })] }), items.length === 0 ? (_jsx(Div, { className: "rounded-xl border border-dashed border-zinc-200 py-12 text-center dark:border-zinc-700", children: _jsx(Text, { color: "muted", children: BUNDLE_COPY.featured.empty }) })) : (_jsx(Div, { className: "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4", children: items.map((bundle) => (_jsx(FeaturedBundleCard, { bundle: bundle }, bundle.id))) }))] }) }));
|
|
11
|
+
return (_jsx(Section, { className: `py-10 ${className}`, children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "sm", align: "center", justify: "between", className: "flex-wrap", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, className: "text-2xl font-semibold text-zinc-900 dark:text-zinc-100", children: title }), description && (_jsx(Text, { size: "sm", color: "muted", children: description }))] }), _jsx(Link, { href: String(ROUTES.PUBLIC.BUNDLES ?? "/bundles"), className: "text-sm font-medium text-[var(--appkit-color-primary)] hover:underline", children: BUNDLE_COPY.featured.viewAll })] }), items.length === 0 ? (_jsx(Div, { className: "rounded-xl border border-dashed border-zinc-200 py-12 text-center dark:border-zinc-700", children: _jsx(Text, { color: "muted", children: BUNDLE_COPY.featured.empty }) })) : (_jsx(Div, { className: "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4", children: items.map((bundle) => (_jsx(FeaturedBundleCard, { bundle: bundle, onBuyNow: onBuyNow }, bundle.id))) }))] }) }));
|
|
11
12
|
}
|
|
12
|
-
function FeaturedBundleCard({ bundle }) {
|
|
13
|
+
function FeaturedBundleCard({ bundle, onBuyNow }) {
|
|
13
14
|
const stock = bundle.bundleStockStatus ?? "in_stock";
|
|
14
15
|
const memberCount = bundle.bundleProductIds?.length ?? 0;
|
|
15
16
|
const cover = bundle.display?.coverImage;
|
|
16
17
|
const href = String(ROUTES.PUBLIC.BUNDLE_DETAIL?.(bundle.slug) ?? "#");
|
|
17
|
-
return (_jsxs(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
return (_jsxs(Stack, { gap: "none", className: "rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-900", children: [_jsxs(Link, { href: href, className: "group block p-3 hover:no-underline", children: [_jsx(Div, { className: "mb-2 aspect-square overflow-hidden rounded-lg bg-zinc-100 dark:bg-zinc-800", children: cover ? (
|
|
19
|
+
// eslint-disable-next-line @next/next/no-img-element, lir/no-raw-media-elements
|
|
20
|
+
_jsx("img", { src: cover, alt: bundle.name, className: "h-full w-full object-cover transition-transform group-hover:scale-105", loading: "lazy" })) : (_jsx(Div, { className: "flex h-full w-full items-center justify-center text-3xl", children: PLACEHOLDER_EMOJI })) }), _jsx(Text, { className: "line-clamp-2 text-sm font-semibold", children: bundle.name }), _jsxs(Row, { gap: "sm", align: "center", className: "mt-1 flex-wrap", children: [_jsx(Text, { size: "sm", weight: "bold", children: bundle.bundlePriceInPaise
|
|
21
|
+
? formatCurrency(bundle.bundlePriceInPaise / 100, "INR")
|
|
22
|
+
: BUNDLE_COPY.featured.priceFallback }), _jsxs(Text, { size: "xs", color: "muted", children: ["\u00B7 ", BUNDLE_COPY.featured.itemCount(memberCount)] }), stock !== "in_stock" && (_jsx(Badge, { variant: BUNDLE_STOCK_VARIANT[stock], children: BUNDLE_COPY.stockBadge.listVariantOutOfStock }))] })] }), onBuyNow && (_jsx(Div, { className: "border-t border-zinc-100 p-3 pt-2 dark:border-zinc-800", children: _jsx(BundleBuyNowCta, { bundleSlug: bundle.slug, outOfStock: stock === "out_of_stock", onBuyNow: onBuyNow, compact: true }) }))] }));
|
|
22
23
|
}
|
|
@@ -11,7 +11,8 @@ import { orderRepository } from "../../orders/repository/orders.repository";
|
|
|
11
11
|
import { RefundStatusValues } from "../../orders/schemas";
|
|
12
12
|
import { siteSettingsRepository } from "../../admin/repository/site-settings.repository";
|
|
13
13
|
import { notificationRepository } from "../../admin/repository/notification.repository";
|
|
14
|
-
const
|
|
14
|
+
const DEFAULT_PLATFORM_FEE_PERCENT = 5;
|
|
15
|
+
const DEFAULT_GST_PERCENT = 18;
|
|
15
16
|
export async function issuePartialRefund(adminUid, orderId, deductFees, refundNote) {
|
|
16
17
|
const order = await orderRepository.findById(orderId);
|
|
17
18
|
if (!order)
|
|
@@ -21,11 +22,12 @@ export async function issuePartialRefund(adminUid, orderId, deductFees, refundNo
|
|
|
21
22
|
if (order.refundStatus === RefundStatusValues.COMPLETED)
|
|
22
23
|
throw new ValidationError("Order has already been fully refunded.");
|
|
23
24
|
const settings = await siteSettingsRepository.getSingleton();
|
|
24
|
-
const
|
|
25
|
-
|
|
25
|
+
const platformFeePercent = settings.commissions?.platformFeePercent ?? DEFAULT_PLATFORM_FEE_PERCENT;
|
|
26
|
+
const gstPercent = settings.commissions?.gstPercent ?? DEFAULT_GST_PERCENT;
|
|
27
|
+
const effectiveRate = platformFeePercent * (1 + gstPercent / 100);
|
|
26
28
|
const grossRefund = order.totalPrice;
|
|
27
29
|
const feeDeducted = deductFees
|
|
28
|
-
? parseFloat(((grossRefund *
|
|
30
|
+
? parseFloat(((grossRefund * effectiveRate) / 100).toFixed(2))
|
|
29
31
|
: 0;
|
|
30
32
|
const netRefund = parseFloat((grossRefund - feeDeducted).toFixed(2));
|
|
31
33
|
await orderRepository.update(orderId, {
|
|
@@ -73,10 +75,11 @@ export async function previewCancellationRefund(userId, orderId) {
|
|
|
73
75
|
if (order.paymentStatus !== "paid")
|
|
74
76
|
return null;
|
|
75
77
|
const settings = await siteSettingsRepository.getSingleton();
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
+
const platformFeePercent = settings.commissions?.platformFeePercent ?? DEFAULT_PLATFORM_FEE_PERCENT;
|
|
79
|
+
const gstPercent = settings.commissions?.gstPercent ?? DEFAULT_GST_PERCENT;
|
|
80
|
+
const effectiveRate = platformFeePercent * (1 + gstPercent / 100);
|
|
78
81
|
const grossRefund = order.totalPrice;
|
|
79
|
-
const feeDeducted = parseFloat(((grossRefund *
|
|
82
|
+
const feeDeducted = parseFloat(((grossRefund * effectiveRate) / 100).toFixed(2));
|
|
80
83
|
const netRefund = parseFloat((grossRefund - feeDeducted).toFixed(2));
|
|
81
84
|
return {
|
|
82
85
|
orderId,
|
|
@@ -7,12 +7,12 @@ export type ShippingMethod = "custom" | "shiprocket";
|
|
|
7
7
|
export type RefundType = "full" | "partial";
|
|
8
8
|
export type OrderPayoutStatus = "eligible" | "requested" | "paid";
|
|
9
9
|
export type RefundStatus = "pending" | "processing" | "completed" | "rejected";
|
|
10
|
-
/** Runtime-accessible shipping method values
|
|
10
|
+
/** Runtime-accessible shipping method values â€" use instead of bare string literals. */
|
|
11
11
|
export declare const ShippingMethodValues: {
|
|
12
12
|
readonly CUSTOM: "custom";
|
|
13
13
|
readonly SHIPROCKET: "shiprocket";
|
|
14
14
|
};
|
|
15
|
-
/** Runtime-accessible order status values
|
|
15
|
+
/** Runtime-accessible order status values â€" use instead of bare string literals. */
|
|
16
16
|
export declare const OrderStatusValues: {
|
|
17
17
|
readonly PENDING: "pending";
|
|
18
18
|
readonly CONFIRMED: "confirmed";
|
|
@@ -24,7 +24,7 @@ export declare const OrderStatusValues: {
|
|
|
24
24
|
readonly RETURN_REQUESTED: "return_requested";
|
|
25
25
|
readonly RETURNED: "returned";
|
|
26
26
|
};
|
|
27
|
-
/** Runtime-accessible payment status values
|
|
27
|
+
/** Runtime-accessible payment status values â€" use instead of bare string literals. */
|
|
28
28
|
export declare const PaymentStatusValues: {
|
|
29
29
|
readonly PENDING: "pending";
|
|
30
30
|
readonly PROCESSING: "processing";
|
|
@@ -33,34 +33,35 @@ export declare const PaymentStatusValues: {
|
|
|
33
33
|
readonly REFUNDED: "refunded";
|
|
34
34
|
readonly PARTIAL_REFUND: "partial_refund";
|
|
35
35
|
};
|
|
36
|
-
/** Runtime-accessible payment method values
|
|
36
|
+
/** Runtime-accessible payment method values â€" use instead of bare string literals. */
|
|
37
37
|
export declare const PaymentMethodValues: {
|
|
38
38
|
readonly COD: "cod";
|
|
39
39
|
readonly ONLINE: "online";
|
|
40
40
|
readonly UPI_MANUAL: "upi_manual";
|
|
41
41
|
readonly RAZORPAY: "razorpay";
|
|
42
|
+
readonly ADMIN_BYPASS: "admin_bypass";
|
|
42
43
|
};
|
|
43
|
-
/** Runtime-accessible refund status values
|
|
44
|
+
/** Runtime-accessible refund status values â€" use instead of bare string literals. */
|
|
44
45
|
export declare const RefundStatusValues: {
|
|
45
46
|
readonly PENDING: "pending";
|
|
46
47
|
readonly PROCESSING: "processing";
|
|
47
48
|
readonly COMPLETED: "completed";
|
|
48
49
|
readonly REJECTED: "rejected";
|
|
49
50
|
};
|
|
50
|
-
/** Firestore storage shape for an order line item
|
|
51
|
+
/** Firestore storage shape for an order line item â€" distinct from the API display model OrderItem */
|
|
51
52
|
export interface OrderDocumentItem {
|
|
52
53
|
productId: string;
|
|
53
54
|
productTitle: string;
|
|
54
55
|
quantity: number;
|
|
55
56
|
unitPrice: number;
|
|
56
57
|
totalPrice: number;
|
|
57
|
-
/** SB8-F
|
|
58
|
+
/** SB8-F â€" set when the item is a prize-draw entry; drives the reveals badge. */
|
|
58
59
|
listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live";
|
|
59
|
-
/** SB8-F
|
|
60
|
+
/** SB8-F â€" per-item reveal status; flips through pending â†' open â†' revealed/closed. */
|
|
60
61
|
prizeRevealStatus?: "pending" | "open" | "closed" | "revealed";
|
|
61
|
-
/** SB8-F
|
|
62
|
+
/** SB8-F â€" ISO timestamp; deadline by which the buyer must claim the prize. */
|
|
62
63
|
prizeRevealDeadline?: string;
|
|
63
|
-
/** SB8-F
|
|
64
|
+
/** SB8-F â€" set after the reveal API picks a winner. */
|
|
64
65
|
revealedItemNumber?: number;
|
|
65
66
|
}
|
|
66
67
|
/**
|
|
@@ -163,13 +164,13 @@ export interface OrderDocument {
|
|
|
163
164
|
};
|
|
164
165
|
/** Deadline for the buyer to claim the won prize (typically 7 days). */
|
|
165
166
|
prizeRevealDeadline?: Date;
|
|
166
|
-
/** True once `prizeRevealDeadline` passes without a claim
|
|
167
|
+
/** True once `prizeRevealDeadline` passes without a claim â€" auto-forfeit. */
|
|
167
168
|
prizeRevealExpired?: boolean;
|
|
168
169
|
/** Source product id when the order came from a prize-draw entry. */
|
|
169
170
|
prizeDrawProductId?: string;
|
|
170
171
|
/** True for prize-draw entries and bundle purchases that bypass refund. */
|
|
171
172
|
isNonRefundable?: boolean;
|
|
172
|
-
/** Set when the order came from a bundle
|
|
173
|
+
/** Set when the order came from a bundle â€" points back to `bundles/{id}`. */
|
|
173
174
|
bundleId?: string;
|
|
174
175
|
/**
|
|
175
176
|
* Internal reference linking sibling orders created from the same checkout
|
|
@@ -184,11 +185,13 @@ export interface OrderDocument {
|
|
|
184
185
|
refunds?: OrderRefundEvent[];
|
|
185
186
|
/**
|
|
186
187
|
* False once ANY refund (full or partial) has been posted. Once false it
|
|
187
|
-
* stays false — no dispute, RMA request, or
|
|
188
|
+
* stays false — no dispute, RMA request, or "Item Not Received" claim may
|
|
188
189
|
* be filed. Both buyer and seller must acknowledge this in the refund UI
|
|
189
190
|
* via `confirmIrrevocable: true` before the refund is posted.
|
|
190
191
|
*/
|
|
191
192
|
contestable?: boolean;
|
|
193
|
+
/** UID of the admin who triggered an admin-bypass checkout. Set only when paymentMethod === "admin_bypass". */
|
|
194
|
+
adminBypassBy?: string;
|
|
192
195
|
/** Signed-URL media slug for a shipping-proof document (label, AWB scan). */
|
|
193
196
|
shippingProofUrl?: string;
|
|
194
197
|
shippingProofMimeType?: string;
|
|
@@ -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
|
}
|