@mohasinac/appkit 2.7.30 → 2.7.32
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/client/features/filters/filter-load-options.d.ts +29 -0
- package/dist/_internal/client/features/filters/filter-load-options.js +98 -0
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
- package/dist/_internal/server/features/classified/adapters.js +1 -1
- package/dist/_internal/server/features/digital-code/adapters.js +1 -1
- package/dist/_internal/server/features/live/adapters.js +1 -1
- package/dist/_internal/shared/actions/action-registry.js +194 -1
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/constants/field-names.d.ts +1 -0
- package/dist/constants/field-names.js +1 -0
- package/dist/features/about/components/FAQPageView.js +1 -1
- package/dist/features/about/components/PublicProfileView.js +2 -2
- package/dist/features/account/components/UserSidebar.js +1 -1
- package/dist/features/admin/components/AdminAddressEditorView.d.ts +7 -0
- package/dist/features/admin/components/AdminAddressEditorView.js +117 -0
- package/dist/features/admin/components/AdminCategoryEditorView.js +12 -9
- package/dist/features/admin/components/AdminFeatureFlagsView.js +58 -13
- package/dist/features/admin/components/AdminProductEditorView.d.ts +3 -1
- package/dist/features/admin/components/AdminProductEditorView.js +12 -9
- package/dist/features/admin/components/AdminReturnRequestsView.js +5 -4
- package/dist/features/admin/components/AdminReviewsView.js +5 -4
- package/dist/features/admin/components/AdminSidebar.js +1 -1
- package/dist/features/admin/components/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- package/dist/features/auctions/components/AuctionBidsTable.d.ts +13 -0
- package/dist/features/auctions/components/AuctionBidsTable.js +62 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
- package/dist/features/auctions/components/AuctionFilters.d.ts +7 -1
- package/dist/features/auctions/components/AuctionFilters.js +3 -2
- package/dist/features/auctions/components/index.d.ts +2 -0
- package/dist/features/auctions/components/index.js +1 -0
- package/dist/features/blog/components/BlogPostView.js +1 -1
- package/dist/features/cart/components/CartView.js +2 -1
- package/dist/features/cart/components/CheckoutView.js +2 -1
- package/dist/features/categories/components/BrandDetailTabs.d.ts +5 -1
- package/dist/features/categories/components/BrandDetailTabs.js +29 -15
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +5 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +30 -15
- package/dist/features/classified/components/ClassifiedFilters.d.ts +5 -1
- package/dist/features/classified/components/ClassifiedFilters.js +3 -2
- package/dist/features/digital-codes/components/DigitalCodeFilters.d.ts +5 -1
- package/dist/features/digital-codes/components/DigitalCodeFilters.js +3 -2
- package/dist/features/events/repository/event-entry.repository.d.ts +1 -0
- package/dist/features/events/repository/event-entry.repository.js +5 -0
- package/dist/features/filters/index.d.ts +2 -0
- package/dist/features/filters/index.js +1 -0
- package/dist/features/homepage/components/FAQSection.js +1 -1
- package/dist/features/homepage/components/FeaturedProductsSection.js +6 -1
- package/dist/features/homepage/components/HeroCarousel.js +21 -5
- package/dist/features/homepage/components/SectionCarousel.js +3 -1
- package/dist/features/homepage/components/ShopByCategorySection.js +2 -2
- package/dist/features/layout/AppLayoutShell.d.ts +1 -1
- package/dist/features/layout/AppLayoutShell.js +1 -1
- package/dist/features/live/components/LiveItemFilters.d.ts +5 -1
- package/dist/features/live/components/LiveItemFilters.js +3 -2
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +2 -2
- package/dist/features/pre-orders/components/PreOrderFilters.d.ts +7 -1
- package/dist/features/pre-orders/components/PreOrderFilters.js +3 -2
- package/dist/features/products/columns/productTableColumns.js +1 -1
- package/dist/features/products/components/CompareOverlay.d.ts +2 -0
- package/dist/features/products/components/CompareOverlay.js +1 -1
- package/dist/features/products/components/ProductFilters.d.ts +7 -1
- package/dist/features/products/components/ProductFilters.js +3 -2
- package/dist/features/products/components/ProductForm.js +6 -6
- package/dist/features/products/components/ProductGrid.js +1 -1
- package/dist/features/products/repository/products.repository.d.ts +4 -0
- package/dist/features/products/repository/products.repository.js +22 -3
- package/dist/features/products/schemas/firestore.d.ts +7 -1
- package/dist/features/products/types/index.d.ts +4 -0
- package/dist/features/reviews/repository/reviews.repository.d.ts +1 -0
- package/dist/features/reviews/repository/reviews.repository.js +14 -3
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/stores/components/StoreAboutView.js +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +16 -2
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/next/routing/route-map.d.ts +14 -0
- package/dist/next/routing/route-map.js +7 -0
- package/dist/seed/bids-seed-data.js +7 -4
- package/dist/seed/events-seed-data.js +23 -0
- package/dist/seed/history-seed-data.js +13 -0
- package/dist/seed/orders-seed-data.js +108 -1
- package/dist/seed/products-auctions-seed-data.js +40 -40
- package/dist/seed/products-preorders-seed-data.js +16 -16
- package/dist/seed/products-prize-draws-seed-data.js +4 -4
- package/dist/seed/products-standard-seed-data.js +202 -202
- package/dist/tailwind-utilities.css +1 -1
- package/package.json +1 -1
|
@@ -12,25 +12,39 @@ function tabLabel(label, count) {
|
|
|
12
12
|
return label;
|
|
13
13
|
return `${label} (${count.toLocaleString()})`;
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const TAB_TYPE_MAP = {
|
|
16
|
+
products: { kind: "listing", type: "standard" },
|
|
17
|
+
auctions: { kind: "listing", type: "auction" },
|
|
18
|
+
"pre-orders": { kind: "listing", type: "pre-order" },
|
|
19
|
+
"prize-draws": { kind: "listing", type: "prize-draw" },
|
|
20
|
+
bundles: { kind: "category", type: "bundle" },
|
|
21
|
+
};
|
|
22
|
+
export function BrandDetailTabs({ brandName, initialProductsData, initialBundles = [], counts, enabledListingTypes, enabledCategoryTypes, }) {
|
|
23
|
+
const visibleTabs = CATEGORY_PAGE_TABS.filter((t) => {
|
|
24
|
+
const mapping = TAB_TYPE_MAP[t.id];
|
|
25
|
+
if (!mapping)
|
|
26
|
+
return true;
|
|
27
|
+
if (mapping.kind === "listing" && enabledListingTypes) {
|
|
28
|
+
return enabledListingTypes.includes(mapping.type);
|
|
29
|
+
}
|
|
30
|
+
if (mapping.kind === "category" && enabledCategoryTypes) {
|
|
31
|
+
return enabledCategoryTypes.includes(mapping.type);
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
});
|
|
35
|
+
const firstTabId = (visibleTabs[0]?.id ?? "products");
|
|
36
|
+
const [activeTab, setActiveTab] = useState(firstTabId);
|
|
17
37
|
const countFor = (id) => {
|
|
18
38
|
switch (id) {
|
|
19
|
-
case "products":
|
|
20
|
-
|
|
21
|
-
case "
|
|
22
|
-
|
|
23
|
-
case "
|
|
24
|
-
|
|
25
|
-
case "prize-draws":
|
|
26
|
-
return counts?.prizeDraws;
|
|
27
|
-
case "bundles":
|
|
28
|
-
return counts?.bundles;
|
|
29
|
-
default:
|
|
30
|
-
return undefined;
|
|
39
|
+
case "products": return counts?.products;
|
|
40
|
+
case "auctions": return counts?.auctions;
|
|
41
|
+
case "pre-orders": return counts?.preOrders;
|
|
42
|
+
case "prize-draws": return counts?.prizeDraws;
|
|
43
|
+
case "bundles": return counts?.bundles;
|
|
44
|
+
default: return undefined;
|
|
31
45
|
}
|
|
32
46
|
};
|
|
33
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex border-b border-zinc-200 dark:border-slate-700 mb-6 overflow-x-auto", children:
|
|
47
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex border-b border-zinc-200 dark:border-slate-700 mb-6 overflow-x-auto", children: visibleTabs.map((t) => (_jsx("button", { type: "button", onClick: () => setActiveTab(t.id), className: `px-5 py-2.5 text-sm font-medium whitespace-nowrap transition-colors -mb-px border-b-2 ${activeTab === t.id
|
|
34
48
|
? "border-primary text-primary"
|
|
35
49
|
: "border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"}`, children: tabLabel(t.label, countFor(t.id)) }, t.id))) }), activeTab === "products" && (_jsx(CategoryProductsListing, { categorySlug: "", brandName: brandName, initialData: initialProductsData })), activeTab === "auctions" && (_jsx(AuctionsIndexListing, { brandName: brandName })), activeTab === "pre-orders" && (_jsx(PreOrdersIndexListing, { brandName: brandName })), activeTab === "prize-draws" && (_jsx(PrizeDrawsIndexListing, { brandName: brandName })), activeTab === "bundles" && (_jsx(CategoryBundlesListing, { initialBundles: initialBundles, brandName: brandName }))] }));
|
|
36
50
|
}
|
|
@@ -11,5 +11,9 @@ export interface CategoryDetailTabsProps {
|
|
|
11
11
|
prizeDraws?: number;
|
|
12
12
|
bundles?: number;
|
|
13
13
|
};
|
|
14
|
+
/** Enabled listing types (e.g. ["standard","auction","pre-order"]). When omitted, all tabs shown. */
|
|
15
|
+
enabledListingTypes?: string[];
|
|
16
|
+
/** Enabled category types (e.g. ["category","brand","bundle"]). When omitted, all tabs shown. */
|
|
17
|
+
enabledCategoryTypes?: string[];
|
|
14
18
|
}
|
|
15
|
-
export declare function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, initialBundles, counts, }: CategoryDetailTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, initialBundles, counts, enabledListingTypes, enabledCategoryTypes, }: CategoryDetailTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -12,25 +12,40 @@ function tabLabel(label, count) {
|
|
|
12
12
|
return label;
|
|
13
13
|
return `${label} (${count.toLocaleString()})`;
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/** Maps CATEGORY_PAGE_TABS id → listing type / category type key for flag filtering. */
|
|
16
|
+
const TAB_TYPE_MAP = {
|
|
17
|
+
products: { kind: "listing", type: "standard" },
|
|
18
|
+
auctions: { kind: "listing", type: "auction" },
|
|
19
|
+
"pre-orders": { kind: "listing", type: "pre-order" },
|
|
20
|
+
"prize-draws": { kind: "listing", type: "prize-draw" },
|
|
21
|
+
bundles: { kind: "category", type: "bundle" },
|
|
22
|
+
};
|
|
23
|
+
export function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, initialBundles = [], counts, enabledListingTypes, enabledCategoryTypes, }) {
|
|
24
|
+
const visibleTabs = CATEGORY_PAGE_TABS.filter((t) => {
|
|
25
|
+
const mapping = TAB_TYPE_MAP[t.id];
|
|
26
|
+
if (!mapping)
|
|
27
|
+
return true;
|
|
28
|
+
if (mapping.kind === "listing" && enabledListingTypes) {
|
|
29
|
+
return enabledListingTypes.includes(mapping.type);
|
|
30
|
+
}
|
|
31
|
+
if (mapping.kind === "category" && enabledCategoryTypes) {
|
|
32
|
+
return enabledCategoryTypes.includes(mapping.type);
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
36
|
+
const firstTabId = (visibleTabs[0]?.id ?? "products");
|
|
37
|
+
const [activeTab, setActiveTab] = useState(firstTabId);
|
|
17
38
|
const countFor = (id) => {
|
|
18
39
|
switch (id) {
|
|
19
|
-
case "products":
|
|
20
|
-
|
|
21
|
-
case "
|
|
22
|
-
|
|
23
|
-
case "
|
|
24
|
-
|
|
25
|
-
case "prize-draws":
|
|
26
|
-
return counts?.prizeDraws;
|
|
27
|
-
case "bundles":
|
|
28
|
-
return counts?.bundles;
|
|
29
|
-
default:
|
|
30
|
-
return undefined;
|
|
40
|
+
case "products": return counts?.products;
|
|
41
|
+
case "auctions": return counts?.auctions;
|
|
42
|
+
case "pre-orders": return counts?.preOrders;
|
|
43
|
+
case "prize-draws": return counts?.prizeDraws;
|
|
44
|
+
case "bundles": return counts?.bundles;
|
|
45
|
+
default: return undefined;
|
|
31
46
|
}
|
|
32
47
|
};
|
|
33
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex border-b border-zinc-200 dark:border-slate-700 mb-6 overflow-x-auto", children:
|
|
48
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex border-b border-zinc-200 dark:border-slate-700 mb-6 overflow-x-auto", children: visibleTabs.map((t) => (_jsx("button", { type: "button", onClick: () => setActiveTab(t.id), className: `px-5 py-2.5 text-sm font-medium whitespace-nowrap transition-colors -mb-px border-b-2 ${activeTab === t.id
|
|
34
49
|
? "border-primary text-primary"
|
|
35
50
|
: "border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"}`, children: tabLabel(t.label, countFor(t.id)) }, t.id))) }), activeTab === "products" && (_jsx(CategoryProductsListing, { categorySlug: categorySlug, categoryId: categoryId, initialData: initialProductsData })), activeTab === "auctions" && (_jsx(AuctionsIndexListing, { categorySlug: categorySlug })), activeTab === "pre-orders" && (_jsx(PreOrdersIndexListing, { categorySlug: categorySlug })), activeTab === "prize-draws" && (_jsx(PrizeDrawsIndexListing, { categorySlug: categorySlug })), activeTab === "bundles" && (_jsx(CategoryBundlesListing, { initialBundles: initialBundles }))] }));
|
|
36
51
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { FacetOption } from "../../filters/FilterFacetSection";
|
|
2
2
|
import type { UrlTable } from "../../filters/FilterPanel";
|
|
3
|
+
import type { AsyncFacetSectionProps } from "../../filters/AsyncFacetSection";
|
|
4
|
+
type LoadOptionsFn = AsyncFacetSectionProps["loadOptions"];
|
|
3
5
|
export interface ClassifiedFiltersProps {
|
|
4
6
|
table: UrlTable;
|
|
5
7
|
categoryOptions?: FacetOption[];
|
|
8
|
+
loadCategoryOptions?: LoadOptionsFn;
|
|
6
9
|
currencyPrefix?: string;
|
|
7
10
|
}
|
|
8
|
-
export declare function ClassifiedFilters({ table, categoryOptions, currencyPrefix, }: ClassifiedFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function ClassifiedFilters({ table, categoryOptions, loadCategoryOptions, currencyPrefix, }: ClassifiedFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { FilterFacetSection } from "../../filters/FilterFacetSection";
|
|
4
|
+
import { AsyncFacetSection } from "../../filters/AsyncFacetSection";
|
|
4
5
|
import { RangeFilter } from "../../filters/RangeFilter";
|
|
5
6
|
import { Div } from "../../../ui";
|
|
6
7
|
const NEGOTIABLE_OPTIONS = [
|
|
@@ -9,11 +10,11 @@ const NEGOTIABLE_OPTIONS = [
|
|
|
9
10
|
const SHIPPING_OPTIONS = [
|
|
10
11
|
{ value: "true", label: "Accepts shipping" },
|
|
11
12
|
];
|
|
12
|
-
export function ClassifiedFilters({ table, categoryOptions = [], currencyPrefix = "₹", }) {
|
|
13
|
+
export function ClassifiedFilters({ table, categoryOptions = [], loadCategoryOptions, currencyPrefix = "₹", }) {
|
|
13
14
|
const selectedCategories = table.get("category")
|
|
14
15
|
? table.get("category").split("|").filter(Boolean)
|
|
15
16
|
: [];
|
|
16
17
|
const selectedNegotiable = table.get("negotiable") ? [table.get("negotiable")] : [];
|
|
17
18
|
const selectedShipping = table.get("acceptsShipping") ? [table.get("acceptsShipping")] : [];
|
|
18
|
-
return (_jsxs(Div, { children: [categoryOptions.length > 0
|
|
19
|
+
return (_jsxs(Div, { children: [loadCategoryOptions ? (_jsx(AsyncFacetSection, { title: "Category", loadOptions: loadCategoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), defaultCollapsed: false })) : categoryOptions.length > 0 ? (_jsx(FilterFacetSection, { title: "Category", options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), searchable: true, defaultCollapsed: categoryOptions.length > 6 })) : null, _jsxs("div", { className: "border-t border-[var(--appkit-color-border)] pt-4 mt-4", children: [_jsx("label", { className: "block text-sm font-medium text-[var(--appkit-color-text)] mb-2", children: "City / Area" }), _jsx("input", { type: "text", value: table.get("city") || "", onChange: (e) => table.set("city", e.target.value), placeholder: "e.g. Mumbai", className: "w-full rounded-lg border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-3 py-2 text-sm text-[var(--appkit-color-text)] placeholder-[var(--appkit-color-text-faint)] focus:outline-none focus:ring-2 focus:ring-[var(--appkit-color-primary)]" })] }), _jsx(FilterFacetSection, { title: "Negotiable", options: NEGOTIABLE_OPTIONS, selected: selectedNegotiable, onChange: (vals) => table.set("negotiable", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(FilterFacetSection, { title: "Shipping", options: SHIPPING_OPTIONS, selected: selectedShipping, onChange: (vals) => table.set("acceptsShipping", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(RangeFilter, { title: "Asking Price", minValue: table.get("minPrice"), maxValue: table.get("maxPrice"), onMinChange: (v) => table.set("minPrice", v), onMaxChange: (v) => table.set("maxPrice", v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 100, minPlaceholder: "Min price", maxPlaceholder: "Max price", defaultCollapsed: false })] }));
|
|
19
20
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { FacetOption } from "../../filters/FilterFacetSection";
|
|
2
2
|
import type { UrlTable } from "../../filters/FilterPanel";
|
|
3
|
+
import type { AsyncFacetSectionProps } from "../../filters/AsyncFacetSection";
|
|
4
|
+
type LoadOptionsFn = AsyncFacetSectionProps["loadOptions"];
|
|
3
5
|
export interface DigitalCodeFiltersProps {
|
|
4
6
|
table: UrlTable;
|
|
5
7
|
categoryOptions?: FacetOption[];
|
|
8
|
+
loadCategoryOptions?: LoadOptionsFn;
|
|
6
9
|
currencyPrefix?: string;
|
|
7
10
|
}
|
|
8
|
-
export declare function DigitalCodeFilters({ table, categoryOptions, currencyPrefix, }: DigitalCodeFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function DigitalCodeFilters({ table, categoryOptions, loadCategoryOptions, currencyPrefix, }: DigitalCodeFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { FilterFacetSection } from "../../filters/FilterFacetSection";
|
|
4
|
+
import { AsyncFacetSection } from "../../filters/AsyncFacetSection";
|
|
4
5
|
import { RangeFilter } from "../../filters/RangeFilter";
|
|
5
6
|
import { Div } from "../../../ui";
|
|
6
7
|
const DELIVERY_OPTIONS = [
|
|
7
8
|
{ value: "auto-claim", label: "Instant (auto-claim)" },
|
|
8
9
|
{ value: "manual-email", label: "Manual (sent by seller)" },
|
|
9
10
|
];
|
|
10
|
-
export function DigitalCodeFilters({ table, categoryOptions = [], currencyPrefix = "₹", }) {
|
|
11
|
+
export function DigitalCodeFilters({ table, categoryOptions = [], loadCategoryOptions, currencyPrefix = "₹", }) {
|
|
11
12
|
const selectedCategories = table.get("category")
|
|
12
13
|
? table.get("category").split("|").filter(Boolean)
|
|
13
14
|
: [];
|
|
14
15
|
const selectedDelivery = table.get("deliveryMethod") ? [table.get("deliveryMethod")] : [];
|
|
15
|
-
return (_jsxs(Div, { children: [categoryOptions.length > 0
|
|
16
|
+
return (_jsxs(Div, { children: [loadCategoryOptions ? (_jsx(AsyncFacetSection, { title: "Category", loadOptions: loadCategoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), defaultCollapsed: false })) : categoryOptions.length > 0 ? (_jsx(FilterFacetSection, { title: "Category", options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), searchable: true, defaultCollapsed: categoryOptions.length > 6 })) : null, _jsx(FilterFacetSection, { title: "Delivery Method", options: DELIVERY_OPTIONS, selected: selectedDelivery, onChange: (vals) => table.set("deliveryMethod", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(RangeFilter, { title: "Price per Code", minValue: table.get("minPrice"), maxValue: table.get("maxPrice"), onMinChange: (v) => table.set("minPrice", v), onMaxChange: (v) => table.set("maxPrice", v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 50000, step: 50, minPlaceholder: "Min price", maxPlaceholder: "Max price", defaultCollapsed: false })] }));
|
|
16
17
|
}
|
|
@@ -6,6 +6,7 @@ declare class EventEntryRepository extends BaseRepository<EventEntryDocument> {
|
|
|
6
6
|
constructor();
|
|
7
7
|
protected mapDoc<D = EventEntryDocument>(snap: import("../../../providers/db-firebase").DocumentSnapshot): D;
|
|
8
8
|
listForEvent(eventId: string, model: SieveModel): Promise<FirebaseSieveResult<EventEntryDocument>>;
|
|
9
|
+
listForUser(userId: string, model: SieveModel): Promise<FirebaseSieveResult<EventEntryDocument>>;
|
|
9
10
|
hasUserEntered(eventId: string, userId: string): Promise<boolean>;
|
|
10
11
|
countUserEntries(eventId: string, userId: string): Promise<number>;
|
|
11
12
|
getLeaderboard(eventId: string, limit?: number): Promise<EventEntryDocument[]>;
|
|
@@ -18,6 +18,11 @@ class EventEntryRepository extends BaseRepository {
|
|
|
18
18
|
baseQuery: this.getCollection().where(EVENT_ENTRY_FIELDS.EVENT_ID, "==", eventId),
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
+
async listForUser(userId, model) {
|
|
22
|
+
return this.sieveQuery(model, EventEntryRepository.SIEVE_FIELDS, {
|
|
23
|
+
baseQuery: this.getCollection().where(EVENT_ENTRY_FIELDS.USER_ID, "==", userId),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
21
26
|
async hasUserEntered(eventId, userId) {
|
|
22
27
|
try {
|
|
23
28
|
const snapshot = await this.getCollection()
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { FilterFacetSection } from "./FilterFacetSection";
|
|
2
2
|
export type { FilterFacetSectionProps, FacetOption, } from "./FilterFacetSection";
|
|
3
|
+
export { AsyncFacetSection } from "./AsyncFacetSection";
|
|
4
|
+
export type { AsyncFacetSectionProps } from "./AsyncFacetSection";
|
|
3
5
|
export { RangeFilter } from "./RangeFilter";
|
|
4
6
|
export type { RangeFilterProps } from "./RangeFilter";
|
|
5
7
|
export { SwitchFilter } from "./SwitchFilter";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Public API for @mohasinac/feat-filters
|
|
2
2
|
export { FilterFacetSection } from "./FilterFacetSection";
|
|
3
|
+
export { AsyncFacetSection } from "./AsyncFacetSection";
|
|
3
4
|
export { RangeFilter } from "./RangeFilter";
|
|
4
5
|
export { SwitchFilter } from "./SwitchFilter";
|
|
5
6
|
export { FilterPanel } from "./FilterPanel";
|
|
@@ -65,6 +65,6 @@ export function FAQSection({ title, subtitle, items, tabs: tabsProp, showCategor
|
|
|
65
65
|
}, [items, showTabs, activeTab]);
|
|
66
66
|
return (_jsx(Section, { className: `py-16 px-4 sm:px-8 ${themed.bgSecondary} ${className}`, children: _jsxs("div", { className: "w-full max-w-4xl mx-auto", "data-section": "faqsection-div-320", children: [_jsxs("div", { className: "text-center mb-10", "data-section": "faqsection-div-321", children: [_jsx(Heading, { level: 2, variant: "none", className: "bg-gradient-to-r from-primary to-cobalt bg-clip-text text-transparent text-3xl md:text-4xl font-bold mb-3", children: title }), subtitle && (_jsx(Text, { className: `text-base max-w-xl mx-auto ${themed.textSecondary}`, children: subtitle }))] }), showTabs && (_jsxs("div", { className: `${flex.center} flex-wrap gap-2 mb-8`, "data-section": "faqsection-div-322", children: [_jsx(Button, { variant: activeTab === "all" ? "primary" : "ghost", size: "sm", onClick: () => setActiveTab("all"), className: "rounded-full", children: "All" }), derivedTabs.map((tab) => (_jsx(Button, { variant: activeTab === tab.value ? "primary" : "ghost", size: "sm", onClick: () => setActiveTab(tab.value), className: "rounded-full", children: tab.label }, tab.value)))] })), _jsxs("div", { className: "space-y-2", "data-section": "faqsection-div-323", children: [visibleItems.length === 0 && (_jsx(Text, { className: `text-center py-12 ${themed.textSecondary}`, children: "No FAQs in this category yet." })), visibleItems.map((faq) => {
|
|
67
67
|
const isOpen = openIds.has(faq.id);
|
|
68
|
-
return (_jsxs("div", { className: `${themed.bgPrimary} rounded-xl overflow-hidden border transition-all duration-200 ${isOpen ? "border-primary/40 shadow-sm" : `${themed.border}`}`, "data-section": "faqsection-div-324", children: [_jsxs(Button, { variant: "ghost", className: `w-full text-left px-5 py-4 ${flex.between} gap-4 hover:${themed.bgTertiary} transition-colors rounded-none`, onClick: () => toggle(faq.id), "aria-expanded": isOpen, children: [_jsx(Span, { className: `text-sm font-medium flex-1 leading-snug ${isOpen ? "text-primary" : themed.textPrimary}`, children: faq.question }), _jsx(ChevronDown, { className: `w-4 h-4 flex-shrink-0 transition-transform duration-250 ${isOpen ? "rotate-180 text-primary" : themed.textSecondary}` })] }), _jsx("div", { className: `grid transition-all duration-300 ease-out ${isOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"}`, "data-section": "faqsection-div-325", children: _jsx("div", { className: "overflow-hidden", "data-section": "faqsection-div-326", children: _jsx("div", { className: "px-5 pb-5 pt-0", children: isHtml(faq.answer) ? (_jsx(RichText, { html: faq.answer, proseClass: "prose prose-sm max-w-none dark:prose-invert"
|
|
68
|
+
return (_jsxs("div", { className: `${themed.bgPrimary} rounded-xl overflow-hidden border transition-all duration-200 ${isOpen ? "border-primary/40 shadow-sm" : `${themed.border}`}`, "data-section": "faqsection-div-324", children: [_jsxs(Button, { variant: "ghost", className: `w-full text-left px-5 py-4 ${flex.between} gap-4 hover:${themed.bgTertiary} transition-colors rounded-none`, onClick: () => toggle(faq.id), "aria-expanded": isOpen, children: [_jsx(Span, { className: `text-sm font-medium flex-1 leading-snug ${isOpen ? "text-primary" : themed.textPrimary}`, children: faq.question }), _jsx(ChevronDown, { className: `w-4 h-4 flex-shrink-0 transition-transform duration-250 ${isOpen ? "rotate-180 text-primary" : themed.textSecondary}` })] }), _jsx("div", { className: `grid transition-all duration-300 ease-out ${isOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"}`, "data-section": "faqsection-div-325", children: _jsx("div", { className: "overflow-hidden", "data-section": "faqsection-div-326", children: _jsx("div", { className: "px-5 pb-5 pt-0", children: isHtml(faq.answer) ? (_jsx(RichText, { html: faq.answer, proseClass: "prose prose-sm max-w-none dark:prose-invert" })) : (_jsx(Text, { className: `text-sm leading-relaxed ${themed.textSecondary}`, children: faq.answer })) }) }) })] }, faq.id));
|
|
69
69
|
})] }), viewMoreHref && (_jsx("div", { className: "text-center mt-10", "data-section": "faqsection-div-327", children: _jsxs(TextLink, { href: viewMoreHref, className: "inline-flex items-center gap-2 px-6 py-2.5 rounded-full text-sm font-medium bg-primary/10 text-primary hover:bg-primary/20 dark:bg-primary/15 transition-colors", children: [viewMoreLabel, hasMore && moreCount > 0 && (_jsxs(Span, { className: "bg-primary text-white text-xs px-2 py-0.5 rounded-full", children: ["+", moreCount] }))] }) }))] }) }));
|
|
70
70
|
}
|
|
@@ -4,6 +4,7 @@ import { useState, useEffect, useRef } from "react";
|
|
|
4
4
|
import Link from "next/link";
|
|
5
5
|
import { SectionCarousel } from "./SectionCarousel";
|
|
6
6
|
import { useFeaturedProducts } from "../hooks/useFeaturedProducts";
|
|
7
|
+
import { useMediaQuery } from "../../../react";
|
|
7
8
|
import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
|
|
8
9
|
import { ROUTES } from "../../../next";
|
|
9
10
|
import { Section, Heading, Text } from "../../../ui";
|
|
@@ -11,7 +12,11 @@ import { THEME_CONSTANTS } from "../../../tokens";
|
|
|
11
12
|
// --- Multi-row grid + pagination -----------------------------------------------
|
|
12
13
|
function ProductGrid({ items, rows, maxItems, pagination, viewMoreHref, viewMoreLabel, title, description, }) {
|
|
13
14
|
const { themed } = THEME_CONSTANTS;
|
|
14
|
-
|
|
15
|
+
// On mobile (<sm, 2-col grid) a second row doubles the section height to ~2 screens.
|
|
16
|
+
// Collapse to 1 row automatically so the section fits within one viewport.
|
|
17
|
+
const isMobile = useMediaQuery("(max-width: 639px)");
|
|
18
|
+
const effectiveRows = isMobile ? 1 : rows;
|
|
19
|
+
const pageSize = Math.min(effectiveRows * 5, maxItems);
|
|
15
20
|
const [offset, setOffset] = useState(0);
|
|
16
21
|
const timerRef = useRef(null);
|
|
17
22
|
const totalItems = Math.min(items.length, maxItems);
|
|
@@ -43,11 +43,27 @@ function getHoverClass(effect) {
|
|
|
43
43
|
return "";
|
|
44
44
|
}
|
|
45
45
|
function getSlideHeightClass(height) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (height === "
|
|
49
|
-
return
|
|
50
|
-
|
|
46
|
+
// BN-1 (fixed bottom nav) = h-16 = 4rem, shown only below lg — must be subtracted on mobile.
|
|
47
|
+
// --header-height is the measured sticky header height set by AppLayoutShell.
|
|
48
|
+
if (height === "viewport") {
|
|
49
|
+
return [
|
|
50
|
+
// mobile: full dvh minus header minus BN-1
|
|
51
|
+
"min-h-[calc(100dvh-var(--header-height,3.5rem)-4rem)]",
|
|
52
|
+
// desktop: full dvh minus header only (no BN-1)
|
|
53
|
+
"lg:min-h-[calc(100dvh-var(--header-height,3.5rem))]",
|
|
54
|
+
].join(" ");
|
|
55
|
+
}
|
|
56
|
+
if (height === "tall") {
|
|
57
|
+
return [
|
|
58
|
+
"min-h-[calc(72dvh-var(--header-height,3.5rem)-4rem)]",
|
|
59
|
+
"lg:min-h-[clamp(480px,76dvh,840px)]",
|
|
60
|
+
].join(" ");
|
|
61
|
+
}
|
|
62
|
+
// medium (default) — comfortable hero without forcing full-screen
|
|
63
|
+
return [
|
|
64
|
+
"min-h-[calc(52dvh-var(--header-height,3.5rem)-4rem)]",
|
|
65
|
+
"lg:min-h-[clamp(420px,65dvh,680px)]",
|
|
66
|
+
].join(" ");
|
|
51
67
|
}
|
|
52
68
|
/** Renders the slide background — image, video, solid colour, or gradient. */
|
|
53
69
|
function SlideBackground({ bg, legacy, mobileLegacy, isMobile, priority, }) {
|
|
@@ -24,7 +24,9 @@ export function SectionCarousel({ title, description, headingVariant = "editoria
|
|
|
24
24
|
return (_jsxs(Section, { className: [
|
|
25
25
|
"relative overflow-hidden w-full",
|
|
26
26
|
hasBg ? "" : themed.bgSecondary,
|
|
27
|
-
|
|
27
|
+
// Vertical padding: 5rem on desktop, 3rem on mobile to leave room for adjacent sections.
|
|
28
|
+
// Horizontal: 1rem mobile → 2rem desktop; handled via inner max-w container.
|
|
29
|
+
"py-10 px-4 md:py-12 md:px-8",
|
|
28
30
|
className,
|
|
29
31
|
]
|
|
30
32
|
.filter(Boolean)
|
|
@@ -15,7 +15,7 @@ function CategoryChip({ category }) {
|
|
|
15
15
|
const coverImage = category.display?.coverImage;
|
|
16
16
|
const initial = category.name[0]?.toUpperCase() ?? "?";
|
|
17
17
|
const productCount = category.metrics?.productCount ?? 0;
|
|
18
|
-
return (_jsxs(Link, { href: ROUTES.PUBLIC.CATEGORY_DETAIL(category.slug), className: "group flex w-full min-h-[220px] flex-col overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-sm transition-all hover:border-primary-300 hover:shadow-md dark:border-slate-700 dark:bg-slate-900 dark:hover:border-primary-600", children: [coverImage && isImageUrl(coverImage) ? (_jsx("div", { className: "aspect-video w-full overflow-hidden bg-zinc-100 dark:bg-slate-800", children: _jsx(Image, { src: coverImage, alt: category.name, width: 320, height: 180, className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" }) })) : (_jsx("div", { className: "aspect-video w-full bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700" })), _jsxs("div", { className: "flex flex-1 flex-col p-3 text-left", children: [_jsx("div", { className: "mb-2 flex h-9 w-9 items-center justify-center rounded-lg bg-primary-100 text-sm font-bold text-primary-700 dark:bg-primary-900 dark:text-primary-300", children: iconSrc && isImageUrl(iconSrc) ? (_jsx(Image, { src: iconSrc, alt: "", width: 24, height: 24, className: "h-6 w-6 rounded object-cover", "aria-hidden": true })) : iconSrc ? (_jsx("span", { "aria-hidden": "true", className: "text-lg leading-none", children: iconSrc })) : (initial) }), _jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-800 dark:text-zinc-200`, children: category.name }), _jsxs(Text, { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: [productCount.toLocaleString(), " items"] }), _jsx(Text, { className: "mt-auto pt-3 text-xs font-medium text-primary dark:text-primary-400", children: "Browse category \u2192" })] })] }));
|
|
18
|
+
return (_jsxs(Link, { href: ROUTES.PUBLIC.CATEGORY_DETAIL(category.slug), className: "group flex w-full min-h-[180px] sm:min-h-[220px] flex-col overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-sm transition-all hover:border-primary-300 hover:shadow-md dark:border-slate-700 dark:bg-slate-900 dark:hover:border-primary-600", children: [coverImage && isImageUrl(coverImage) ? (_jsx("div", { className: "aspect-video w-full overflow-hidden bg-zinc-100 dark:bg-slate-800", children: _jsx(Image, { src: coverImage, alt: category.name, width: 320, height: 180, className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" }) })) : (_jsx("div", { className: "aspect-video w-full bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700" })), _jsxs("div", { className: "flex flex-1 flex-col p-3 text-left", children: [_jsx("div", { className: "mb-2 flex h-9 w-9 items-center justify-center rounded-lg bg-primary-100 text-sm font-bold text-primary-700 dark:bg-primary-900 dark:text-primary-300", children: iconSrc && isImageUrl(iconSrc) ? (_jsx(Image, { src: iconSrc, alt: "", width: 24, height: 24, className: "h-6 w-6 rounded object-cover", "aria-hidden": true })) : iconSrc ? (_jsx("span", { "aria-hidden": "true", className: "text-lg leading-none", children: iconSrc })) : (initial) }), _jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-800 dark:text-zinc-200`, children: category.name }), _jsxs(Text, { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: [productCount.toLocaleString(), " items"] }), _jsx(Text, { className: "mt-auto pt-3 text-xs font-medium text-primary dark:text-primary-400", children: "Browse category \u2192" })] })] }));
|
|
19
19
|
}
|
|
20
20
|
const CTA_CLASSES = {
|
|
21
21
|
filled: "rounded-lg bg-[var(--appkit-color-primary)] px-5 py-2 text-sm font-semibold text-white hover:opacity-90",
|
|
@@ -56,7 +56,7 @@ export function ShopByCategorySection({ title = "Shop by Category", subtitle, li
|
|
|
56
56
|
const categories = filtered.length > 0 ? filtered : allCategories;
|
|
57
57
|
if (!isLoading && allCategories.length === 0)
|
|
58
58
|
return null;
|
|
59
|
-
return (_jsx(Section, { className: `py-
|
|
59
|
+
return (_jsx(Section, { className: `py-10 px-4 md:py-12 ${themed.bgSecondary} ${className}`, children: _jsxs("div", { className: "mx-auto max-w-7xl", children: [_jsxs("div", { className: "mb-6 text-center", children: [_jsx(Heading, { level: 2, className: `text-2xl font-bold md:text-3xl ${themed.textPrimary}`, children: title }), subtitle && (_jsx(Text, { variant: "secondary", className: "mt-1 text-sm", children: subtitle }))] }), hasFilters && !isLoading && (_jsxs("div", { className: "mb-4 flex flex-wrap gap-2", children: [_jsx(FilterChip, { label: "All", active: activeFilter === "all", onClick: () => setActiveFilter("all") }), rootIds.slice(0, 5).map((id) => {
|
|
60
60
|
const cat = allCategories.find((c) => c.id === id);
|
|
61
61
|
if (!cat)
|
|
62
62
|
return null;
|
|
@@ -90,7 +90,7 @@ export interface AppLayoutShellProps {
|
|
|
90
90
|
eventBannerSlot?: React.ReactNode;
|
|
91
91
|
/**
|
|
92
92
|
* Override className for the main content wrapper div.
|
|
93
|
-
* When set, replaces the default `
|
|
93
|
+
* When set, replaces the default `w-full px-4 py-6 …`
|
|
94
94
|
* Use `"w-full"` for dashboard layouts that provide their own padding via DashboardLayoutClient.
|
|
95
95
|
*/
|
|
96
96
|
contentClassName?: string;
|
|
@@ -147,5 +147,5 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
147
147
|
opacity: darkBackground.overlay?.opacity ?? 0,
|
|
148
148
|
},
|
|
149
149
|
};
|
|
150
|
-
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsxs(Div, { className: "flex min-h-screen w-full flex-col overflow-x-clip transition-colors duration-300", children: [_jsx(BackgroundRenderer, { mode: theme === "dark" ? "dark" : "light", lightMode: normalizedLightBackground, darkMode: normalizedDarkBackground }), _jsxs(Div, { ref: headerRef, className: "sticky top-0 z-50 w-full", children: [_jsx(TitleBar, { onToggleSidebar: handleTogglePublicSidebar, sidebarOpen: sidebarOpen, onSearchToggle: () => setSearchOpen((prev) => !prev), searchOpen: searchOpen, brandName: brandName, brandShortName: brandShortName, siteLogoUrl: siteLogoUrl, logoHref: logoHref, promotionsHref: promotionsHref, cartHref: cartHref, wishlistHref: wishlistHref, userId: userId, profileHref: profileHref, loginHref: loginHref, registerHref: registerHref, user: user, navSlot: titleBarNavSlot, notificationSlot: titleBarNotificationSlot, devSlot: titleBarDevSlot, promoStripText: titleBarPromoStripText, isDark: theme === "dark", onToggleTheme: showThemeToggle ? toggleTheme : undefined, onBeforeToggleDashboardNav: handleBeforeDashboardNavToggle, suppressDashboardNav: suppressDashboardNav, hideSidebarToggle: hideSidebarToggle }), _jsx(NavbarWithSettings, { navItems: navItems, hiddenNavItems: hiddenNavItems, permissions: authUser?.permissions }), searchOpen && (searchSlotRenderer ? searchSlotRenderer(() => setSearchOpen(false)) : searchSlot)] }), eventBannerSlot, _jsx(AutoBreadcrumbs, {}), _jsxs(Div, { className: "relative flex w-full flex-1 overflow-x-clip", children: [_jsx(SidebarLayout, { isOpen: sidebarOpen, ariaLabel: "Secondary navigation", header: user ? (_jsx(SidebarUserHeader, { user: user, onClose: () => setSidebarOpen(false) })) : (_jsx(SidebarGuestHeader, { sidebarTitle: sidebarTitle, onClose: () => setSidebarOpen(false) })), onClose: () => setSidebarOpen(false), children: sidebarContent }), _jsx(Main, { id: "main-content", className: `w-full flex-1 ${hasBottomActions ? "mb-28" : "mb-16"} md:mb-0`, children: _jsx(Div, { className: contentClassName ?? "
|
|
150
|
+
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsxs(Div, { className: "flex min-h-screen w-full flex-col overflow-x-clip transition-colors duration-300", children: [_jsx(BackgroundRenderer, { mode: theme === "dark" ? "dark" : "light", lightMode: normalizedLightBackground, darkMode: normalizedDarkBackground }), _jsxs(Div, { ref: headerRef, className: "sticky top-0 z-50 w-full", children: [_jsx(TitleBar, { onToggleSidebar: handleTogglePublicSidebar, sidebarOpen: sidebarOpen, onSearchToggle: () => setSearchOpen((prev) => !prev), searchOpen: searchOpen, brandName: brandName, brandShortName: brandShortName, siteLogoUrl: siteLogoUrl, logoHref: logoHref, promotionsHref: promotionsHref, cartHref: cartHref, wishlistHref: wishlistHref, userId: userId, profileHref: profileHref, loginHref: loginHref, registerHref: registerHref, user: user, navSlot: titleBarNavSlot, notificationSlot: titleBarNotificationSlot, devSlot: titleBarDevSlot, promoStripText: titleBarPromoStripText, isDark: theme === "dark", onToggleTheme: showThemeToggle ? toggleTheme : undefined, onBeforeToggleDashboardNav: handleBeforeDashboardNavToggle, suppressDashboardNav: suppressDashboardNav, hideSidebarToggle: hideSidebarToggle }), _jsx(NavbarWithSettings, { navItems: navItems, hiddenNavItems: hiddenNavItems, permissions: authUser?.permissions }), searchOpen && (searchSlotRenderer ? searchSlotRenderer(() => setSearchOpen(false)) : searchSlot)] }), eventBannerSlot, _jsx(AutoBreadcrumbs, {}), _jsxs(Div, { className: "relative flex w-full flex-1 overflow-x-clip", children: [_jsx(SidebarLayout, { isOpen: sidebarOpen, ariaLabel: "Secondary navigation", header: user ? (_jsx(SidebarUserHeader, { user: user, onClose: () => setSidebarOpen(false) })) : (_jsx(SidebarGuestHeader, { sidebarTitle: sidebarTitle, onClose: () => setSidebarOpen(false) })), onClose: () => setSidebarOpen(false), children: sidebarContent }), _jsx(Main, { id: "main-content", className: `w-full flex-1 ${hasBottomActions ? "mb-28" : "mb-16"} md:mb-0`, children: _jsx(Div, { className: contentClassName ?? "w-full px-4 py-6 md:px-6 lg:px-8", children: children }) })] }), _jsx(BackToTop, {}), _jsx(FooterLayout, { ...footer }), _jsx(BottomActions, {}), _jsx(BottomNavbar, { user: user, homeHref: homeHref, shopHref: shopHref, cartHref: cartHref, profileHref: profileHref, loginHref: loginHref, onSearchToggle: () => setSearchOpen((prev) => !prev), navItems: navItems, onMoreToggle: hasDashboardNav ? toggleDashboardNav : handleTogglePublicSidebar }), _jsx(UnsavedChangesModal, {})] }) }));
|
|
151
151
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { FacetOption } from "../../filters/FilterFacetSection";
|
|
2
2
|
import type { UrlTable } from "../../filters/FilterPanel";
|
|
3
|
+
import type { AsyncFacetSectionProps } from "../../filters/AsyncFacetSection";
|
|
4
|
+
type LoadOptionsFn = AsyncFacetSectionProps["loadOptions"];
|
|
3
5
|
export interface LiveItemFiltersProps {
|
|
4
6
|
table: UrlTable;
|
|
5
7
|
categoryOptions?: FacetOption[];
|
|
8
|
+
loadCategoryOptions?: LoadOptionsFn;
|
|
6
9
|
currencyPrefix?: string;
|
|
7
10
|
}
|
|
8
|
-
export declare function LiveItemFilters({ table, categoryOptions, currencyPrefix, }: LiveItemFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function LiveItemFilters({ table, categoryOptions, loadCategoryOptions, currencyPrefix, }: LiveItemFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { FilterFacetSection } from "../../filters/FilterFacetSection";
|
|
4
|
+
import { AsyncFacetSection } from "../../filters/AsyncFacetSection";
|
|
4
5
|
import { RangeFilter } from "../../filters/RangeFilter";
|
|
5
6
|
import { Div } from "../../../ui";
|
|
6
7
|
const SEX_OPTIONS = [
|
|
@@ -13,11 +14,11 @@ const TRANSPORT_OPTIONS = [
|
|
|
13
14
|
{ value: "courier", label: "Live-animal courier" },
|
|
14
15
|
{ value: "specialist", label: "Specialist transport" },
|
|
15
16
|
];
|
|
16
|
-
export function LiveItemFilters({ table, categoryOptions = [], currencyPrefix = "₹", }) {
|
|
17
|
+
export function LiveItemFilters({ table, categoryOptions = [], loadCategoryOptions, currencyPrefix = "₹", }) {
|
|
17
18
|
const selectedCategories = table.get("category")
|
|
18
19
|
? table.get("category").split("|").filter(Boolean)
|
|
19
20
|
: [];
|
|
20
21
|
const selectedSex = table.get("liveSex") ? [table.get("liveSex")] : [];
|
|
21
22
|
const selectedTransport = table.get("liveTransportMethod") ? [table.get("liveTransportMethod")] : [];
|
|
22
|
-
return (_jsxs(Div, { children: [categoryOptions.length > 0
|
|
23
|
+
return (_jsxs(Div, { children: [loadCategoryOptions ? (_jsx(AsyncFacetSection, { title: "Category", loadOptions: loadCategoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), defaultCollapsed: false })) : categoryOptions.length > 0 ? (_jsx(FilterFacetSection, { title: "Category", options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), searchable: true, defaultCollapsed: categoryOptions.length > 6 })) : null, _jsxs("div", { className: "border-t border-[var(--appkit-color-border)] pt-4 mt-4", children: [_jsx("label", { className: "block text-sm font-medium text-[var(--appkit-color-text)] mb-2", children: "Species / Animal" }), _jsx("input", { type: "text", value: table.get("species") || "", onChange: (e) => table.set("species", e.target.value), placeholder: "e.g. Axolotl, Parrot", className: "w-full rounded-lg border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-3 py-2 text-sm text-[var(--appkit-color-text)] placeholder-[var(--appkit-color-text-faint)] focus:outline-none focus:ring-2 focus:ring-[var(--appkit-color-primary)]" })] }), _jsxs("div", { className: "border-t border-[var(--appkit-color-border)] pt-4 mt-4", children: [_jsx("label", { className: "block text-sm font-medium text-[var(--appkit-color-text)] mb-2", children: "Jurisdiction (state/UT)" }), _jsx("input", { type: "text", value: table.get("jurisdiction") || "", onChange: (e) => table.set("jurisdiction", e.target.value), placeholder: "e.g. Maharashtra", className: "w-full rounded-lg border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-3 py-2 text-sm text-[var(--appkit-color-text)] placeholder-[var(--appkit-color-text-faint)] focus:outline-none focus:ring-2 focus:ring-[var(--appkit-color-primary)]" })] }), _jsx(FilterFacetSection, { title: "Sex", options: SEX_OPTIONS, selected: selectedSex, onChange: (vals) => table.set("liveSex", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(FilterFacetSection, { title: "Transport Method", options: TRANSPORT_OPTIONS, selected: selectedTransport, onChange: (vals) => table.set("liveTransportMethod", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(RangeFilter, { title: "Price", minValue: table.get("minPrice"), maxValue: table.get("maxPrice"), onMinChange: (v) => table.set("minPrice", v), onMaxChange: (v) => table.set("maxPrice", v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 200000, step: 100, minPlaceholder: "Min price", maxPlaceholder: "Max price", defaultCollapsed: false })] }));
|
|
23
24
|
}
|
|
@@ -112,8 +112,8 @@ export async function PreOrderDetailPageView({ id, initialPreOrder, onReserveNow
|
|
|
112
112
|
const storeHref = storeSlug
|
|
113
113
|
? String(ROUTES.PUBLIC.STORE_DETAIL(storeSlug))
|
|
114
114
|
: null;
|
|
115
|
-
const category = typeof p.category === "string" ? p.category : null;
|
|
116
|
-
const categoryName = typeof p.categoryName === "string" ? p.categoryName : null;
|
|
115
|
+
const category = Array.isArray(p.categorySlugs) && p.categorySlugs.length > 0 ? p.categorySlugs[0] : (typeof p.category === "string" ? p.category : null);
|
|
116
|
+
const categoryName = Array.isArray(p.categoryNames) && p.categoryNames.length > 0 ? p.categoryNames[0] : (typeof p.categoryName === "string" ? p.categoryName : null);
|
|
117
117
|
const brand = typeof p.brand === "string" ? p.brand : null;
|
|
118
118
|
const brandSlug = typeof p.brandSlug === "string" ? p.brandSlug : null;
|
|
119
119
|
const features = Array.isArray(p.features) ? p.features : [];
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { FacetOption } from "../../filters/FilterFacetSection";
|
|
2
2
|
import type { UrlTable } from "../../filters/FilterPanel";
|
|
3
|
+
import type { AsyncFacetSectionProps } from "../../filters/AsyncFacetSection";
|
|
4
|
+
type LoadOptionsFn = AsyncFacetSectionProps["loadOptions"];
|
|
3
5
|
export interface PreOrderFiltersProps {
|
|
4
6
|
table: UrlTable;
|
|
5
7
|
categoryOptions?: FacetOption[];
|
|
6
8
|
brandOptions?: FacetOption[];
|
|
7
9
|
storeOptions?: FacetOption[];
|
|
10
|
+
loadCategoryOptions?: LoadOptionsFn;
|
|
11
|
+
loadBrandOptions?: LoadOptionsFn;
|
|
12
|
+
loadStoreOptions?: LoadOptionsFn;
|
|
8
13
|
currencyPrefix?: string;
|
|
9
14
|
}
|
|
10
|
-
export declare function PreOrderFilters({ table, categoryOptions, brandOptions, storeOptions, currencyPrefix, }: PreOrderFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function PreOrderFilters({ table, categoryOptions, brandOptions, storeOptions, loadCategoryOptions, loadBrandOptions, loadStoreOptions, currencyPrefix, }: PreOrderFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useTranslations } from "next-intl";
|
|
4
4
|
import { FilterFacetSection } from "../../filters/FilterFacetSection";
|
|
5
|
+
import { AsyncFacetSection } from "../../filters/AsyncFacetSection";
|
|
5
6
|
import { RangeFilter } from "../../filters/RangeFilter";
|
|
6
7
|
import { Div } from "../../../ui";
|
|
7
|
-
export function PreOrderFilters({ table, categoryOptions = [], brandOptions = [], storeOptions = [], currencyPrefix = "", }) {
|
|
8
|
+
export function PreOrderFilters({ table, categoryOptions = [], brandOptions = [], storeOptions = [], loadCategoryOptions, loadBrandOptions, loadStoreOptions, currencyPrefix = "", }) {
|
|
8
9
|
const t = useTranslations("filters");
|
|
9
10
|
const selectedCategories = table.get("category")
|
|
10
11
|
? table.get("category").split("|").filter(Boolean)
|
|
@@ -21,5 +22,5 @@ export function PreOrderFilters({ table, categoryOptions = [], brandOptions = []
|
|
|
21
22
|
const selectedStores = table.get("storeId")
|
|
22
23
|
? table.get("storeId").split("|").filter(Boolean)
|
|
23
24
|
: [];
|
|
24
|
-
return (_jsxs(Div, { children: [categoryOptions.length > 0
|
|
25
|
+
return (_jsxs(Div, { children: [loadCategoryOptions ? (_jsx(AsyncFacetSection, { title: t("category"), loadOptions: loadCategoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), defaultCollapsed: false })) : categoryOptions.length > 0 ? (_jsx(FilterFacetSection, { title: t("category"), options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), searchable: true, defaultCollapsed: categoryOptions.length > 6 })) : null, loadBrandOptions ? (_jsx(AsyncFacetSection, { title: t("brand"), loadOptions: loadBrandOptions, selected: selectedBrands, onChange: (vals) => table.set("brand", vals[0] ?? ""), defaultCollapsed: false })) : brandOptions.length > 0 ? (_jsx(FilterFacetSection, { title: t("brand"), options: brandOptions, selected: selectedBrands, onChange: (vals) => table.set("brand", vals[0] ?? ""), searchable: brandOptions.length > 4, defaultCollapsed: brandOptions.length > 6 })) : null, _jsx(FilterFacetSection, { title: t("productionStatus"), options: productionStatusOptions, selected: selectedProductionStatuses, onChange: (vals) => table.set("preOrderProductionStatus", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(RangeFilter, { title: t("priceRange"), minValue: table.get("minPrice"), maxValue: table.get("maxPrice"), onMinChange: (v) => table.set("minPrice", v), onMaxChange: (v) => table.set("maxPrice", v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 100, minPlaceholder: t("minPrice"), maxPlaceholder: t("maxPrice"), defaultCollapsed: false }), loadStoreOptions ? (_jsx(AsyncFacetSection, { title: t("store"), loadOptions: loadStoreOptions, selected: selectedStores, onChange: (vals) => table.set("storeId", vals[0] ?? ""), defaultCollapsed: false })) : storeOptions.length > 0 ? (_jsx(FilterFacetSection, { title: t("store"), options: storeOptions, selected: selectedStores, onChange: (vals) => table.set("storeId", vals[0] ?? ""), searchable: storeOptions.length > 4, selectionMode: "single", defaultCollapsed: storeOptions.length > 6 })) : null, _jsx(RangeFilter, { title: "Delivery Date Range", type: "date", minValue: table.get("dateFrom"), maxValue: table.get("dateTo"), onMinChange: (v) => table.set("dateFrom", v), onMaxChange: (v) => table.set("dateTo", v), minPlaceholder: t("minDate"), maxPlaceholder: t("maxDate"), defaultCollapsed: false })] }));
|
|
25
26
|
}
|
|
@@ -23,7 +23,7 @@ export function getProductTableColumns({ labels, onEdit, onDelete, currencyCode,
|
|
|
23
23
|
header: labels.category,
|
|
24
24
|
sortable: true,
|
|
25
25
|
width: "12%",
|
|
26
|
-
render: (product) => _jsx(Span, { children: product.category ?? "-" }),
|
|
26
|
+
render: (product) => _jsx(Span, { children: (Array.isArray(product.categorySlugs) ? product.categorySlugs[0] : product.category) ?? "-" }),
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
key: "price",
|
|
@@ -70,7 +70,7 @@ function CompareColumn({ item, productType, labels, onRemove, onClose }) {
|
|
|
70
70
|
const img = item.mainImage ?? item.images?.[0];
|
|
71
71
|
return (_jsxs(Stack, { gap: "md", className: COLUMN_CARD_CLASS, children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", children: [_jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 hover:underline line-clamp-2", children: item.title ?? item.id }), onRemove && (_jsx("button", { type: "button", onClick: () => onRemove(item.id), "aria-label": `${labels.remove} ${item.title ?? item.id}`, className: "shrink-0 rounded-full p-1 text-zinc-400 hover:bg-zinc-100 hover:text-rose-500 dark:hover:bg-zinc-800", children: _jsx(X, { className: "h-4 w-4" }) }))] }), _jsx(FieldRow, { label: labels.field.image, children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, children: _jsx(Div, { className: "relative aspect-square overflow-hidden rounded-lg bg-zinc-100 dark:bg-zinc-800", children: img ? (
|
|
72
72
|
// eslint-disable-next-line @next/next/no-img-element
|
|
73
|
-
_jsx("img", { src: img, alt: item.title ?? item.id, className: "h-full w-full object-cover" })) : null }) }) }), _jsx(FieldRow, { label: labels.field.price, children: _jsx(Text, { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: priceLabel(item) }) }), _jsx(FieldRow, { label: labels.field.condition, children: _jsx(Text, { children: item.condition ? _jsx("span", { className: CHIP_CLASS, children: item.condition }) : "—" }) }), _jsx(FieldRow, { label: labels.field.brand, children: _jsx(Text, { children: item.brand ? _jsx("span", { className: CHIP_CLASS, children: item.brand }) : "—" }) }), _jsx(FieldRow, { label: labels.field.category, children: _jsx(Text, { children: item.categoryName || item.category ? (_jsx("span", { className: CHIP_CLASS, children: item.categoryName ?? item.category })) : ("—") }) }), _jsx(FieldRow, { label: labels.field.store, children: _jsx(Text, { className: "text-sm text-zinc-700 dark:text-zinc-300", children: item.storeName ?? "—" }) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", className: "mt-auto", children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, onClick: onClose, children: labels.view }) })] }));
|
|
73
|
+
_jsx("img", { src: img, alt: item.title ?? item.id, className: "h-full w-full object-cover" })) : null }) }) }), _jsx(FieldRow, { label: labels.field.price, children: _jsx(Text, { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: priceLabel(item) }) }), _jsx(FieldRow, { label: labels.field.condition, children: _jsx(Text, { children: item.condition ? _jsx("span", { className: CHIP_CLASS, children: item.condition }) : "—" }) }), _jsx(FieldRow, { label: labels.field.brand, children: _jsx(Text, { children: item.brand ? _jsx("span", { className: CHIP_CLASS, children: item.brand }) : "—" }) }), _jsx(FieldRow, { label: labels.field.category, children: _jsx(Text, { children: (Array.isArray(item.categoryNames) ? item.categoryNames[0] : item.categoryName) || (Array.isArray(item.categorySlugs) ? item.categorySlugs[0] : item.category) ? (_jsx("span", { className: CHIP_CLASS, children: (Array.isArray(item.categoryNames) ? item.categoryNames[0] : item.categoryName) ?? (Array.isArray(item.categorySlugs) ? item.categorySlugs[0] : item.category) })) : ("—") }) }), _jsx(FieldRow, { label: labels.field.store, children: _jsx(Text, { className: "text-sm text-zinc-700 dark:text-zinc-300", children: item.storeName ?? "—" }) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", className: "mt-auto", children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, onClick: onClose, children: labels.view }) })] }));
|
|
74
74
|
}
|
|
75
75
|
function useFetchedProducts(productIds, passedItems, isOpen) {
|
|
76
76
|
const [fetched, setFetched] = useState([]);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { FacetOption } from "../../filters/FilterFacetSection";
|
|
2
2
|
import type { UrlTable } from "../../filters/FilterPanel";
|
|
3
|
+
import type { AsyncFacetSectionProps } from "../../filters/AsyncFacetSection";
|
|
4
|
+
type LoadOptionsFn = AsyncFacetSectionProps["loadOptions"];
|
|
3
5
|
export type { FacetOption, UrlTable };
|
|
4
6
|
export type ProductFilterVariant = "admin" | "seller" | "public";
|
|
5
7
|
export declare const PRODUCT_FILTER_KEYS: {
|
|
@@ -81,6 +83,10 @@ export interface ProductFiltersProps {
|
|
|
81
83
|
/** @deprecated use storeOptions */
|
|
82
84
|
sellerOptions?: FacetOption[];
|
|
83
85
|
tagOptions?: FacetOption[];
|
|
86
|
+
/** Async load functions — when provided, use AsyncFacetSection instead of static FilterFacetSection */
|
|
87
|
+
loadCategoryOptions?: LoadOptionsFn;
|
|
88
|
+
loadBrandOptions?: LoadOptionsFn;
|
|
89
|
+
loadStoreOptions?: LoadOptionsFn;
|
|
84
90
|
/** Sublisting category options — separate from main category filter */
|
|
85
91
|
sublistingCategoryOptions?: FacetOption[];
|
|
86
92
|
/** Feature badge options for filtering by product features */
|
|
@@ -94,4 +100,4 @@ export interface ProductFiltersProps {
|
|
|
94
100
|
/** Show "part of a bundle" switch filter (default true) */
|
|
95
101
|
showBundleFilter?: boolean;
|
|
96
102
|
}
|
|
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;
|
|
103
|
+
export declare function ProductFilters({ table, categoryOptions, brandOptions, storeOptions, sellerOptions, tagOptions, sublistingCategoryOptions, featureOptions, showStatus, variant, statusOptions, currencyPrefix, showShipping, showBundleFilter, loadCategoryOptions, loadBrandOptions, loadStoreOptions, }: ProductFiltersProps): import("react/jsx-runtime").JSX.Element;
|