@mohasinac/appkit 2.7.9 → 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/_internal/shared/config/index.d.ts +1 -1
- package/dist/_internal/shared/config/schema.d.ts +95 -1
- package/dist/client-entry.d.ts +1 -1
- package/dist/client.d.ts +3 -0
- package/dist/client.js +5 -0
- package/dist/configs/next.js +17 -1
- package/dist/features/account/components/UserOffersPanel.js +17 -5
- package/dist/features/auctions/components/AuctionDetailPageView.js +17 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +10 -5
- package/dist/features/blog/components/BlogIndexListing.js +8 -1
- package/dist/features/categories/components/BundleBuyNowCta.js +13 -6
- package/dist/features/categories/components/CategoriesIndexListing.js +9 -2
- package/dist/features/events/components/EventPollWidget.js +7 -1
- package/dist/features/events/components/EventsIndexListing.js +8 -1
- package/dist/features/events/components/SpinWheelView.js +12 -5
- package/dist/features/layout/TitleBarLayout.js +1 -1
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +10 -3
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +11 -7
- package/dist/features/products/components/AuctionsIndexListing.js +31 -2
- package/dist/features/products/components/MakeOfferButton.js +16 -8
- package/dist/features/products/components/PrizeDrawsIndexListing.js +5 -2
- package/dist/features/products/components/PrizeRevealModal.js +18 -5
- package/dist/features/products/components/ProductDetailPageView.js +23 -1
- package/dist/features/products/components/ProductsIndexListing.js +12 -8
- package/dist/features/reviews/components/ReviewsIndexListing.js +8 -1
- package/dist/features/seller/components/SellerOffersPanel.js +17 -5
- 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/features/wishlist/components/WishlistToggleButton.js +26 -12
- package/dist/http/api-handler.js +7 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +2 -0
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/BaseListingCard.js +8 -6
- package/dist/ui/components/Button.style.css +8 -5
- package/dist/ui/components/ListingLayout.style.css +19 -9
- package/dist/ui/components/ListingToolbar.d.ts +7 -4
- package/dist/ui/components/ListingToolbar.js +4 -3
- package/dist/ui/components/LoginRequiredModal.d.ts +9 -0
- package/dist/ui/components/LoginRequiredModal.js +11 -0
- package/dist/ui/components/SiteLogo.js +1 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/utils/auth-error.d.ts +8 -0
- package/dist/utils/auth-error.js +20 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useEffect, useState, useTransition } from "react";
|
|
4
|
-
import { Alert, Badge, Button, Div, Heading, Input, Spinner, Text } from "../../../ui";
|
|
4
|
+
import { Alert, Badge, Button, Div, Heading, Input, LoginRequiredModal, Spinner, Text } from "../../../ui";
|
|
5
|
+
import { isAuthError } from "../../../utils/auth-error";
|
|
5
6
|
function formatRupees(amount) {
|
|
6
7
|
if (amount === undefined || amount === null)
|
|
7
8
|
return "—";
|
|
@@ -46,7 +47,7 @@ function statusVariant(status) {
|
|
|
46
47
|
default: return "default";
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
|
-
function OfferCard({ offer, onRespond, onUpdate }) {
|
|
50
|
+
function OfferCard({ offer, onRespond, onUpdate, onNeedsLogin }) {
|
|
50
51
|
const [uiState, setUiState] = useState("idle");
|
|
51
52
|
const [counterInput, setCounterInput] = useState("");
|
|
52
53
|
const [sellerNote, setSellerNote] = useState("");
|
|
@@ -68,7 +69,12 @@ function OfferCard({ offer, onRespond, onUpdate }) {
|
|
|
68
69
|
setUiState("idle");
|
|
69
70
|
}
|
|
70
71
|
catch (err) {
|
|
71
|
-
|
|
72
|
+
if (isAuthError(err)) {
|
|
73
|
+
onNeedsLogin();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
setError(err instanceof Error ? err.message : "Something went wrong.");
|
|
77
|
+
}
|
|
72
78
|
}
|
|
73
79
|
});
|
|
74
80
|
}
|
|
@@ -100,6 +106,7 @@ export function SellerOffersPanel({ fetchEndpoint = "/api/store/offers", onRespo
|
|
|
100
106
|
const [offers, setOffers] = useState([]);
|
|
101
107
|
const [loading, setLoading] = useState(true);
|
|
102
108
|
const [fetchError, setFetchError] = useState("");
|
|
109
|
+
const [showLoginModal, setShowLoginModal] = useState(false);
|
|
103
110
|
const [statusFilter, setStatusFilter] = useState("all");
|
|
104
111
|
const loadOffers = useCallback(async () => {
|
|
105
112
|
setLoading(true);
|
|
@@ -109,8 +116,13 @@ export function SellerOffersPanel({ fetchEndpoint = "/api/store/offers", onRespo
|
|
|
109
116
|
? fetchEndpoint
|
|
110
117
|
: `${fetchEndpoint}?status=${statusFilter}`;
|
|
111
118
|
const res = await fetch(url);
|
|
112
|
-
if (!res.ok)
|
|
119
|
+
if (!res.ok) {
|
|
120
|
+
if (res.status === 401 || res.status === 403) {
|
|
121
|
+
setShowLoginModal(true);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
113
124
|
throw new Error(`Error ${res.status}`);
|
|
125
|
+
}
|
|
114
126
|
const json = (await res.json());
|
|
115
127
|
setOffers(json.items ?? json.offers ?? []);
|
|
116
128
|
}
|
|
@@ -134,5 +146,5 @@ export function SellerOffersPanel({ fetchEndpoint = "/api/store/offers", onRespo
|
|
|
134
146
|
{ value: "expired", label: "Expired" },
|
|
135
147
|
];
|
|
136
148
|
const pending = offers.filter((o) => o.status === "pending").length;
|
|
137
|
-
return (_jsxs(Div, { className: `space-y-4 ${className}`, children: [_jsxs(Div, { className: "flex items-center justify-between flex-wrap gap-2", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-zinc-900 dark:text-zinc-100", children: "Offers Received" }), pending > 0 && (_jsxs(Text, { className: "text-xs text-amber-600 dark:text-amber-400 mt-0.5", children: [pending, " pending offer", pending > 1 ? "s" : "", " awaiting your response"] }))] }), _jsx(Button, { size: "sm", variant: "ghost", onClick: loadOffers, disabled: loading, className: "border border-zinc-300 dark:border-zinc-600 text-xs", children: loading ? "Refreshing…" : "Refresh" })] }), _jsx(Div, { className: "flex gap-1 flex-wrap", children: STATUS_FILTERS.map((f) => (_jsx(Button, { size: "sm", variant: statusFilter === f.value ? "primary" : "ghost", onClick: () => setStatusFilter(f.value), className: "text-xs px-2 py-1", children: f.label }, f.value))) }), fetchError && _jsx(Alert, { variant: "error", children: _jsx(Text, { className: "text-sm", children: fetchError }) }), loading && (_jsx(Div, { className: "flex justify-center py-12", children: _jsx(Spinner, { size: "lg" }) })), !loading && offers.length === 0 && (_jsx(Div, { className: "text-center py-12", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500 text-sm", children: "No offers found" }) })), !loading && offers.length > 0 && (_jsx(Div, { className: "space-y-3", children: offers.map((offer) => (_jsx(OfferCard, { offer: offer, onRespond: onRespond, onUpdate: handleUpdate }, offer.id))) }))] }));
|
|
149
|
+
return (_jsxs(Div, { className: `space-y-4 ${className}`, children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to manage offers. Please log in or create an account to continue." }), _jsxs(Div, { className: "flex items-center justify-between flex-wrap gap-2", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-zinc-900 dark:text-zinc-100", children: "Offers Received" }), pending > 0 && (_jsxs(Text, { className: "text-xs text-amber-600 dark:text-amber-400 mt-0.5", children: [pending, " pending offer", pending > 1 ? "s" : "", " awaiting your response"] }))] }), _jsx(Button, { size: "sm", variant: "ghost", onClick: loadOffers, disabled: loading, className: "border border-zinc-300 dark:border-zinc-600 text-xs", children: loading ? "Refreshing…" : "Refresh" })] }), _jsx(Div, { className: "flex gap-1 flex-wrap", children: STATUS_FILTERS.map((f) => (_jsx(Button, { size: "sm", variant: statusFilter === f.value ? "primary" : "ghost", onClick: () => setStatusFilter(f.value), className: "text-xs px-2 py-1", children: f.label }, f.value))) }), fetchError && _jsx(Alert, { variant: "error", children: _jsx(Text, { className: "text-sm", children: fetchError }) }), loading && (_jsx(Div, { className: "flex justify-center py-12", children: _jsx(Spinner, { size: "lg" }) })), !loading && offers.length === 0 && (_jsx(Div, { className: "text-center py-12", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500 text-sm", children: "No offers found" }) })), !loading && offers.length > 0 && (_jsx(Div, { className: "space-y-3", children: offers.map((offer) => (_jsx(OfferCard, { offer: offer, onRespond: onRespond, onUpdate: handleUpdate, onNeedsLogin: () => setShowLoginModal(true) }, offer.id))) }))] }));
|
|
138
150
|
}
|
|
@@ -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
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Button, LoginRequiredModal } from "../../../ui";
|
|
5
|
+
import { isAuthError } from "../../../utils/auth-error";
|
|
3
6
|
const sizeClasses = {
|
|
4
7
|
sm: "w-7 h-7",
|
|
5
8
|
md: "w-9 h-9",
|
|
@@ -7,14 +10,25 @@ const sizeClasses = {
|
|
|
7
10
|
};
|
|
8
11
|
export function WishlistToggleButton({ inWishlist, isLoading = false, onToggle, addLabel, removeLabel, className = "", size = "md", }) {
|
|
9
12
|
const label = inWishlist ? removeLabel : addLabel;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
const [showLoginModal, setShowLoginModal] = useState(false);
|
|
14
|
+
async function handleClick(e) {
|
|
15
|
+
try {
|
|
16
|
+
await onToggle(e);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
if (isAuthError(err)) {
|
|
20
|
+
setShowLoginModal(true);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to save items to your wishlist. Please log in or create an account." }), _jsx(Button, { type: "button", onClick: handleClick, disabled: isLoading, "aria-label": label, title: label, className: `
|
|
25
|
+
flex items-center justify-center rounded-full
|
|
26
|
+
transition-all duration-150
|
|
27
|
+
${inWishlist
|
|
28
|
+
? "bg-rose-50 text-rose-500 hover:bg-rose-100"
|
|
29
|
+
: "bg-white/80 text-zinc-400 hover:text-rose-400"}
|
|
30
|
+
${sizeClasses[size]}
|
|
31
|
+
${isLoading ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
|
|
32
|
+
${className}
|
|
33
|
+
`, children: _jsx("svg", { className: "w-4/6 h-4/6", viewBox: "0 0 24 24", fill: inWishlist ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" }) }) })] }));
|
|
20
34
|
}
|
package/dist/http/api-handler.js
CHANGED
|
@@ -34,7 +34,13 @@ async function getUserFromRequest(request) {
|
|
|
34
34
|
if (!sessionCookie)
|
|
35
35
|
return null;
|
|
36
36
|
const providers = getProviders();
|
|
37
|
-
|
|
37
|
+
let decoded;
|
|
38
|
+
try {
|
|
39
|
+
decoded = await providers.session.verifySession(sessionCookie);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
throw new AuthenticationError(ERROR_MESSAGES.USER.NOT_AUTHENTICATED);
|
|
43
|
+
}
|
|
38
44
|
const db = providers.db;
|
|
39
45
|
if (!db) {
|
|
40
46
|
throw new Error("Database provider is not registered");
|
package/dist/index.d.ts
CHANGED
|
@@ -2931,6 +2931,9 @@ export { SlottedListingView, DetailViewShell, StackedViewShell } from "./ui/inde
|
|
|
2931
2931
|
export { buildColumns, createColumnBuilder } from "./ui/index";
|
|
2932
2932
|
export { renderBoolean, renderCurrency, renderCurrencyCompact, renderCount, renderNullable } from "./ui/index";
|
|
2933
2933
|
export { Ol } from "./ui/index";
|
|
2934
|
+
export { LoginRequiredModal } from "./ui/components/LoginRequiredModal";
|
|
2935
|
+
export type { LoginRequiredModalProps } from "./ui/components/LoginRequiredModal";
|
|
2936
|
+
export { isAuthError } from "./utils/auth-error";
|
|
2934
2937
|
export { API_ENDPOINTS, API_ROUTES, LOGS_ENDPOINTS, AUTH_ENDPOINTS, ACCOUNT_ENDPOINTS, NOTIFICATIONS_ENDPOINTS, ADMIN_ENDPOINTS, CHAT_ENDPOINTS, AUCTION_ENDPOINTS, BID_ENDPOINTS, CART_ENDPOINTS, CATEGORY_ENDPOINTS, CHECKOUT_ENDPOINTS, PAYMENT_ENDPOINTS, COPILOT_ENDPOINTS, CORPORATE_ENDPOINTS, EVENT_ENDPOINTS, FAQ_ENDPOINTS, HOMEPAGE_ENDPOINTS, LOYALTY_ENDPOINTS, MEDIA_ENDPOINTS, ORDER_ENDPOINTS, PREORDER_ENDPOINTS, PRODUCT_ENDPOINTS, REVIEW_ENDPOINTS, SEARCH_ENDPOINTS, SELLER_ENDPOINTS, BLOG_ENDPOINTS, WISHLIST_ENDPOINTS, DEMO_ENDPOINTS, COLLECTION_CACHE_PATHS, resolveEndpoint, resolveEndpointFn, ROUTES, PUBLIC_ROUTES, PROTECTED_ROUTES, AUTH_ROUTES, WISHLIST_MAX, HISTORY_MAX, CART_MAX_ITEMS, WISHLIST_DOC_ID, HISTORY_DOC_ID, WISHLIST_COLLECTION, HISTORY_COLLECTION, } from "./constants/index";
|
|
2935
2938
|
export { WishlistFullError } from "./features/wishlist/server";
|
|
2936
2939
|
export { useHistory, useHistoryMergeOnLogin, getGuestHistory, trackGuestHistory, removeGuestHistoryItem, clearGuestHistory, getGuestHistoryCount, type GuestHistoryItem, type GuestHistoryType, type UserHistoryItem, type HistoryProductType, type HistoryItemSnapshot, type TrackArgs as TrackHistoryArgs, } from "./features/history";
|
package/dist/index.js
CHANGED
|
@@ -5362,6 +5362,8 @@ export { SlottedListingView, DetailViewShell, StackedViewShell } from "./ui/inde
|
|
|
5362
5362
|
export { buildColumns, createColumnBuilder } from "./ui/index";
|
|
5363
5363
|
export { renderBoolean, renderCurrency, renderCurrencyCompact, renderCount, renderNullable } from "./ui/index";
|
|
5364
5364
|
export { Ol } from "./ui/index";
|
|
5365
|
+
export { LoginRequiredModal } from "./ui/components/LoginRequiredModal";
|
|
5366
|
+
export { isAuthError } from "./utils/auth-error";
|
|
5365
5367
|
// -- Missing constants ----------------------------------------------------------
|
|
5366
5368
|
export { API_ENDPOINTS, API_ROUTES, LOGS_ENDPOINTS, AUTH_ENDPOINTS, ACCOUNT_ENDPOINTS, NOTIFICATIONS_ENDPOINTS, ADMIN_ENDPOINTS, CHAT_ENDPOINTS, AUCTION_ENDPOINTS, BID_ENDPOINTS, CART_ENDPOINTS, CATEGORY_ENDPOINTS, CHECKOUT_ENDPOINTS, PAYMENT_ENDPOINTS, COPILOT_ENDPOINTS, CORPORATE_ENDPOINTS, EVENT_ENDPOINTS, FAQ_ENDPOINTS, HOMEPAGE_ENDPOINTS, LOYALTY_ENDPOINTS, MEDIA_ENDPOINTS, ORDER_ENDPOINTS, PREORDER_ENDPOINTS, PRODUCT_ENDPOINTS, REVIEW_ENDPOINTS, SEARCH_ENDPOINTS, SELLER_ENDPOINTS, BLOG_ENDPOINTS, WISHLIST_ENDPOINTS, DEMO_ENDPOINTS, COLLECTION_CACHE_PATHS, resolveEndpoint, resolveEndpointFn, ROUTES, PUBLIC_ROUTES, PROTECTED_ROUTES, AUTH_ROUTES, WISHLIST_MAX, HISTORY_MAX, CART_MAX_ITEMS, WISHLIST_DOC_ID, HISTORY_DOC_ID, WISHLIST_COLLECTION, HISTORY_COLLECTION, } from "./constants/index";
|
|
5367
5369
|
// Wishlist server errors (server-only via tree-shake — class only, no firebase-admin at module scope)
|
package/dist/server.d.ts
CHANGED
package/dist/server.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// Server-only public exports
|
|
2
|
+
// isAuthError - Pure util — detects auth errors from caught exceptions (usable in server actions).
|
|
3
|
+
export { isAuthError } from "./utils/auth-error";
|
|
2
4
|
// [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
|
|
3
5
|
// ADDRESS_FIXTURES - Constant used across modules.
|
|
4
6
|
export { ADDRESS_FIXTURES } from "./seed/index";
|