@mohasinac/appkit 2.7.10 → 2.7.11
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/configs/next.js +17 -1
- package/dist/features/blog/components/BlogIndexListing.js +8 -1
- package/dist/features/categories/components/CategoriesIndexListing.js +9 -2
- package/dist/features/events/components/EventsIndexListing.js +8 -1
- package/dist/features/layout/TitleBarLayout.js +1 -1
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +11 -7
- package/dist/features/products/components/AuctionsIndexListing.js +31 -2
- package/dist/features/products/components/PrizeDrawsIndexListing.js +5 -2
- package/dist/features/products/components/ProductsIndexListing.js +12 -8
- package/dist/features/reviews/components/ReviewsIndexListing.js +8 -1
- package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
- package/dist/features/stores/components/StorePreOrdersListing.js +2 -1
- package/dist/features/stores/components/StoresIndexListing.js +25 -11
- package/dist/ui/components/ListingToolbar.d.ts +7 -4
- package/dist/ui/components/ListingToolbar.js +4 -3
- package/dist/ui/components/SiteLogo.js +1 -1
- package/package.json +1 -1
package/dist/configs/next.js
CHANGED
|
@@ -65,7 +65,7 @@ const FIREBASE_EXTERNAL_PACKAGES = [
|
|
|
65
65
|
* ```
|
|
66
66
|
*/
|
|
67
67
|
export function defineNextConfig(override = {}) {
|
|
68
|
-
const { serverExternalPackages: consumerExternal = [], experimental: consumerExperimental = {}, webpack: consumerWebpack, outputFileTracingIncludes: consumerOutputFileTracingIncludes = {}, ...rest } = override;
|
|
68
|
+
const { serverExternalPackages: consumerExternal = [], experimental: consumerExperimental = {}, webpack: consumerWebpack, outputFileTracingIncludes: consumerOutputFileTracingIncludes = {}, turbopack: consumerTurbopack = {}, ...rest } = override;
|
|
69
69
|
const mergedExternal = [
|
|
70
70
|
...FIREBASE_EXTERNAL_PACKAGES,
|
|
71
71
|
...consumerExternal.filter((p) => !FIREBASE_EXTERNAL_PACKAGES.includes(p)),
|
|
@@ -177,6 +177,8 @@ export function defineNextConfig(override = {}) {
|
|
|
177
177
|
// disagree on which apps exist, causing "No Firebase App '[DEFAULT]'" errors.
|
|
178
178
|
// Pinning every firebase/* import to the root copy ensures the singleton
|
|
179
179
|
// registry is shared regardless of which file imports firebase.
|
|
180
|
+
// NOTE: This fixes webpack (dev --webpack) only. Turbopack (next build) uses
|
|
181
|
+
// turbopack.resolveAlias in the returned config object — see below.
|
|
180
182
|
const _firebaseRoot = path.resolve(process.cwd(), "node_modules", "firebase");
|
|
181
183
|
config.resolve.alias = {
|
|
182
184
|
...(config.resolve.alias ?? {}),
|
|
@@ -265,6 +267,19 @@ export function defineNextConfig(override = {}) {
|
|
|
265
267
|
...consumerRemotePatterns.filter((p) => !defaultRemotePatterns.some((d) => d.hostname ===
|
|
266
268
|
p.hostname)),
|
|
267
269
|
];
|
|
270
|
+
// Turbopack (used by `next build`) has its own alias system separate from
|
|
271
|
+
// webpack. Mirror the firebase deduplication alias so prod builds share the
|
|
272
|
+
// same Firebase app registry as dev builds.
|
|
273
|
+
const firebaseRoot = path.resolve(process.cwd(), "node_modules", "firebase");
|
|
274
|
+
const consumerTurbopackResolved = consumerTurbopack;
|
|
275
|
+
const consumerTurbopackAlias = (consumerTurbopackResolved.resolveAlias ?? {});
|
|
276
|
+
const mergedTurbopack = {
|
|
277
|
+
...consumerTurbopackResolved,
|
|
278
|
+
resolveAlias: {
|
|
279
|
+
...consumerTurbopackAlias,
|
|
280
|
+
firebase: firebaseRoot,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
268
283
|
return {
|
|
269
284
|
images: {
|
|
270
285
|
...consumerImages,
|
|
@@ -275,5 +290,6 @@ export function defineNextConfig(override = {}) {
|
|
|
275
290
|
experimental: mergedExperimental,
|
|
276
291
|
outputFileTracingIncludes: mergedOutputFileTracingIncludes,
|
|
277
292
|
webpack: mergedWebpack,
|
|
293
|
+
turbopack: mergedTurbopack,
|
|
278
294
|
};
|
|
279
295
|
}
|
|
@@ -14,6 +14,13 @@ export function BlogIndexListing({ initialData }) {
|
|
|
14
14
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: "-publishedAt" } });
|
|
15
15
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
16
16
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
17
|
+
const [view, setView] = useState(table.get("view") || "grid");
|
|
18
|
+
const handleViewToggle = (next) => {
|
|
19
|
+
if (next === "table")
|
|
20
|
+
return;
|
|
21
|
+
setView(next);
|
|
22
|
+
table.set("view", next);
|
|
23
|
+
};
|
|
17
24
|
// Pending filter state — buffered until "Apply Filters" clicked
|
|
18
25
|
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
19
26
|
const pendingTable = useMemo(() => ({
|
|
@@ -78,5 +85,5 @@ export function BlogIndexListing({ initialData }) {
|
|
|
78
85
|
const commitSearch = useCallback(() => {
|
|
79
86
|
table.set("q", searchInput.trim());
|
|
80
87
|
}, [searchInput, table]);
|
|
81
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search posts...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "-publishedAt", sortOptions: BLOG_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
88
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search posts...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "-publishedAt", sortOptions: BLOG_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), 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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-5 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/4" }), _jsx("div", { className: "h-4 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-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] })] }, i))) })) : posts.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No posts found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: posts.map((post) => (_jsx(BlogCard, { post: post, href: String(ROUTES.BLOG.ARTICLE(post.slug)) }, post.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: posts.map((post) => (_jsx(BlogCard, { post: post, href: String(ROUTES.BLOG.ARTICLE(post.slug)) }, post.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: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(BlogFilters, { table: pendingTable, variant: "public" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
82
89
|
}
|
|
@@ -19,6 +19,13 @@ export function CategoriesIndexListing({ initialData: _, brandsOnly = false }) {
|
|
|
19
19
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: "name" } });
|
|
20
20
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
21
21
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
22
|
+
const [view, setView] = useState(table.get("view") || "grid");
|
|
23
|
+
const handleViewToggle = (next) => {
|
|
24
|
+
if (next === "table")
|
|
25
|
+
return;
|
|
26
|
+
setView(next);
|
|
27
|
+
table.set("view", next);
|
|
28
|
+
};
|
|
22
29
|
const sort = table.get("sort") || "name";
|
|
23
30
|
const page = table.getNumber("page", 1);
|
|
24
31
|
// Pending filter state — buffered until "Apply Filters" clicked
|
|
@@ -112,9 +119,9 @@ export function CategoriesIndexListing({ initialData: _, brandsOnly = false }) {
|
|
|
112
119
|
activeTab === tab.key
|
|
113
120
|
? "border-primary text-primary dark:text-primary-400 dark:border-primary-400"
|
|
114
121
|
: "border-transparent text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200",
|
|
115
|
-
].join(" "), children: tab.label }, tab.key))) })), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: activeTab === "brands" || brandsOnly ? "Search brands..." : "Search categories...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
122
|
+
].join(" "), children: tab.label }, tab.key))) })), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: activeTab === "brands" || brandsOnly ? "Search brands..." : "Search categories...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4", children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-[4/3] bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3.5 space-y-2", children: [_jsx("div", { className: "h-3.5 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-full" })] })] }, i))) })) : categories.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: activeSearch
|
|
116
123
|
? `No ${activeTab === "brands" || brandsOnly ? "brands" : "categories"} matching "${activeSearch}"`
|
|
117
124
|
: activeTab === "brands" || brandsOnly
|
|
118
125
|
? "No brands found"
|
|
119
|
-
: "No categories found" })) : (_jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4", children: categories.map((category) => (_jsx(CategoryCard, { category: category, href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category.slug)) }, category.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: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(CategoryFilters, { table: pendingTable, variant: "public" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
126
|
+
: "No categories found" })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: categories.map((category) => (_jsx(CategoryCard, { category: category, href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category.slug)) }, category.id))) })) : (_jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4", children: categories.map((category) => (_jsx(CategoryCard, { category: category, href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category.slug)) }, category.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: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(CategoryFilters, { table: pendingTable, variant: "public" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
120
127
|
}
|
|
@@ -13,6 +13,13 @@ export function EventsIndexListing({ initialData }) {
|
|
|
13
13
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: "startsAt" } });
|
|
14
14
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
15
15
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
16
|
+
const [view, setView] = useState(table.get("view") || "grid");
|
|
17
|
+
const handleViewToggle = (next) => {
|
|
18
|
+
if (next === "table")
|
|
19
|
+
return;
|
|
20
|
+
setView(next);
|
|
21
|
+
table.set("view", next);
|
|
22
|
+
};
|
|
16
23
|
// Pending filter state — buffered until "Apply Filters" clicked
|
|
17
24
|
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
18
25
|
const pendingTable = useMemo(() => ({
|
|
@@ -101,5 +108,5 @@ export function EventsIndexListing({ initialData }) {
|
|
|
101
108
|
const commitSearch = useCallback(() => {
|
|
102
109
|
table.set("q", searchInput.trim());
|
|
103
110
|
}, [searchInput, table]);
|
|
104
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "startsAt", sortOptions: EVENT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
111
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "startsAt", sortOptions: EVENT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), 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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-4 space-y-2", children: [_jsx("div", { className: "h-4 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-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded mt-2" })] })] }, i))) })) : events.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No events found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: events.map((event) => (_jsx(EventCard, { event: event }, event.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: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(EventFilters, { table: pendingTable, variant: "public" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
105
112
|
}
|
|
@@ -30,7 +30,7 @@ export function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, s
|
|
|
30
30
|
const authButtonsEl = !user && (loginHref || registerHref) ? (_jsxs(Row, { gap: "xs", className: "hidden lg:flex items-center", children: [loginHref && (_jsx(Link, { href: loginHref, className: "px-3 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-300 hover:text-primary-700 dark:hover:text-secondary-400 transition-colors rounded-lg hover:bg-primary-50 dark:hover:bg-slate-800", children: "Sign in" })), registerHref && (_jsx(Link, { href: registerHref, className: "px-3 py-1.5 text-sm font-semibold rounded-lg bg-primary text-white hover:bg-primary/90 transition-colors btn-glow shadow-sm", children: "Register" }))] })) : null;
|
|
31
31
|
const hasTb2 = !!(wishlistEl || cartEl || profileEl);
|
|
32
32
|
// ── Render ───────────────────────────────────────────────────────────────────
|
|
33
|
-
return (_jsxs(BlockHeader, { id: id, className: `sticky top-0 z-50 bg-white/95 dark:bg-slate-950/95 backdrop-blur-md border-b border-zinc-100 dark:border-slate-800 shadow-sm ${className}`, children: [promoStripText && (_jsx(Div, { className: "bg-gradient-to-r from-primary-700 to-secondary-600 dark:from-primary-700 dark:to-cobalt-700 text-white text-xs py-1 text-center font-medium", children: promoStripText })), _jsxs(Div, { className: "container mx-auto px-4 sm:px-6 lg:px-8 max-w-[1920px]", children: [_jsxs(Row, { justify: "between", gap: "none", className: "h-14", children: [_jsx(Row, { gap: "3", children: _jsx(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center transition-opacity hover:opacity-80", children: _jsx(SiteLogo, { title: brandName, src: siteLogoUrl, className: "h-7 md:h-9 lg:h-10" }) }) })
|
|
33
|
+
return (_jsxs(BlockHeader, { id: id, className: `sticky top-0 z-50 bg-white/95 dark:bg-slate-950/95 backdrop-blur-md border-b border-zinc-100 dark:border-slate-800 shadow-sm ${className}`, children: [promoStripText && (_jsx(Div, { className: "bg-gradient-to-r from-primary-700 to-secondary-600 dark:from-primary-700 dark:to-cobalt-700 text-white text-xs py-1 text-center font-medium", children: promoStripText })), _jsxs(Div, { className: "container mx-auto px-4 sm:px-6 lg:px-8 max-w-[1920px]", children: [_jsxs(Row, { justify: "between", gap: "none", className: "relative h-14", children: [_jsx(Row, { gap: "3", children: _jsx(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center transition-opacity hover:opacity-80", children: _jsx(SiteLogo, { title: brandName, className: "h-7 md:h-9 lg:h-10" }) }) }), siteLogoUrl ? (_jsx(Div, { className: "absolute inset-y-0 left-1/2 -translate-x-1/2 flex items-center", children: _jsx(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center transition-opacity hover:opacity-80", children: _jsx(SiteLogo, { src: siteLogoUrl, title: brandName, className: "h-7 md:h-9 lg:h-10" }) }) })) : (navSlot && _jsx(Div, { className: "hidden md:flex", children: navSlot })), _jsxs(Row, { gap: "xs", children: [devSlot, compareEl, notificationSlot, wishlistEl && _jsx(Div, { className: "hidden lg:flex", children: wishlistEl }), cartEl && _jsx(Div, { className: "hidden lg:flex", children: cartEl }), user
|
|
34
34
|
? profileEl && _jsx(Div, { className: "hidden lg:flex", children: profileEl })
|
|
35
35
|
: authButtonsEl ?? (profileEl && _jsx(Div, { className: "hidden lg:flex", children: profileEl })), searchBtn, promotionsEl, themeBtn, hamburgerBtn] })] }), hasTb2 && (_jsxs(Row, { as: "nav", "aria-label": "Account actions", justify: "end", gap: "xs", className: "flex lg:hidden h-10 border-t border-zinc-100 dark:border-slate-800 px-1", children: [wishlistEl, cartEl, profileEl] }))] })] }));
|
|
36
36
|
}
|
|
@@ -4,7 +4,7 @@ import { useState, useCallback, useMemo } from "react";
|
|
|
4
4
|
import { Columns, Heart, ShoppingCart, SlidersHorizontal, X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
7
|
-
import { Pagination, useToast,
|
|
7
|
+
import { Pagination, useToast, BulkActionBar, ListingToolbar } from "../../../ui";
|
|
8
8
|
import { ACTION_ID, ACTION_META, COMPARE_MAX_ITEMS } from "../../products/constants/action-defs";
|
|
9
9
|
import { CompareOverlay } from "../../products/components/CompareOverlay";
|
|
10
10
|
import { ROUTES } from "../../../next";
|
|
@@ -107,7 +107,9 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
107
107
|
page: table.getNumber("page", 1),
|
|
108
108
|
perPage: table.getNumber("pageSize", 24),
|
|
109
109
|
listingType: "pre-order",
|
|
110
|
-
|
|
110
|
+
// Hide out-of-stock pre-orders by default. Uses stockQuantity>0 (always-present field).
|
|
111
|
+
// Quota over-sign is intentional — never block pre-orders by availability.
|
|
112
|
+
inStock: showClosed ? undefined : true,
|
|
111
113
|
};
|
|
112
114
|
const { products: preOrders, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
113
115
|
const commitSearch = useCallback(() => {
|
|
@@ -118,6 +120,8 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
118
120
|
commitSearch();
|
|
119
121
|
};
|
|
120
122
|
const handleViewToggle = (next) => {
|
|
123
|
+
if (next === "table")
|
|
124
|
+
return;
|
|
121
125
|
setView(next);
|
|
122
126
|
table.set("view", next);
|
|
123
127
|
};
|
|
@@ -147,9 +151,9 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
147
151
|
}, [localCart, showToast]);
|
|
148
152
|
const selection = useBulkSelection({ items: preOrders, keyExtractor: (p) => p.id });
|
|
149
153
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
|
|
150
|
-
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("sort") || "-createdAt", sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set("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("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]"}` }) })] }) }),
|
|
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("sort") || "-createdAt", sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set("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("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]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
151
155
|
{
|
|
152
|
-
|
|
156
|
+
id: ACTION_ID.ADD_TO_CART,
|
|
153
157
|
label: ACTION_META[ACTION_ID.ADD_TO_CART].label,
|
|
154
158
|
icon: _jsx(ShoppingCart, { className: "h-3.5 w-3.5" }),
|
|
155
159
|
variant: "primary",
|
|
@@ -164,7 +168,7 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
164
168
|
},
|
|
165
169
|
},
|
|
166
170
|
{
|
|
167
|
-
|
|
171
|
+
id: ACTION_ID.ADD_TO_WISHLIST,
|
|
168
172
|
label: ACTION_META[ACTION_ID.ADD_TO_WISHLIST].label,
|
|
169
173
|
icon: _jsx(Heart, { className: "h-3.5 w-3.5" }),
|
|
170
174
|
variant: "secondary",
|
|
@@ -179,7 +183,7 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
179
183
|
},
|
|
180
184
|
},
|
|
181
185
|
{
|
|
182
|
-
|
|
186
|
+
id: ACTION_ID.COMPARE,
|
|
183
187
|
label: ACTION_META[ACTION_ID.COMPARE].label,
|
|
184
188
|
icon: _jsx(Columns, { className: "h-3.5 w-3.5" }),
|
|
185
189
|
variant: "secondary",
|
|
@@ -189,7 +193,7 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
189
193
|
setCompareIds(ids);
|
|
190
194
|
},
|
|
191
195
|
},
|
|
192
|
-
] }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "preorder", onClose: () => {
|
|
196
|
+
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No pre-orders found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) : (_jsx("div", { className: gridClass, children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "preorder", onClose: () => {
|
|
193
197
|
setCompareIds([]);
|
|
194
198
|
selection.clearSelection();
|
|
195
199
|
}, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(PreOrderFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
@@ -4,7 +4,8 @@ import { useState, useCallback, useMemo } from "react";
|
|
|
4
4
|
import { SlidersHorizontal, X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useProducts } from "../hooks/useProducts";
|
|
7
|
-
import { Pagination, useToast, ListingToolbar } from "../../../ui";
|
|
7
|
+
import { Pagination, useToast, ListingToolbar, BulkActionBar } from "../../../ui";
|
|
8
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
9
|
import { MarketplaceAuctionGrid } from "../../auctions/components/MarketplaceAuctionGrid";
|
|
9
10
|
import { AuctionFilters } from "../../auctions/components/AuctionFilters";
|
|
10
11
|
import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
|
|
@@ -105,6 +106,7 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
|
105
106
|
listingType: "auction",
|
|
106
107
|
};
|
|
107
108
|
const { products: auctions, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
109
|
+
const selection = useBulkSelection({ items: auctions, keyExtractor: (a) => a.id });
|
|
108
110
|
const commitSearch = useCallback(() => {
|
|
109
111
|
table.set("q", searchInput.trim());
|
|
110
112
|
}, [searchInput, table]);
|
|
@@ -113,6 +115,8 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
|
113
115
|
commitSearch();
|
|
114
116
|
};
|
|
115
117
|
const handleViewToggle = (next) => {
|
|
118
|
+
if (next === "table")
|
|
119
|
+
return;
|
|
116
120
|
setView(next);
|
|
117
121
|
table.set("view", next);
|
|
118
122
|
};
|
|
@@ -132,5 +136,30 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
|
132
136
|
isWishlisted: (productId) => wishlistedIds.has(productId),
|
|
133
137
|
};
|
|
134
138
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-3 gap-4";
|
|
135
|
-
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("sort") || "auctionEndDate", sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: handleViewToggle, 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 ended" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showEnded, onClick: () => table.set("showEnded", 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]"}` }) })] }) }),
|
|
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("sort") || "auctionEndDate", sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set("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("showEnded", 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
|
+
{
|
|
141
|
+
id: "watchlist",
|
|
142
|
+
label: "Add to Watchlist",
|
|
143
|
+
variant: "primary",
|
|
144
|
+
onClick: () => {
|
|
145
|
+
const selected = auctions.filter((a) => selection.selectedIdSet.has(a.id));
|
|
146
|
+
selected.forEach((a) => {
|
|
147
|
+
wishlistActions.addToWishlist(a.id);
|
|
148
|
+
});
|
|
149
|
+
selection.clearSelection();
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: "remove-watchlist",
|
|
154
|
+
label: "Remove from Watchlist",
|
|
155
|
+
variant: "secondary",
|
|
156
|
+
onClick: () => {
|
|
157
|
+
const selected = auctions.filter((a) => selection.selectedIdSet.has(a.id));
|
|
158
|
+
selected.forEach((a) => {
|
|
159
|
+
wishlistActions.removeFromWishlist(a.id);
|
|
160
|
+
});
|
|
161
|
+
selection.clearSelection();
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-4" : gridClass, wishlistActions: wishlistActions })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
136
165
|
}
|
|
@@ -98,14 +98,15 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
|
|
|
98
98
|
page: table.getNumber("page", 1),
|
|
99
99
|
perPage: table.getNumber("pageSize", 24),
|
|
100
100
|
listingType: "prize-draw",
|
|
101
|
-
status: showClosed ? undefined : "published",
|
|
102
101
|
};
|
|
103
102
|
const { products: draws, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
104
103
|
// Client-side filter for prizeRevealStatus until the server query supports it.
|
|
105
104
|
const revealFilter = table.get("prizeRevealStatus");
|
|
106
105
|
const filteredDraws = revealFilter
|
|
107
106
|
? draws.filter((d) => d.prizeRevealStatus === revealFilter)
|
|
108
|
-
:
|
|
107
|
+
: !showClosed
|
|
108
|
+
? draws.filter((d) => d.prizeRevealStatus !== "closed")
|
|
109
|
+
: draws;
|
|
109
110
|
const commitSearch = useCallback(() => {
|
|
110
111
|
table.set("q", searchInput.trim());
|
|
111
112
|
}, [searchInput, table]);
|
|
@@ -117,6 +118,8 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
|
|
|
117
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("sort") || "-createdAt", sortOptions: PRIZE_DRAW_SORT_OPTIONS, onSortChange: (v) => {
|
|
118
119
|
table.set("sort", v);
|
|
119
120
|
}, view: view, onViewChange: (v) => {
|
|
121
|
+
if (v === "table")
|
|
122
|
+
return;
|
|
120
123
|
setView(v);
|
|
121
124
|
table.set("view", v);
|
|
122
125
|
}, 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) => ({
|
|
@@ -5,7 +5,7 @@ import { X, ShoppingCart, Heart, SlidersHorizontal, Columns } from "lucide-react
|
|
|
5
5
|
import { useRouter } from "next/navigation";
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
7
|
import { useProducts } from "../hooks/useProducts";
|
|
8
|
-
import { Pagination, useToast,
|
|
8
|
+
import { Pagination, useToast, BulkActionBar, ListingToolbar } from "../../../ui";
|
|
9
9
|
import { ACTION_ID, ACTION_META, COMPARE_MAX_ITEMS } from "../constants/action-defs";
|
|
10
10
|
import { CompareOverlay } from "./CompareOverlay";
|
|
11
11
|
import { ROUTES } from "../../../next";
|
|
@@ -24,7 +24,7 @@ export function ProductsIndexListing({ initialData }) {
|
|
|
24
24
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
25
25
|
const [compareIds, setCompareIds] = useState([]);
|
|
26
26
|
const showSold = table.get("showSold") === "true";
|
|
27
|
-
const [view, setView] = useState(table.get("view") || "
|
|
27
|
+
const [view, setView] = useState(table.get("view") || "grid");
|
|
28
28
|
// Pending filter state — buffered until "Apply Filters" clicked
|
|
29
29
|
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
30
30
|
const pendingTable = useMemo(() => ({
|
|
@@ -97,7 +97,9 @@ export function ProductsIndexListing({ initialData }) {
|
|
|
97
97
|
page: table.getNumber("page", 1),
|
|
98
98
|
perPage: table.getNumber("pageSize", 24),
|
|
99
99
|
listingType: "standard",
|
|
100
|
-
|
|
100
|
+
// Hide sold-out items by default. Uses stockQuantity>0 (always-present field)
|
|
101
|
+
// instead of status=="published" because sellers don't actively transition status.
|
|
102
|
+
inStock: showSold ? undefined : true,
|
|
101
103
|
};
|
|
102
104
|
const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
103
105
|
const selection = useBulkSelection({ items: products, keyExtractor: (p) => p.id });
|
|
@@ -109,6 +111,8 @@ export function ProductsIndexListing({ initialData }) {
|
|
|
109
111
|
commitSearch();
|
|
110
112
|
};
|
|
111
113
|
const handleViewToggle = (next) => {
|
|
114
|
+
if (next === "table")
|
|
115
|
+
return;
|
|
112
116
|
setView(next);
|
|
113
117
|
table.set("view", next);
|
|
114
118
|
};
|
|
@@ -143,9 +147,9 @@ export function ProductsIndexListing({ initialData }) {
|
|
|
143
147
|
pushCartOp({ op: "add", productId: product.id, quantity: 1, productTitle: product.title, productImage: product.mainImage, price: product.price });
|
|
144
148
|
router.push(String(ROUTES.USER.CART));
|
|
145
149
|
}, [localCart, router]);
|
|
146
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search products...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get("sort") || "-createdAt", sortOptions: PRODUCT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view
|
|
150
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search products...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get("sort") || "-createdAt", sortOptions: PRODUCT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.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 sold" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showSold, onClick: () => table.set("showSold", showSold ? "" : "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 ${showSold ? "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 ${showSold ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
147
151
|
{
|
|
148
|
-
|
|
152
|
+
id: ACTION_ID.ADD_TO_CART,
|
|
149
153
|
label: ACTION_META[ACTION_ID.ADD_TO_CART].label,
|
|
150
154
|
icon: _jsx(ShoppingCart, { className: "h-3.5 w-3.5" }),
|
|
151
155
|
variant: "primary",
|
|
@@ -160,7 +164,7 @@ export function ProductsIndexListing({ initialData }) {
|
|
|
160
164
|
},
|
|
161
165
|
},
|
|
162
166
|
{
|
|
163
|
-
|
|
167
|
+
id: ACTION_ID.ADD_TO_WISHLIST,
|
|
164
168
|
label: ACTION_META[ACTION_ID.ADD_TO_WISHLIST].label,
|
|
165
169
|
icon: _jsx(Heart, { className: "h-3.5 w-3.5" }),
|
|
166
170
|
variant: "secondary",
|
|
@@ -175,7 +179,7 @@ export function ProductsIndexListing({ initialData }) {
|
|
|
175
179
|
},
|
|
176
180
|
},
|
|
177
181
|
{
|
|
178
|
-
|
|
182
|
+
id: ACTION_ID.COMPARE,
|
|
179
183
|
label: ACTION_META[ACTION_ID.COMPARE].label,
|
|
180
184
|
icon: _jsx(Columns, { className: "h-3.5 w-3.5" }),
|
|
181
185
|
variant: "secondary",
|
|
@@ -185,7 +189,7 @@ export function ProductsIndexListing({ initialData }) {
|
|
|
185
189
|
setCompareIds(ids);
|
|
186
190
|
},
|
|
187
191
|
},
|
|
188
|
-
] }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "product", onClose: () => {
|
|
192
|
+
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4", children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: view === "grid" ? "card" : "list", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart, onBuyNow: handleBuyNow, selectionMode: selection.isSelecting, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "product", onClose: () => {
|
|
189
193
|
setCompareIds([]);
|
|
190
194
|
selection.clearSelection();
|
|
191
195
|
}, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
@@ -13,6 +13,13 @@ export function ReviewsIndexListing({ initialData, variant = "public", }) {
|
|
|
13
13
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: "-createdAt" } });
|
|
14
14
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
15
15
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
16
|
+
const [view, setView] = useState(table.get("view") || "grid");
|
|
17
|
+
const handleViewToggle = (next) => {
|
|
18
|
+
if (next === "table")
|
|
19
|
+
return;
|
|
20
|
+
setView(next);
|
|
21
|
+
table.set("view", next);
|
|
22
|
+
};
|
|
16
23
|
const sort = table.get("sort") || "-createdAt";
|
|
17
24
|
const currentPage = table.getNumber("page", 1);
|
|
18
25
|
// Pending filter state — buffered until "Apply Filters" clicked
|
|
@@ -85,5 +92,5 @@ export function ReviewsIndexListing({ initialData, variant = "public", }) {
|
|
|
85
92
|
value: opt.value,
|
|
86
93
|
label: opt.key,
|
|
87
94
|
}));
|
|
88
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set("sort", v); },
|
|
95
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), 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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: _jsxs("div", { className: "p-4 space-y-3", children: [_jsx("div", { className: "h-4 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-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] }) }, i))) })) : reviews.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No reviews found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.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: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ReviewFilters, { table: pendingTable, variant: variant }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
89
96
|
}
|
|
@@ -127,5 +127,6 @@ export function StoreAuctionsListing({ storeId, initialData }) {
|
|
|
127
127
|
isWishlisted: (productId) => wishlistedIds.has(productId),
|
|
128
128
|
};
|
|
129
129
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
|
|
130
|
-
return (_jsxs("div", { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "auctionEndDate", sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: (v) => {
|
|
130
|
+
return (_jsxs("div", { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "auctionEndDate", sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: (v) => { if (v === "table")
|
|
131
|
+
return; setView(v); table.set("view", v); }, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-4" : gridClass, wishlistActions: wishlistActions, labels: { emptyTitle: "No auctions yet", emptyDescription: "This store has no active auctions." } })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
131
132
|
}
|
|
@@ -87,5 +87,6 @@ export function StorePreOrdersListing({ storeId, initialData }) {
|
|
|
87
87
|
table.setPage(1);
|
|
88
88
|
}, [searchInput, table]);
|
|
89
89
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4";
|
|
90
|
-
return (_jsxs("div", { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "-createdAt", sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: (v) => {
|
|
90
|
+
return (_jsxs("div", { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "-createdAt", sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: (v) => { if (v === "table")
|
|
91
|
+
return; setView(v); table.set("view", v); }, onResetAll: resetAll, hasActiveState: hasActiveState }), 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))) })) : preOrders.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "This store has no pre-orders yet." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)) }, product.id))) })) : (_jsx("div", { className: gridClass, children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)) }, 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" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
91
92
|
}
|
|
@@ -4,7 +4,7 @@ import { useState, useCallback, useMemo } from "react";
|
|
|
4
4
|
import { SlidersHorizontal, X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useStores } from "../hooks/useStores";
|
|
7
|
-
import {
|
|
7
|
+
import { BulkActionBar, Pagination, ListingToolbar } from "../../../ui";
|
|
8
8
|
import { ROUTES } from "../../../next";
|
|
9
9
|
import { InteractiveStoreCard } from "./InteractiveStoreCard";
|
|
10
10
|
import { StoreFilters } from "./StoreFilters";
|
|
@@ -20,6 +20,13 @@ export function StoresIndexListing({ initialData }) {
|
|
|
20
20
|
const table = useUrlTable({ defaults: { pageSize: "24", sort: "-createdAt" } });
|
|
21
21
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
22
22
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
23
|
+
const [view, setView] = useState(table.get("view") || "grid");
|
|
24
|
+
const handleViewToggle = (next) => {
|
|
25
|
+
if (next === "table")
|
|
26
|
+
return;
|
|
27
|
+
setView(next);
|
|
28
|
+
table.set("view", next);
|
|
29
|
+
};
|
|
23
30
|
// Pending filter state — buffered until "Apply Filters" clicked
|
|
24
31
|
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
25
32
|
const pendingTable = useMemo(() => ({
|
|
@@ -101,18 +108,25 @@ export function StoresIndexListing({ initialData }) {
|
|
|
101
108
|
filters: filterParts.length > 0 ? filterParts.join(",") : undefined,
|
|
102
109
|
}, { initialData });
|
|
103
110
|
const selection = useBulkSelection({ items: stores, keyExtractor: (s) => s.id ?? s.storeSlug });
|
|
104
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search stores...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "-createdAt", sortOptions: STORE_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
105
|
-
const storeKey = store.storeSlug ?? store.id;
|
|
106
|
-
return (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(storeKey)), selectable: selection.isSelecting, isSelected: selection.isSelected(store.id ?? store.storeSlug), onSelect: (id, sel) => { void sel; selection.toggle(id); } }, storeKey));
|
|
107
|
-
}) })) }), _jsx(BulkActionsBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
111
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search stores...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "-createdAt", sortOptions: STORE_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: stores.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
108
112
|
{
|
|
109
|
-
|
|
113
|
+
id: "compare",
|
|
110
114
|
label: "Compare",
|
|
111
115
|
variant: "secondary",
|
|
112
|
-
onClick: () => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
onClick: () => { selection.clearSelection(); },
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: "visit",
|
|
120
|
+
label: "Visit Store",
|
|
121
|
+
variant: "primary",
|
|
122
|
+
disabled: selection.selectedCount !== 1,
|
|
123
|
+
onClick: () => { selection.clearSelection(); },
|
|
116
124
|
},
|
|
117
|
-
] }),
|
|
125
|
+
] }), 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: table.getNumber("page", 1), totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-4 space-y-2.5", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsx("div", { className: "h-10 w-10 rounded-lg bg-zinc-200 dark:bg-slate-700" }) }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] })] }, i))) })) : stores.length === 0 ? (_jsx("p", { className: "py-16 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No stores found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: stores.map((store) => {
|
|
126
|
+
const storeKey = store.storeSlug ?? store.id;
|
|
127
|
+
return (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(storeKey)), selectable: selection.isSelecting, isSelected: selection.isSelected(store.id ?? store.storeSlug), onSelect: (id, sel) => { void sel; selection.toggle(id); } }, storeKey));
|
|
128
|
+
}) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: stores.map((store) => {
|
|
129
|
+
const storeKey = store.storeSlug ?? store.id;
|
|
130
|
+
return (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(storeKey)), selectable: selection.isSelecting, isSelected: selection.isSelected(store.id ?? store.storeSlug), onSelect: (id, sel) => { void sel; selection.toggle(id); } }, storeKey));
|
|
131
|
+
}) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(StoreFilters, { table: pendingTable }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
118
132
|
}
|
|
@@ -9,6 +9,7 @@ export interface ListingToolbarLabels {
|
|
|
9
9
|
sort?: string;
|
|
10
10
|
gridView?: string;
|
|
11
11
|
listView?: string;
|
|
12
|
+
tableView?: string;
|
|
12
13
|
resetAll?: string;
|
|
13
14
|
selectAll?: (total: number) => string;
|
|
14
15
|
deselectAll?: string;
|
|
@@ -29,10 +30,12 @@ export interface ListingToolbarProps {
|
|
|
29
30
|
sortValue?: string;
|
|
30
31
|
sortOptions?: readonly ListingToolbarSortOption[];
|
|
31
32
|
onSortChange?: (value: string) => void;
|
|
32
|
-
/** Grid / list view toggle */
|
|
33
|
-
view?: "grid" | "list";
|
|
34
|
-
onViewChange?: (view: "grid" | "list") => void;
|
|
33
|
+
/** Grid / list / table view toggle */
|
|
34
|
+
view?: "grid" | "list" | "table";
|
|
35
|
+
onViewChange?: (view: "grid" | "list" | "table") => void;
|
|
35
36
|
hideViewToggle?: boolean;
|
|
37
|
+
/** When true, adds a third Table button to the view toggle group (admin pages only). */
|
|
38
|
+
showTableView?: boolean;
|
|
36
39
|
/** Reset all toolbar state to defaults */
|
|
37
40
|
onResetAll?: () => void;
|
|
38
41
|
hasActiveState?: boolean;
|
|
@@ -51,4 +54,4 @@ export interface ListingToolbarProps {
|
|
|
51
54
|
labels?: ListingToolbarLabels;
|
|
52
55
|
className?: string;
|
|
53
56
|
}
|
|
54
|
-
export declare function ListingToolbar({ filterCount, onFiltersClick, searchValue, searchPlaceholder, onSearchChange, onSearchCommit, onSearchKeyDown, sortValue, sortOptions, onSortChange, view, onViewChange, hideViewToggle, onResetAll, hasActiveState, bulkMode, bulkSelectedCount, bulkTotalCount, onBulkSelectAll, onBulkClear, extra, labels, className, }: ListingToolbarProps): import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
export declare function ListingToolbar({ filterCount, onFiltersClick, searchValue, searchPlaceholder, onSearchChange, onSearchCommit, onSearchKeyDown, sortValue, sortOptions, onSortChange, view, onViewChange, hideViewToggle, showTableView, onResetAll, hasActiveState, bulkMode, bulkSelectedCount, bulkTotalCount, onBulkSelectAll, onBulkClear, extra, labels, className, }: ListingToolbarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { CheckSquare, Search, SlidersHorizontal, LayoutGrid, List, RotateCcw, Square, } from "lucide-react";
|
|
3
|
+
import { CheckSquare, Search, SlidersHorizontal, LayoutGrid, List, Table2, RotateCcw, Square, } from "lucide-react";
|
|
4
4
|
import { SortDropdown } from "./SortDropdown";
|
|
5
5
|
const DEFAULT_LABELS = {
|
|
6
6
|
search: "Search",
|
|
@@ -8,6 +8,7 @@ const DEFAULT_LABELS = {
|
|
|
8
8
|
sort: "Sort",
|
|
9
9
|
gridView: "Grid view",
|
|
10
10
|
listView: "List view",
|
|
11
|
+
tableView: "Table view",
|
|
11
12
|
resetAll: "Reset all",
|
|
12
13
|
selectAll: (total) => `Select All (${total})`,
|
|
13
14
|
deselectAll: "Deselect All",
|
|
@@ -17,7 +18,7 @@ const DEFAULT_LABELS = {
|
|
|
17
18
|
const VIEW_BTN_BASE = "p-1.5 sm:p-2 transition-colors";
|
|
18
19
|
const VIEW_BTN_ACTIVE = "bg-[var(--appkit-color-primary,theme(colors.violet.600))] text-white";
|
|
19
20
|
const VIEW_BTN_INACTIVE = "text-zinc-500 hover:bg-zinc-50 dark:hover:bg-slate-800 dark:text-zinc-400";
|
|
20
|
-
export function ListingToolbar({ filterCount = 0, onFiltersClick, searchValue = "", searchPlaceholder = "Search…", onSearchChange, onSearchCommit, onSearchKeyDown, sortValue, sortOptions, onSortChange, view = "grid", onViewChange, hideViewToggle = false, onResetAll, hasActiveState = false, bulkMode = false, bulkSelectedCount = 0, bulkTotalCount = 0, onBulkSelectAll, onBulkClear, extra, labels, className = "", }) {
|
|
21
|
+
export function ListingToolbar({ filterCount = 0, onFiltersClick, searchValue = "", searchPlaceholder = "Search…", onSearchChange, onSearchCommit, onSearchKeyDown, sortValue, sortOptions, onSortChange, view = "grid", onViewChange, hideViewToggle = false, showTableView = false, onResetAll, hasActiveState = false, bulkMode = false, bulkSelectedCount = 0, bulkTotalCount = 0, onBulkSelectAll, onBulkClear, extra, labels, className = "", }) {
|
|
21
22
|
const l = { ...DEFAULT_LABELS, ...labels };
|
|
22
23
|
const handleKeyDown = (e) => {
|
|
23
24
|
if (onSearchKeyDown) {
|
|
@@ -30,5 +31,5 @@ export function ListingToolbar({ filterCount = 0, onFiltersClick, searchValue =
|
|
|
30
31
|
const allSelected = bulkTotalCount > 0 && bulkSelectedCount === bulkTotalCount;
|
|
31
32
|
return (_jsx("div", { className: `sticky top-[var(--header-height,0px)] z-20 border-b border-zinc-200 dark:border-slate-700 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm py-2 px-3 sm:py-2.5 sm:px-4 ${className}`, children: _jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-2.5", children: [bulkMode ? (_jsxs("div", { className: "flex flex-1 items-center gap-2", children: [_jsxs("button", { type: "button", onClick: onBulkSelectAll, className: "flex items-center gap-1.5 rounded-lg border border-zinc-300 dark:border-slate-600 px-3 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [allSelected
|
|
32
33
|
? _jsx(CheckSquare, { className: "h-4 w-4 text-[var(--appkit-color-primary,theme(colors.violet.600))]" })
|
|
33
|
-
: _jsx(Square, { className: "h-4 w-4" }), allSelected ? l.deselectAll : l.selectAll(bulkTotalCount)] }), _jsx("span", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: l.selected(bulkSelectedCount) }), _jsx("button", { type: "button", onClick: onBulkClear, className: "text-xs text-zinc-400 hover:text-rose-500 dark:text-zinc-500 transition-colors", children: l.clearSelection })] })) : onSearchChange ? (_jsxs("div", { className: "flex flex-1 items-center overflow-hidden rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-900 min-w-0", children: [_jsx("input", { type: "text", value: searchValue, onChange: (e) => onSearchChange(e.target.value), onKeyDown: handleKeyDown, placeholder: searchPlaceholder, className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none" }), _jsx("button", { type: "button", onClick: onSearchCommit, className: "flex shrink-0 items-center justify-center px-2.5 py-2 text-zinc-400 hover:text-[var(--appkit-color-primary,theme(colors.violet.600))] transition-colors", "aria-label": l.search, children: _jsx(Search, { className: "h-4 w-4" }) })] })) : null, _jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2 shrink-0", children: [onFiltersClick && (_jsxs("button", { type: "button", onClick: onFiltersClick, className: "relative flex shrink-0 items-center gap-1.5 rounded-lg border border-zinc-300 dark:border-slate-600 px-2.5 py-1.5 sm:px-3.5 sm:py-2 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsx("span", { className: "hidden sm:inline", children: l.filters }), filterCount > 0 && (_jsx("span", { className: "absolute -top-1.5 -right-1.5 flex h-4 w-4 items-center justify-center rounded-full bg-[var(--appkit-color-primary,theme(colors.violet.600))] text-[10px] font-bold text-white", children: filterCount }))] })), sortOptions && sortValue !== undefined && onSortChange && (_jsxs("div", { className: "flex items-center gap-1.5 text-sm text-zinc-500 dark:text-zinc-400", children: [_jsx("span", { className: "hidden md:inline whitespace-nowrap text-xs", children: l.sort }), _jsx(SortDropdown, { value: sortValue, onChange: onSortChange, options: sortOptions })] })), !hideViewToggle && onViewChange && (_jsxs("div", { className: "flex items-center rounded-lg border border-zinc-300 dark:border-slate-600 overflow-hidden", children: [_jsx("button", { type: "button", onClick: () => onViewChange("grid"), "aria-label": l.gridView, className: `${VIEW_BTN_BASE} ${view === "grid" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(LayoutGrid, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => onViewChange("list"), "aria-label": l.listView, className: `${VIEW_BTN_BASE} ${view === "list" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(List, { className: "h-4 w-4" }) })] })), onResetAll && hasActiveState && (_jsx("button", { type: "button", onClick: onResetAll, "aria-label": l.resetAll, title: l.resetAll, className: "flex shrink-0 items-center justify-center rounded-lg border border-zinc-300 dark:border-slate-600 p-1.5 sm:p-2 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:bg-slate-800 hover:text-rose-500 dark:hover:text-rose-400 transition-colors", children: _jsx(RotateCcw, { className: "h-4 w-4" }) })), extra] })] }) }));
|
|
34
|
+
: _jsx(Square, { className: "h-4 w-4" }), allSelected ? l.deselectAll : l.selectAll(bulkTotalCount)] }), _jsx("span", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: l.selected(bulkSelectedCount) }), _jsx("button", { type: "button", onClick: onBulkClear, className: "text-xs text-zinc-400 hover:text-rose-500 dark:text-zinc-500 transition-colors", children: l.clearSelection })] })) : onSearchChange ? (_jsxs("div", { className: "flex flex-1 items-center overflow-hidden rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-900 min-w-0", children: [_jsx("input", { type: "text", value: searchValue, onChange: (e) => onSearchChange(e.target.value), onKeyDown: handleKeyDown, placeholder: searchPlaceholder, className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none" }), _jsx("button", { type: "button", onClick: onSearchCommit, className: "flex shrink-0 items-center justify-center px-2.5 py-2 text-zinc-400 hover:text-[var(--appkit-color-primary,theme(colors.violet.600))] transition-colors", "aria-label": l.search, children: _jsx(Search, { className: "h-4 w-4" }) })] })) : null, _jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2 shrink-0", children: [onFiltersClick && (_jsxs("button", { type: "button", onClick: onFiltersClick, className: "relative flex shrink-0 items-center gap-1.5 rounded-lg border border-zinc-300 dark:border-slate-600 px-2.5 py-1.5 sm:px-3.5 sm:py-2 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsx("span", { className: "hidden sm:inline", children: l.filters }), filterCount > 0 && (_jsx("span", { className: "absolute -top-1.5 -right-1.5 flex h-4 w-4 items-center justify-center rounded-full bg-[var(--appkit-color-primary,theme(colors.violet.600))] text-[10px] font-bold text-white", children: filterCount }))] })), sortOptions && sortValue !== undefined && onSortChange && (_jsxs("div", { className: "flex items-center gap-1.5 text-sm text-zinc-500 dark:text-zinc-400", children: [_jsx("span", { className: "hidden md:inline whitespace-nowrap text-xs", children: l.sort }), _jsx(SortDropdown, { value: sortValue, onChange: onSortChange, options: sortOptions })] })), !hideViewToggle && onViewChange && (_jsxs("div", { className: "flex items-center rounded-lg border border-zinc-300 dark:border-slate-600 overflow-hidden", children: [_jsx("button", { type: "button", onClick: () => onViewChange("grid"), "aria-label": l.gridView, className: `${VIEW_BTN_BASE} ${view === "grid" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(LayoutGrid, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => onViewChange("list"), "aria-label": l.listView, className: `${VIEW_BTN_BASE} ${view === "list" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(List, { className: "h-4 w-4" }) }), showTableView && (_jsx("button", { type: "button", onClick: () => onViewChange("table"), "aria-label": l.tableView, className: `${VIEW_BTN_BASE} ${view === "table" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(Table2, { className: "h-4 w-4" }) }))] })), onResetAll && hasActiveState && (_jsx("button", { type: "button", onClick: onResetAll, "aria-label": l.resetAll, title: l.resetAll, className: "flex shrink-0 items-center justify-center rounded-lg border border-zinc-300 dark:border-slate-600 p-1.5 sm:p-2 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:bg-slate-800 hover:text-rose-500 dark:hover:text-rose-400 transition-colors", children: _jsx(RotateCcw, { className: "h-4 w-4" }) })), extra] })] }) }));
|
|
34
35
|
}
|
|
@@ -14,5 +14,5 @@ export function SiteLogo({ className = "h-7", title = "LetItRip.in", variant = "
|
|
|
14
14
|
_jsx("img", { src: src, alt: title, className: `block w-auto object-contain ${className}` }));
|
|
15
15
|
}
|
|
16
16
|
const fill = variant === "gradient" ? `url(#${GRADIENT_ID})` : "currentColor";
|
|
17
|
-
return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0
|
|
17
|
+
return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 214 56", role: "img", "aria-label": title, className: `block w-auto ${className}`, children: [_jsx("title", { children: title }), variant === "gradient" && (_jsx("defs", { children: _jsxs("linearGradient", { id: GRADIENT_ID, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [_jsx("stop", { offset: "0%", style: { stopColor: "var(--appkit-color-primary-700, #1343de)" } }), _jsx("stop", { offset: "55%", style: { stopColor: "var(--appkit-color-cobalt, #3570fc)" } }), _jsx("stop", { offset: "100%", style: { stopColor: "var(--appkit-color-secondary-400, #84e122)" } })] }) })), _jsx("text", { x: "0", y: "44", fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif", fontWeight: "800", fontSize: "44", letterSpacing: "-1.5", fill: fill, children: "LetItRip" }), _jsx("rect", { x: "169", y: "5", width: "43", height: "21", rx: "10.5", fill: fill, opacity: "0.12" }), _jsx("text", { x: "174", y: "21", fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif", fontWeight: "700", fontSize: "15", letterSpacing: "0.2", fill: fill, children: ".in" })] }));
|
|
18
18
|
}
|