@mohasinac/appkit 2.7.11 → 2.7.13
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/jobs/core/assignSpinPrize.js +8 -7
- package/dist/_internal/server/jobs/core/bundleStockSync.js +17 -20
- package/dist/_internal/server/jobs/core/countersReconcile.js +3 -2
- package/dist/_internal/server/jobs/core/onProductStockChange.js +18 -20
- package/dist/_internal/server/jobs/core/onProductWrite.js +3 -2
- package/dist/_internal/server/jobs/core/prizeRevealClose.js +7 -5
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +11 -6
- package/dist/_internal/server/jobs/core/prizeRevealOpen.js +13 -7
- package/dist/_internal/server/jobs/core/prizeRevealReminder.js +9 -4
- package/dist/_internal/server/jobs/core/triggerEventRaffle.js +3 -2
- package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +3 -6
- package/dist/_internal/shared/features/categories/bundle-copy.js +3 -6
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
- package/dist/_internal/shared/features/events/schema.d.ts +4 -4
- package/dist/_internal/shared/features/products/schema.d.ts +20 -20
- package/dist/_internal/shared/features/promotions/schema.d.ts +2 -2
- package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
- package/dist/constants/field-names.d.ts +824 -0
- package/dist/constants/field-names.js +929 -0
- package/dist/constants/sort.d.ts +7 -0
- package/dist/constants/sort.js +5 -0
- package/dist/constants/table-keys.d.ts +48 -0
- package/dist/constants/table-keys.js +46 -0
- package/dist/features/account/components/AddressesIndexListing.js +9 -6
- package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -1
- package/dist/features/admin/components/AdminBidsView.js +4 -1
- package/dist/features/admin/components/AdminBlogView.js +9 -2
- package/dist/features/admin/components/AdminBrandsView.js +9 -2
- package/dist/features/admin/components/AdminBundlesView.js +0 -1
- package/dist/features/admin/components/AdminCarouselView.js +4 -1
- package/dist/features/admin/components/AdminCartsView.js +4 -1
- package/dist/features/admin/components/AdminCategoriesView.js +9 -2
- package/dist/features/admin/components/AdminContactView.js +4 -1
- package/dist/features/admin/components/AdminCouponsView.js +4 -1
- package/dist/features/admin/components/AdminFaqsView.js +5 -1
- package/dist/features/admin/components/AdminFeaturesView.js +5 -2
- package/dist/features/admin/components/AdminHistoryView.js +5 -1
- package/dist/features/admin/components/AdminNewsletterView.js +4 -1
- package/dist/features/admin/components/AdminNotificationsView.js +4 -1
- package/dist/features/admin/components/AdminOrdersView.js +14 -12
- package/dist/features/admin/components/AdminPayoutsView.js +4 -1
- package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -1
- package/dist/features/admin/components/AdminProductsView.js +14 -3
- package/dist/features/admin/components/AdminReturnRequestsView.js +4 -1
- package/dist/features/admin/components/AdminReviewsView.js +4 -1
- package/dist/features/admin/components/AdminScammersView.js +4 -1
- package/dist/features/admin/components/AdminSessionsView.js +4 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +5 -1
- package/dist/features/admin/components/AdminStoresView.js +10 -3
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +4 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +4 -1
- package/dist/features/admin/components/AdminTeamView.js +4 -1
- package/dist/features/admin/components/AdminUsersView.js +9 -12
- package/dist/features/admin/components/AdminViewCards.d.ts +12 -0
- package/dist/features/admin/components/AdminViewCards.js +53 -0
- package/dist/features/admin/components/AdminWishlistsView.js +4 -1
- package/dist/features/auctions/repository/bid.repository.js +1 -6
- package/dist/features/auctions/schemas/index.d.ts +4 -4
- package/dist/features/blog/actions/blog-actions.d.ts +16 -16
- package/dist/features/blog/components/BlogFilters.d.ts +28 -28
- package/dist/features/blog/components/BlogFilters.js +18 -15
- package/dist/features/blog/components/BlogIndexListing.js +20 -16
- package/dist/features/blog/schemas/index.d.ts +10 -10
- package/dist/features/categories/components/BundleDetailView.js +0 -1
- package/dist/features/categories/components/CategoriesIndexListing.js +25 -21
- package/dist/features/categories/components/CategoryBundlesListing.js +2 -4
- package/dist/features/categories/schemas/firestore.d.ts +1 -1
- package/dist/features/categories/schemas/index.d.ts +2 -2
- package/dist/features/events/components/AdminEventsView.js +5 -1
- package/dist/features/events/components/EventFilters.d.ts +24 -24
- package/dist/features/events/components/EventFilters.js +16 -13
- package/dist/features/events/components/EventStatusBadge.js +1 -0
- package/dist/features/events/components/EventsIndexListing.js +31 -26
- package/dist/features/events/components/EventsListPageView.js +13 -10
- package/dist/features/events/schemas/firestore.d.ts +1 -0
- package/dist/features/events/schemas/firestore.js +1 -0
- package/dist/features/events/schemas/index.d.ts +1 -1
- package/dist/features/events/types/index.d.ts +1 -1
- package/dist/features/faq/actions/faq-actions.d.ts +10 -10
- package/dist/features/faq/schemas/index.d.ts +4 -4
- package/dist/features/homepage/components/FeaturedBundlesSection.js +1 -3
- package/dist/features/orders/components/OrderFilters.d.ts +20 -20
- package/dist/features/orders/components/OrderFilters.js +33 -30
- package/dist/features/orders/repository/orders.repository.js +1 -15
- package/dist/features/orders/schemas/index.d.ts +2 -2
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +35 -31
- package/dist/features/pre-orders/repository/preorders.repository.js +8 -4
- package/dist/features/pre-orders/schemas/index.d.ts +3 -3
- package/dist/features/products/components/AuctionsIndexListing.js +34 -30
- package/dist/features/products/components/BidHistory.d.ts +0 -1
- package/dist/features/products/components/BidHistory.js +1 -1
- package/dist/features/products/components/PrizeDrawsIndexListing.js +44 -40
- package/dist/features/products/components/ProductDetailPageView.js +1 -1
- package/dist/features/products/components/ProductFilters.d.ts +20 -20
- package/dist/features/products/components/ProductFilters.js +62 -59
- package/dist/features/products/components/ProductsIndexListing.js +28 -24
- package/dist/features/products/components/ProductsIndexPageView.js +18 -10
- package/dist/features/products/hooks/useProducts.js +2 -0
- package/dist/features/products/repository/products.repository.js +2 -22
- package/dist/features/products/schemas/index.d.ts +28 -28
- package/dist/features/products/schemas/product-features.validators.d.ts +12 -12
- package/dist/features/products/types/index.d.ts +2 -0
- package/dist/features/promotions/components/CouponsIndexListing.js +57 -32
- package/dist/features/promotions/schemas/index.d.ts +4 -4
- package/dist/features/reviews/components/ReviewDetailShell.js +6 -5
- package/dist/features/reviews/components/ReviewFilters.d.ts +16 -16
- package/dist/features/reviews/components/ReviewFilters.js +10 -7
- package/dist/features/reviews/components/ReviewsIndexListing.js +21 -17
- package/dist/features/reviews/components/ReviewsList.d.ts +5 -2
- package/dist/features/reviews/components/ReviewsList.js +11 -5
- package/dist/features/reviews/schemas/firestore.d.ts +1 -0
- package/dist/features/reviews/schemas/index.d.ts +8 -8
- package/dist/features/reviews/types/index.d.ts +1 -0
- package/dist/features/search/schemas/index.d.ts +2 -2
- package/dist/features/seller/components/SellerAuctionsView.js +5 -1
- package/dist/features/seller/components/SellerBidsView.js +4 -1
- package/dist/features/seller/components/SellerCouponsView.js +4 -1
- package/dist/features/seller/components/SellerOffersView.js +5 -1
- package/dist/features/seller/components/SellerOrdersView.js +4 -1
- package/dist/features/seller/components/SellerPayoutsView.js +5 -1
- package/dist/features/seller/components/SellerPreOrdersView.js +4 -1
- package/dist/features/seller/components/SellerPrizeDrawsView.js +4 -1
- package/dist/features/seller/components/SellerProductsView.js +4 -1
- package/dist/features/seller/schemas/index.d.ts +12 -12
- package/dist/features/stores/api/route.js +10 -1
- package/dist/features/stores/components/StoreReviewsListing.js +1 -1
- package/dist/features/stores/components/StoresIndexListing.js +24 -20
- package/dist/features/stores/repository/stores.repository.js +8 -5
- package/dist/features/stores/schemas/firestore.d.ts +1 -0
- package/dist/features/stores/schemas/index.d.ts +4 -4
- package/dist/index.d.ts +8 -1
- package/dist/index.js +12 -1
- package/dist/providers/firebase-client/auth.d.ts +7 -0
- package/dist/providers/firebase-client/auth.js +15 -8
- package/dist/react/hooks/useUrlTable.js +2 -1
- package/dist/seed/bids-seed-data.js +45 -44
- package/dist/seed/categories-seed-data.js +41 -40
- package/dist/seed/coupons-seed-data.js +41 -40
- package/dist/seed/products-auctions-seed-data.js +54 -53
- package/dist/seed/products-preorders-seed-data.js +25 -24
- package/dist/seed/products-prize-draws-seed-data.js +24 -23
- package/dist/seed/products-standard-seed-data.js +294 -293
- package/dist/seed/reviews-seed-data.js +61 -60
- package/dist/seed/scammers-seed-data.js +3 -2
- package/dist/seed/stores-seed-data.js +9 -8
- package/dist/seed/support-tickets-seed-data.js +13 -12
- package/dist/seed/users-seed-data.js +28 -27
- package/dist/tailwind-utilities.css +1 -1
- package/dist/utils/sieve-builder.d.ts +41 -0
- package/dist/utils/sieve-builder.js +66 -0
- package/package.json +2 -2
|
@@ -16,23 +16,27 @@ import { pushCartOp, pushWishlistOp } from "../../cart/utils/pending-ops";
|
|
|
16
16
|
import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
|
|
17
17
|
import { useBrands } from "../../products/hooks/useBrands";
|
|
18
18
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
19
|
+
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
20
|
+
import { sortBy } from "../../../constants/sort";
|
|
21
|
+
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
22
|
+
const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
|
|
19
23
|
const PREORDER_SORT_OPTIONS = [
|
|
20
|
-
{ value:
|
|
21
|
-
{ value: "
|
|
22
|
-
{ value: "
|
|
23
|
-
{ value:
|
|
24
|
-
{ value: "
|
|
25
|
-
{ value:
|
|
24
|
+
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
|
|
25
|
+
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
|
|
26
|
+
{ value: sortBy(PRODUCT_FIELDS.PRE_ORDER_DELIVERY_DATE, "ASC"), label: "Delivery: Soonest" },
|
|
27
|
+
{ value: sortBy(PRODUCT_FIELDS.PRE_ORDER_DELIVERY_DATE), label: "Delivery: Furthest" },
|
|
28
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Price: Low to High" },
|
|
29
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE), label: "Price: High to Low" },
|
|
26
30
|
];
|
|
27
|
-
const FILTER_KEYS = [
|
|
31
|
+
const FILTER_KEYS = [TABLE_KEYS.CATEGORY, TABLE_KEYS.BRAND, TABLE_KEYS.MIN_PRICE, TABLE_KEYS.MAX_PRICE, TABLE_KEYS.STORE_ID, TABLE_KEYS.PREORDER_STATUS, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO];
|
|
28
32
|
export function PreOrdersIndexListing({ initialData, categorySlug, brandName }) {
|
|
29
|
-
const table = useUrlTable({ defaults: { pageSize: "24", sort:
|
|
33
|
+
const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
|
|
30
34
|
const { showToast } = useToast();
|
|
31
|
-
const [searchInput, setSearchInput] = useState(table.get(
|
|
35
|
+
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
32
36
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
33
37
|
const [compareIds, setCompareIds] = useState([]);
|
|
34
|
-
const showClosed = table.get(
|
|
35
|
-
const [view, setView] = useState(table.get(
|
|
38
|
+
const showClosed = table.get(TABLE_KEYS.SHOW_CLOSED) === "true";
|
|
39
|
+
const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
|
|
36
40
|
const localCart = useGuestCart();
|
|
37
41
|
const localWishlist = useGuestWishlist();
|
|
38
42
|
const { categories } = useCategoryTree();
|
|
@@ -81,31 +85,31 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
81
85
|
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
82
86
|
}, []);
|
|
83
87
|
const resetAll = useCallback(() => {
|
|
84
|
-
const updates = {
|
|
88
|
+
const updates = { [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.SHOW_CLOSED]: "" };
|
|
85
89
|
for (const k of FILTER_KEYS)
|
|
86
90
|
updates[k] = "";
|
|
87
91
|
table.setMany(updates);
|
|
88
92
|
setSearchInput("");
|
|
89
93
|
}, [table]);
|
|
90
94
|
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
91
|
-
const hasActiveState = !!table.get(
|
|
92
|
-
table.get(
|
|
93
|
-
table.get(
|
|
95
|
+
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
96
|
+
table.get(TABLE_KEYS.SHOW_CLOSED) === "true" ||
|
|
97
|
+
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
94
98
|
activeFilterCount > 0;
|
|
95
99
|
const params = {
|
|
96
|
-
q: table.get(
|
|
97
|
-
category: table.get(
|
|
100
|
+
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
101
|
+
category: table.get(TABLE_KEYS.CATEGORY) || undefined,
|
|
98
102
|
categorySlug: categorySlug || undefined,
|
|
99
|
-
brand: brandName || table.get(
|
|
100
|
-
minPrice: table.get(
|
|
101
|
-
maxPrice: table.get(
|
|
102
|
-
storeId: table.get(
|
|
103
|
-
preOrderProductionStatus: (table.get(
|
|
104
|
-
dateFrom: table.get(
|
|
105
|
-
dateTo: table.get(
|
|
106
|
-
sort: table.get(
|
|
107
|
-
page: table.getNumber(
|
|
108
|
-
perPage: table.getNumber(
|
|
103
|
+
brand: brandName || table.get(TABLE_KEYS.BRAND) || undefined,
|
|
104
|
+
minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
|
|
105
|
+
maxPrice: table.get(TABLE_KEYS.MAX_PRICE) ? Number(table.get(TABLE_KEYS.MAX_PRICE)) : undefined,
|
|
106
|
+
storeId: table.get(TABLE_KEYS.STORE_ID) || undefined,
|
|
107
|
+
preOrderProductionStatus: (table.get(TABLE_KEYS.PREORDER_STATUS) || undefined),
|
|
108
|
+
dateFrom: table.get(TABLE_KEYS.DATE_FROM) || undefined,
|
|
109
|
+
dateTo: table.get(TABLE_KEYS.DATE_TO) || undefined,
|
|
110
|
+
sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
|
|
111
|
+
page: table.getNumber(TABLE_KEYS.PAGE, 1),
|
|
112
|
+
perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, 24),
|
|
109
113
|
listingType: "pre-order",
|
|
110
114
|
// Hide out-of-stock pre-orders by default. Uses stockQuantity>0 (always-present field).
|
|
111
115
|
// Quota over-sign is intentional — never block pre-orders by availability.
|
|
@@ -113,17 +117,17 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
113
117
|
};
|
|
114
118
|
const { products: preOrders, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
115
119
|
const commitSearch = useCallback(() => {
|
|
116
|
-
table.set(
|
|
120
|
+
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
117
121
|
}, [searchInput, table]);
|
|
118
122
|
const handleSearchKeyDown = (e) => {
|
|
119
123
|
if (e.key === "Enter")
|
|
120
124
|
commitSearch();
|
|
121
125
|
};
|
|
122
126
|
const handleViewToggle = (next) => {
|
|
123
|
-
if (next ===
|
|
127
|
+
if (next === VIEW_MODE.TABLE)
|
|
124
128
|
return;
|
|
125
129
|
setView(next);
|
|
126
|
-
table.set(
|
|
130
|
+
table.set(TABLE_KEYS.VIEW, next);
|
|
127
131
|
};
|
|
128
132
|
const wishlistActions = {
|
|
129
133
|
addToWishlist: (productId) => {
|
|
@@ -151,7 +155,7 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
151
155
|
}, [localCart, showToast]);
|
|
152
156
|
const selection = useBulkSelection({ items: preOrders, keyExtractor: (p) => p.id });
|
|
153
157
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
|
|
154
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(
|
|
158
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: preOrders.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show closed" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showClosed, onClick: () => table.set(TABLE_KEYS.SHOW_CLOSED, showClosed ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showClosed ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showClosed ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
155
159
|
{
|
|
156
160
|
id: ACTION_ID.ADD_TO_CART,
|
|
157
161
|
label: ACTION_META[ACTION_ID.ADD_TO_CART].label,
|
|
@@ -1,19 +1,23 @@
|
|
|
1
|
+
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
2
|
+
const { LISTING_TYPE, STATUS, FEATURED, CREATED_AT } = PRODUCT_FIELDS;
|
|
3
|
+
const { PRE_ORDER } = PRODUCT_FIELDS.LISTING_TYPE_VALUES;
|
|
4
|
+
const { PUBLISHED } = PRODUCT_FIELDS.STATUS_VALUES;
|
|
1
5
|
export class PreordersRepository {
|
|
2
6
|
constructor(repo) {
|
|
3
7
|
this.repo = repo;
|
|
4
8
|
}
|
|
5
9
|
async findPreorders() {
|
|
6
10
|
const result = await this.repo.findAll({
|
|
7
|
-
filters:
|
|
8
|
-
sort:
|
|
11
|
+
filters: `${LISTING_TYPE}==${PRE_ORDER},${STATUS}==${PUBLISHED}`,
|
|
12
|
+
sort: CREATED_AT,
|
|
9
13
|
order: "desc",
|
|
10
14
|
});
|
|
11
15
|
return result.data;
|
|
12
16
|
}
|
|
13
17
|
async findFeaturedPreorders() {
|
|
14
18
|
const result = await this.repo.findAll({
|
|
15
|
-
filters:
|
|
16
|
-
sort:
|
|
19
|
+
filters: `${LISTING_TYPE}==${PRE_ORDER},${STATUS}==${PUBLISHED},${FEATURED}==true`,
|
|
20
|
+
sort: CREATED_AT,
|
|
17
21
|
order: "desc",
|
|
18
22
|
});
|
|
19
23
|
return result.data;
|
|
@@ -48,8 +48,8 @@ export declare const preorderItemSchema: z.ZodObject<{
|
|
|
48
48
|
id: string;
|
|
49
49
|
name: string;
|
|
50
50
|
active: boolean;
|
|
51
|
-
slug: string;
|
|
52
51
|
isFeatured: boolean;
|
|
52
|
+
slug: string;
|
|
53
53
|
salePrice: number;
|
|
54
54
|
regularPrice: number;
|
|
55
55
|
createdAt?: string | undefined;
|
|
@@ -61,8 +61,8 @@ export declare const preorderItemSchema: z.ZodObject<{
|
|
|
61
61
|
thumbnailUrl?: string | undefined;
|
|
62
62
|
youtubeId?: string | undefined;
|
|
63
63
|
}[] | undefined;
|
|
64
|
-
images?: string[] | undefined;
|
|
65
64
|
brand?: string | undefined;
|
|
65
|
+
images?: string[] | undefined;
|
|
66
66
|
franchise?: string | undefined;
|
|
67
67
|
preorderShipDate?: string | undefined;
|
|
68
68
|
}, {
|
|
@@ -81,9 +81,9 @@ export declare const preorderItemSchema: z.ZodObject<{
|
|
|
81
81
|
youtubeId?: string | undefined;
|
|
82
82
|
}[] | undefined;
|
|
83
83
|
active?: boolean | undefined;
|
|
84
|
-
images?: string[] | undefined;
|
|
85
84
|
brand?: string | undefined;
|
|
86
85
|
isFeatured?: boolean | undefined;
|
|
86
|
+
images?: string[] | undefined;
|
|
87
87
|
franchise?: string | undefined;
|
|
88
88
|
preorderShipDate?: string | undefined;
|
|
89
89
|
}>;
|
|
@@ -12,22 +12,26 @@ import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
|
|
|
12
12
|
import { pushWishlistOp } from "../../cart/utils/pending-ops";
|
|
13
13
|
import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
|
|
14
14
|
import { useBrands } from "../hooks/useBrands";
|
|
15
|
+
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
16
|
+
import { sortBy } from "../../../constants/sort";
|
|
17
|
+
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
18
|
+
const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.AUCTION_END_DATE, "ASC");
|
|
15
19
|
const AUCTION_SORT_OPTIONS = [
|
|
16
|
-
{ value: "
|
|
17
|
-
{ value:
|
|
18
|
-
{ value: "
|
|
19
|
-
{ value:
|
|
20
|
-
{ value:
|
|
21
|
-
{ value: "
|
|
20
|
+
{ value: sortBy(PRODUCT_FIELDS.AUCTION_END_DATE, "ASC"), label: "Ending Soonest" },
|
|
21
|
+
{ value: sortBy(PRODUCT_FIELDS.AUCTION_END_DATE), label: "Ending Latest" },
|
|
22
|
+
{ value: sortBy(PRODUCT_FIELDS.CURRENT_BID, "ASC"), label: "Bid: Low to High" },
|
|
23
|
+
{ value: sortBy(PRODUCT_FIELDS.CURRENT_BID), label: "Bid: High to Low" },
|
|
24
|
+
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newly Listed" },
|
|
25
|
+
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest Listed" },
|
|
22
26
|
];
|
|
23
|
-
const FILTER_KEYS = [
|
|
27
|
+
const FILTER_KEYS = [TABLE_KEYS.CATEGORY, TABLE_KEYS.BRAND, TABLE_KEYS.MIN_BID, TABLE_KEYS.MAX_BID, TABLE_KEYS.STORE_ID, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO];
|
|
24
28
|
export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
25
|
-
const table = useUrlTable({ defaults: { pageSize: "24", sort:
|
|
29
|
+
const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
|
|
26
30
|
const { showToast } = useToast();
|
|
27
|
-
const [searchInput, setSearchInput] = useState(table.get(
|
|
31
|
+
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
28
32
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
29
|
-
const showEnded = table.get(
|
|
30
|
-
const [view, setView] = useState(table.get(
|
|
33
|
+
const showEnded = table.get(TABLE_KEYS.SHOW_ENDED) === "true";
|
|
34
|
+
const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
|
|
31
35
|
const localWishlist = useGuestWishlist();
|
|
32
36
|
const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "auction").map((i) => i.itemId));
|
|
33
37
|
const { categories } = useCategoryTree();
|
|
@@ -75,50 +79,50 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
|
75
79
|
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
76
80
|
}, []);
|
|
77
81
|
const resetAll = useCallback(() => {
|
|
78
|
-
const updates = {
|
|
82
|
+
const updates = { [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.SHOW_ENDED]: "" };
|
|
79
83
|
for (const k of FILTER_KEYS)
|
|
80
84
|
updates[k] = "";
|
|
81
85
|
table.setMany(updates);
|
|
82
86
|
setSearchInput("");
|
|
83
87
|
}, [table]);
|
|
84
88
|
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
85
|
-
const hasActiveState = !!table.get(
|
|
86
|
-
table.get(
|
|
87
|
-
table.get(
|
|
89
|
+
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
90
|
+
table.get(TABLE_KEYS.SHOW_ENDED) === "true" ||
|
|
91
|
+
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
88
92
|
activeFilterCount > 0;
|
|
89
93
|
const params = {
|
|
90
|
-
q: table.get(
|
|
91
|
-
category: table.get(
|
|
94
|
+
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
95
|
+
category: table.get(TABLE_KEYS.CATEGORY) || undefined,
|
|
92
96
|
categorySlug: categorySlug || undefined,
|
|
93
|
-
brand: brandName || table.get(
|
|
94
|
-
minBid: table.get(
|
|
95
|
-
maxBid: table.get(
|
|
96
|
-
storeId: table.get(
|
|
97
|
+
brand: brandName || table.get(TABLE_KEYS.BRAND) || undefined,
|
|
98
|
+
minBid: table.get(TABLE_KEYS.MIN_BID) ? Number(table.get(TABLE_KEYS.MIN_BID)) : undefined,
|
|
99
|
+
maxBid: table.get(TABLE_KEYS.MAX_BID) ? Number(table.get(TABLE_KEYS.MAX_BID)) : undefined,
|
|
100
|
+
storeId: table.get(TABLE_KEYS.STORE_ID) || undefined,
|
|
97
101
|
// When showEnded is false (default), force dateFrom=now so only live auctions appear.
|
|
98
102
|
// When showEnded is true, respect the filter-drawer value (or show all if none set).
|
|
99
103
|
dateFrom: showEnded
|
|
100
|
-
? (table.get(
|
|
104
|
+
? (table.get(TABLE_KEYS.DATE_FROM) || undefined)
|
|
101
105
|
: new Date().toISOString(),
|
|
102
|
-
dateTo: table.get(
|
|
103
|
-
sort: table.get(
|
|
104
|
-
page: table.getNumber(
|
|
105
|
-
perPage: table.getNumber(
|
|
106
|
+
dateTo: table.get(TABLE_KEYS.DATE_TO) || undefined,
|
|
107
|
+
sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
|
|
108
|
+
page: table.getNumber(TABLE_KEYS.PAGE, 1),
|
|
109
|
+
perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, 24),
|
|
106
110
|
listingType: "auction",
|
|
107
111
|
};
|
|
108
112
|
const { products: auctions, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
109
113
|
const selection = useBulkSelection({ items: auctions, keyExtractor: (a) => a.id });
|
|
110
114
|
const commitSearch = useCallback(() => {
|
|
111
|
-
table.set(
|
|
115
|
+
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
112
116
|
}, [searchInput, table]);
|
|
113
117
|
const handleSearchKeyDown = (e) => {
|
|
114
118
|
if (e.key === "Enter")
|
|
115
119
|
commitSearch();
|
|
116
120
|
};
|
|
117
121
|
const handleViewToggle = (next) => {
|
|
118
|
-
if (next ===
|
|
122
|
+
if (next === VIEW_MODE.TABLE)
|
|
119
123
|
return;
|
|
120
124
|
setView(next);
|
|
121
|
-
table.set(
|
|
125
|
+
table.set(TABLE_KEYS.VIEW, next);
|
|
122
126
|
};
|
|
123
127
|
const wishlistActions = {
|
|
124
128
|
addToWishlist: (productId) => {
|
|
@@ -136,7 +140,7 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
|
136
140
|
isWishlisted: (productId) => wishlistedIds.has(productId),
|
|
137
141
|
};
|
|
138
142
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-3 gap-4";
|
|
139
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(
|
|
143
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: auctions.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show ended" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showEnded, onClick: () => table.set(TABLE_KEYS.SHOW_ENDED, showEnded ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showEnded ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showEnded ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
140
144
|
{
|
|
141
145
|
id: "watchlist",
|
|
142
146
|
label: "Add to Watchlist",
|
|
@@ -30,5 +30,5 @@ export function BidHistory({ isLoading = false, isEmpty = false, renderList, ren
|
|
|
30
30
|
}
|
|
31
31
|
if (renderList)
|
|
32
32
|
return _jsx("div", { className: className, "data-section": "bidhistory-div-420", children: renderList() });
|
|
33
|
-
return (_jsxs("div", { className: className, "data-section": "bidhistory-div-421", children: [labels.title && (_jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: labels.title })), _jsx("div", { className: "space-y-2", children: bids.map((bid, i) => renderBid ? (_jsx(React.Fragment, { children: renderBid(bid, i) }, bid.id)) : (_jsxs("div", { className: "flex items-center justify-between rounded-lg border border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-3 text-sm", "data-section": "bidhistory-div-422", children: [_jsx(Span, { className: "font-
|
|
33
|
+
return (_jsxs("div", { className: className, "data-section": "bidhistory-div-421", children: [labels.title && (_jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: labels.title })), _jsx("div", { className: "space-y-2", children: bids.map((bid, i) => renderBid ? (_jsx(React.Fragment, { children: renderBid(bid, i) }, bid.id)) : (_jsxs("div", { className: "flex items-center justify-between rounded-lg border border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-3 text-sm", "data-section": "bidhistory-div-422", children: [_jsx(Span, { className: "font-bold text-primary-600 dark:text-primary-400", children: currency ? formatCurrency(bid.amount, currency) : bid.amount.toLocaleString() }), _jsx(Span, { className: "text-xs text-zinc-400 dark:text-zinc-500", children: formatBidDate(bid.placedAt) })] }, bid.id))) })] }));
|
|
34
34
|
}
|
|
@@ -9,28 +9,32 @@ import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hook
|
|
|
9
9
|
import { useBrands } from "../hooks/useBrands";
|
|
10
10
|
import { MarketplacePrizeDrawCard } from "./MarketplacePrizeDrawCard";
|
|
11
11
|
import { ProductFilters } from "./ProductFilters";
|
|
12
|
+
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
13
|
+
import { sortBy } from "../../../constants/sort";
|
|
14
|
+
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
15
|
+
const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
|
|
12
16
|
const PRIZE_DRAW_SORT_OPTIONS = [
|
|
13
|
-
{ value:
|
|
14
|
-
{ value: "
|
|
15
|
-
{ value: "
|
|
16
|
-
{ value:
|
|
17
|
-
{ value: "
|
|
18
|
-
{ value:
|
|
17
|
+
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
|
|
18
|
+
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
|
|
19
|
+
{ value: sortBy(PRODUCT_FIELDS.PRIZE_REVEAL_WINDOW_START, "ASC"), label: "Reveal: Soonest" },
|
|
20
|
+
{ value: sortBy(PRODUCT_FIELDS.PRIZE_REVEAL_WINDOW_START), label: "Reveal: Furthest" },
|
|
21
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Entry: Low to High" },
|
|
22
|
+
{ value: sortBy(PRODUCT_FIELDS.PRICE), label: "Entry: High to Low" },
|
|
19
23
|
];
|
|
20
24
|
const FILTER_KEYS = [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
TABLE_KEYS.CATEGORY,
|
|
26
|
+
TABLE_KEYS.BRAND,
|
|
27
|
+
TABLE_KEYS.MIN_PRICE,
|
|
28
|
+
TABLE_KEYS.MAX_PRICE,
|
|
29
|
+
TABLE_KEYS.STORE_ID,
|
|
30
|
+
TABLE_KEYS.PRIZE_REVEAL_STATUS,
|
|
27
31
|
];
|
|
28
32
|
export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, storeId: forcedStoreId, }) {
|
|
29
|
-
const table = useUrlTable({ defaults: { pageSize: "24", sort:
|
|
30
|
-
const [searchInput, setSearchInput] = useState(table.get(
|
|
33
|
+
const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
|
|
34
|
+
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
31
35
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
32
|
-
const showClosed = table.get(
|
|
33
|
-
const [view, setView] = useState(table.get(
|
|
36
|
+
const showClosed = table.get(TABLE_KEYS.SHOW_CLOSED) === "true";
|
|
37
|
+
const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
|
|
34
38
|
const { categories } = useCategoryTree();
|
|
35
39
|
const categoryOptions = categoriesToFacetOptions(categories);
|
|
36
40
|
const { brandOptions } = useBrands();
|
|
@@ -75,53 +79,53 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
|
|
|
75
79
|
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
76
80
|
}, []);
|
|
77
81
|
const resetAll = useCallback(() => {
|
|
78
|
-
const updates = {
|
|
82
|
+
const updates = { [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.SHOW_CLOSED]: "" };
|
|
79
83
|
for (const k of FILTER_KEYS)
|
|
80
84
|
updates[k] = "";
|
|
81
85
|
table.setMany(updates);
|
|
82
86
|
setSearchInput("");
|
|
83
87
|
}, [table]);
|
|
84
88
|
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
85
|
-
const hasActiveState = !!table.get(
|
|
89
|
+
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
86
90
|
showClosed ||
|
|
87
|
-
table.get(
|
|
91
|
+
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
88
92
|
activeFilterCount > 0;
|
|
93
|
+
const revealFilter = (table.get(TABLE_KEYS.PRIZE_REVEAL_STATUS) || undefined);
|
|
89
94
|
const params = {
|
|
90
|
-
q: table.get(
|
|
91
|
-
category: table.get(
|
|
95
|
+
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
96
|
+
category: table.get(TABLE_KEYS.CATEGORY) || undefined,
|
|
92
97
|
categorySlug: categorySlug || undefined,
|
|
93
|
-
brand: brandName || table.get(
|
|
94
|
-
minPrice: table.get(
|
|
95
|
-
maxPrice: table.get(
|
|
96
|
-
storeId: forcedStoreId || table.get(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
brand: brandName || table.get(TABLE_KEYS.BRAND) || undefined,
|
|
99
|
+
minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
|
|
100
|
+
maxPrice: table.get(TABLE_KEYS.MAX_PRICE) ? Number(table.get(TABLE_KEYS.MAX_PRICE)) : undefined,
|
|
101
|
+
storeId: forcedStoreId || table.get(TABLE_KEYS.STORE_ID) || undefined,
|
|
102
|
+
prizeRevealStatus: revealFilter,
|
|
103
|
+
sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
|
|
104
|
+
page: table.getNumber(TABLE_KEYS.PAGE, 1),
|
|
105
|
+
perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, 24),
|
|
100
106
|
listingType: "prize-draw",
|
|
101
107
|
};
|
|
102
108
|
const { products: draws, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
const filteredDraws = revealFilter
|
|
106
|
-
? draws.filter((d) => d.prizeRevealStatus
|
|
107
|
-
:
|
|
108
|
-
? draws.filter((d) => d.prizeRevealStatus !== "closed")
|
|
109
|
-
: draws;
|
|
109
|
+
// When no explicit reveal-status filter is set, hide closed draws client-side
|
|
110
|
+
// as a UX default (showClosed toggle). Server handles explicit status filters.
|
|
111
|
+
const filteredDraws = !revealFilter && !showClosed
|
|
112
|
+
? draws.filter((d) => d.prizeRevealStatus !== PRODUCT_FIELDS.PRIZE_REVEAL_STATUS_VALUES.CLOSED)
|
|
113
|
+
: draws;
|
|
110
114
|
const commitSearch = useCallback(() => {
|
|
111
|
-
table.set(
|
|
115
|
+
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
112
116
|
}, [searchInput, table]);
|
|
113
117
|
const handleSearchKeyDown = (e) => {
|
|
114
118
|
if (e.key === "Enter")
|
|
115
119
|
commitSearch();
|
|
116
120
|
};
|
|
117
121
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
|
|
118
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search prize draws...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(
|
|
119
|
-
table.set(
|
|
122
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search prize draws...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PRIZE_DRAW_SORT_OPTIONS, onSortChange: (v) => {
|
|
123
|
+
table.set(TABLE_KEYS.SORT, v);
|
|
120
124
|
}, view: view, onViewChange: (v) => {
|
|
121
|
-
if (v ===
|
|
125
|
+
if (v === VIEW_MODE.TABLE)
|
|
122
126
|
return;
|
|
123
127
|
setView(v);
|
|
124
|
-
table.set(
|
|
128
|
+
table.set(TABLE_KEYS.VIEW, v);
|
|
125
129
|
}, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show closed" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showClosed, onClick: () => table.set("showClosed", showClosed ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showClosed ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showClosed ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), 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-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : filteredDraws.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No prize draws found." })) : (_jsx("div", { className: gridClass, children: filteredDraws.map((product) => (_jsx(MarketplacePrizeDrawCard, { product: product, variant: view }, product.id))) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-4", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "prizeRevealStatusFilter", className: "block text-xs font-semibold text-zinc-700 dark:text-zinc-300 mb-1.5", children: "Reveal status" }), _jsxs("select", { id: "prizeRevealStatusFilter", value: pendingFilters.prizeRevealStatus ?? "", onChange: (e) => setPendingFilters((p) => ({
|
|
126
130
|
...p,
|
|
127
131
|
prizeRevealStatus: e.target.value,
|
|
@@ -259,7 +259,7 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
|
|
|
259
259
|
? () => (_jsx(SublistingCarouselSection, { sublistingCategoryId: sublistingCategoryId, currentListingId: String(product.id) }))
|
|
260
260
|
: undefined, renderGroupSection: groupId
|
|
261
261
|
? () => (_jsx(ShowGroupSection, { groupId: groupId, currentSlug: String(p.slug ?? product.id), isParent: isGroupParent, groupTitle: groupTitle }))
|
|
262
|
-
: undefined, renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-zinc-700 dark:text-zinc-300" })) : undefined, specsContent: specs.length > 0 ? (_jsx("dl", { className: "divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800 overflow-hidden text-sm", children: specs.map((s, i) => (_jsxs("div", { className: "flex gap-4 px-4 py-3 bg-white dark:bg-zinc-900 even:bg-zinc-50 dark:even:bg-zinc-800/50", children: [_jsx("dt", { className: "w-36 flex-shrink-0 font-medium text-zinc-700 dark:text-zinc-300", children: s.name }), _jsxs("dd", { className: "flex-1 text-zinc-600 dark:text-zinc-400", children: [s.value, s.unit ? ` ${s.unit}` : ""] })] }, i))) })) : undefined, ingredientsContent: ingredients.length > 0 ? (_jsx("ul", { className: "space-y-2", children: ingredients.map((item, i) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "mt-1 flex-shrink-0 h-1.5 w-1.5 rounded-full bg-primary-400" }), item] }, i))) })) : undefined, howToUseContent: howToUse.length > 0 ? (_jsx("ol", { className: "space-y-3", children: howToUse.map((step, i) => (_jsxs("li", { className: "flex items-start gap-3 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "flex-shrink-0 flex h-6 w-6 items-center justify-center rounded-full bg-primary-100 dark:bg-primary-900/30 text-xs font-bold text-primary-700 dark:text-primary-300", children: i + 1 }), step] }, i))) })) : undefined, reviewsContent: _jsx(ReviewsList, { reviews: reviews, emptyLabel: "No reviews yet \u2014 be the first to review this product." }), customTabs: customSections.map((s) => ({
|
|
262
|
+
: undefined, renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-zinc-700 dark:text-zinc-300" })) : undefined, specsContent: specs.length > 0 ? (_jsx("dl", { className: "divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800 overflow-hidden text-sm", children: specs.map((s, i) => (_jsxs("div", { className: "flex gap-4 px-4 py-3 bg-white dark:bg-zinc-900 even:bg-zinc-50 dark:even:bg-zinc-800/50", children: [_jsx("dt", { className: "w-36 flex-shrink-0 font-medium text-zinc-700 dark:text-zinc-300", children: s.name }), _jsxs("dd", { className: "flex-1 text-zinc-600 dark:text-zinc-400", children: [s.value, s.unit ? ` ${s.unit}` : ""] })] }, i))) })) : undefined, ingredientsContent: ingredients.length > 0 ? (_jsx("ul", { className: "space-y-2", children: ingredients.map((item, i) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "mt-1 flex-shrink-0 h-1.5 w-1.5 rounded-full bg-primary-400" }), item] }, i))) })) : undefined, howToUseContent: howToUse.length > 0 ? (_jsx("ol", { className: "space-y-3", children: howToUse.map((step, i) => (_jsxs("li", { className: "flex items-start gap-3 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "flex-shrink-0 flex h-6 w-6 items-center justify-center rounded-full bg-primary-100 dark:bg-primary-900/30 text-xs font-bold text-primary-700 dark:text-primary-300", children: i + 1 }), step] }, i))) })) : undefined, reviewsContent: _jsx(ReviewsList, { reviews: reviews, context: "listing", emptyLabel: "No reviews yet \u2014 be the first to review this product." }), customTabs: customSections.map((s) => ({
|
|
263
263
|
id: s.id,
|
|
264
264
|
label: s.title,
|
|
265
265
|
content: _jsx(CustomSectionTabContent, { section: s }),
|
|
@@ -5,66 +5,66 @@ export type ProductFilterVariant = "admin" | "seller" | "public";
|
|
|
5
5
|
export declare const PRODUCT_FILTER_KEYS: {
|
|
6
6
|
readonly admin: readonly ["category", "condition", "minPrice", "maxPrice", "brand", "seller", "tags", "status"];
|
|
7
7
|
readonly seller: readonly ["category", "condition", "minPrice", "maxPrice", "brand", "tags", "status"];
|
|
8
|
-
readonly public: readonly ["category", "condition", "minPrice", "maxPrice", "brand", "
|
|
8
|
+
readonly public: readonly ["category", "condition", "minPrice", "maxPrice", "brand", "storeId", "tags"];
|
|
9
9
|
};
|
|
10
10
|
export declare const PRODUCT_ADMIN_SORT_OPTIONS: readonly [{
|
|
11
|
-
readonly value:
|
|
11
|
+
readonly value: string;
|
|
12
12
|
readonly label: "Newest First";
|
|
13
13
|
}, {
|
|
14
|
-
readonly value:
|
|
14
|
+
readonly value: string;
|
|
15
15
|
readonly label: "Oldest First";
|
|
16
16
|
}, {
|
|
17
|
-
readonly value:
|
|
17
|
+
readonly value: string;
|
|
18
18
|
readonly label: "Price: High to Low";
|
|
19
19
|
}, {
|
|
20
|
-
readonly value:
|
|
20
|
+
readonly value: string;
|
|
21
21
|
readonly label: "Price: Low to High";
|
|
22
22
|
}, {
|
|
23
|
-
readonly value:
|
|
23
|
+
readonly value: string;
|
|
24
24
|
readonly label: "Title A-Z";
|
|
25
25
|
}, {
|
|
26
|
-
readonly value:
|
|
26
|
+
readonly value: string;
|
|
27
27
|
readonly label: "Title Z-A";
|
|
28
28
|
}, {
|
|
29
|
-
readonly value:
|
|
29
|
+
readonly value: string;
|
|
30
30
|
readonly label: "Most Viewed";
|
|
31
31
|
}];
|
|
32
32
|
export declare const PRODUCT_SELLER_SORT_OPTIONS: readonly [{
|
|
33
|
-
readonly value:
|
|
33
|
+
readonly value: string;
|
|
34
34
|
readonly label: "Newest First";
|
|
35
35
|
}, {
|
|
36
|
-
readonly value:
|
|
36
|
+
readonly value: string;
|
|
37
37
|
readonly label: "Oldest First";
|
|
38
38
|
}, {
|
|
39
|
-
readonly value:
|
|
39
|
+
readonly value: string;
|
|
40
40
|
readonly label: "Price: High to Low";
|
|
41
41
|
}, {
|
|
42
|
-
readonly value:
|
|
42
|
+
readonly value: string;
|
|
43
43
|
readonly label: "Price: Low to High";
|
|
44
44
|
}, {
|
|
45
|
-
readonly value:
|
|
45
|
+
readonly value: string;
|
|
46
46
|
readonly label: "Title A-Z";
|
|
47
47
|
}];
|
|
48
48
|
export declare const PRODUCT_PUBLIC_SORT_OPTIONS: readonly [{
|
|
49
|
-
readonly value:
|
|
49
|
+
readonly value: string;
|
|
50
50
|
readonly label: "Newest First";
|
|
51
51
|
}, {
|
|
52
|
-
readonly value:
|
|
52
|
+
readonly value: string;
|
|
53
53
|
readonly label: "Oldest First";
|
|
54
54
|
}, {
|
|
55
|
-
readonly value:
|
|
55
|
+
readonly value: string;
|
|
56
56
|
readonly label: "Price: High to Low";
|
|
57
57
|
}, {
|
|
58
|
-
readonly value:
|
|
58
|
+
readonly value: string;
|
|
59
59
|
readonly label: "Price: Low to High";
|
|
60
60
|
}, {
|
|
61
|
-
readonly value:
|
|
61
|
+
readonly value: string;
|
|
62
62
|
readonly label: "Title A–Z";
|
|
63
63
|
}, {
|
|
64
|
-
readonly value:
|
|
64
|
+
readonly value: string;
|
|
65
65
|
readonly label: "Title Z–A";
|
|
66
66
|
}, {
|
|
67
|
-
readonly value:
|
|
67
|
+
readonly value: string;
|
|
68
68
|
readonly label: "Most Viewed";
|
|
69
69
|
}];
|
|
70
70
|
export declare function getProductFilterKeys(variant: ProductFilterVariant): readonly string[];
|