@mohasinac/appkit 2.7.10 → 2.7.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/server/jobs/core/assignSpinPrize.js +8 -7
- package/dist/_internal/server/jobs/core/bundleStockSync.js +11 -6
- package/dist/_internal/server/jobs/core/countersReconcile.js +3 -2
- package/dist/_internal/server/jobs/core/onProductStockChange.js +12 -7
- package/dist/_internal/server/jobs/core/onProductWrite.js +3 -2
- package/dist/_internal/server/jobs/core/prizeRevealClose.js +7 -5
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +11 -6
- package/dist/_internal/server/jobs/core/prizeRevealOpen.js +13 -7
- package/dist/_internal/server/jobs/core/prizeRevealReminder.js +9 -4
- package/dist/_internal/server/jobs/core/triggerEventRaffle.js +3 -2
- package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
- package/dist/_internal/shared/features/events/schema.d.ts +4 -4
- package/dist/_internal/shared/features/products/schema.d.ts +20 -20
- package/dist/_internal/shared/features/promotions/schema.d.ts +2 -2
- package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
- package/dist/configs/next.js +17 -1
- package/dist/constants/field-names.d.ts +813 -0
- package/dist/constants/field-names.js +918 -0
- package/dist/constants/sort.d.ts +7 -0
- package/dist/constants/sort.js +5 -0
- package/dist/constants/table-keys.d.ts +48 -0
- package/dist/constants/table-keys.js +46 -0
- package/dist/features/account/components/AddressesIndexListing.js +9 -6
- package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -1
- package/dist/features/admin/components/AdminBidsView.js +4 -1
- package/dist/features/admin/components/AdminBlogView.js +9 -2
- package/dist/features/admin/components/AdminBrandsView.js +9 -2
- package/dist/features/admin/components/AdminCarouselView.js +4 -1
- package/dist/features/admin/components/AdminCartsView.js +4 -1
- package/dist/features/admin/components/AdminCategoriesView.js +9 -2
- package/dist/features/admin/components/AdminContactView.js +4 -1
- package/dist/features/admin/components/AdminCouponsView.js +4 -1
- package/dist/features/admin/components/AdminFaqsView.js +5 -1
- package/dist/features/admin/components/AdminFeaturesView.js +5 -2
- package/dist/features/admin/components/AdminHistoryView.js +5 -1
- package/dist/features/admin/components/AdminNewsletterView.js +4 -1
- package/dist/features/admin/components/AdminNotificationsView.js +4 -1
- package/dist/features/admin/components/AdminOrdersView.js +14 -12
- package/dist/features/admin/components/AdminPayoutsView.js +4 -1
- package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -1
- package/dist/features/admin/components/AdminProductsView.js +14 -3
- package/dist/features/admin/components/AdminReturnRequestsView.js +4 -1
- package/dist/features/admin/components/AdminReviewsView.js +4 -1
- package/dist/features/admin/components/AdminScammersView.js +4 -1
- package/dist/features/admin/components/AdminSessionsView.js +4 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +5 -1
- package/dist/features/admin/components/AdminStoresView.js +10 -3
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +4 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +4 -1
- package/dist/features/admin/components/AdminTeamView.js +4 -1
- package/dist/features/admin/components/AdminUsersView.js +9 -12
- package/dist/features/admin/components/AdminViewCards.d.ts +12 -0
- package/dist/features/admin/components/AdminViewCards.js +53 -0
- package/dist/features/admin/components/AdminWishlistsView.js +4 -1
- package/dist/features/auctions/repository/bid.repository.js +1 -6
- package/dist/features/auctions/schemas/index.d.ts +4 -4
- package/dist/features/blog/actions/blog-actions.d.ts +16 -16
- package/dist/features/blog/components/BlogFilters.d.ts +28 -28
- package/dist/features/blog/components/BlogFilters.js +18 -15
- package/dist/features/blog/components/BlogIndexListing.js +24 -13
- package/dist/features/blog/schemas/index.d.ts +10 -10
- package/dist/features/categories/components/CategoriesIndexListing.js +30 -19
- package/dist/features/categories/schemas/index.d.ts +2 -2
- package/dist/features/events/components/AdminEventsView.js +5 -1
- package/dist/features/events/components/EventFilters.d.ts +24 -24
- package/dist/features/events/components/EventFilters.js +16 -13
- package/dist/features/events/components/EventStatusBadge.js +1 -0
- package/dist/features/events/components/EventsIndexListing.js +36 -24
- package/dist/features/events/components/EventsListPageView.js +13 -10
- package/dist/features/events/schemas/firestore.d.ts +1 -0
- package/dist/features/events/schemas/firestore.js +1 -0
- package/dist/features/events/schemas/index.d.ts +1 -1
- package/dist/features/events/types/index.d.ts +1 -1
- package/dist/features/faq/actions/faq-actions.d.ts +10 -10
- package/dist/features/faq/schemas/index.d.ts +4 -4
- package/dist/features/layout/TitleBarLayout.js +1 -1
- package/dist/features/orders/components/OrderFilters.d.ts +20 -20
- package/dist/features/orders/components/OrderFilters.js +33 -30
- package/dist/features/orders/repository/orders.repository.js +1 -15
- package/dist/features/orders/schemas/index.d.ts +2 -2
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +44 -36
- package/dist/features/pre-orders/schemas/index.d.ts +3 -3
- package/dist/features/products/components/AuctionsIndexListing.js +63 -30
- package/dist/features/products/components/BidHistory.d.ts +0 -1
- package/dist/features/products/components/BidHistory.js +1 -1
- package/dist/features/products/components/PrizeDrawsIndexListing.js +44 -37
- package/dist/features/products/components/ProductDetailPageView.js +1 -1
- package/dist/features/products/components/ProductFilters.d.ts +20 -20
- package/dist/features/products/components/ProductFilters.js +62 -59
- package/dist/features/products/components/ProductsIndexListing.js +37 -29
- package/dist/features/products/components/ProductsIndexPageView.js +18 -10
- package/dist/features/products/hooks/useProducts.js +2 -0
- package/dist/features/products/repository/products.repository.js +2 -22
- package/dist/features/products/schemas/index.d.ts +28 -28
- package/dist/features/products/schemas/product-features.validators.d.ts +12 -12
- package/dist/features/products/types/index.d.ts +2 -0
- package/dist/features/promotions/components/CouponsIndexListing.js +21 -17
- package/dist/features/promotions/schemas/index.d.ts +4 -4
- package/dist/features/reviews/components/ReviewDetailShell.js +6 -5
- package/dist/features/reviews/components/ReviewFilters.d.ts +16 -16
- package/dist/features/reviews/components/ReviewFilters.js +10 -7
- package/dist/features/reviews/components/ReviewsIndexListing.js +25 -14
- package/dist/features/reviews/components/ReviewsList.d.ts +5 -2
- package/dist/features/reviews/components/ReviewsList.js +11 -5
- package/dist/features/reviews/schemas/firestore.d.ts +1 -0
- package/dist/features/reviews/schemas/index.d.ts +8 -8
- package/dist/features/reviews/types/index.d.ts +1 -0
- package/dist/features/search/schemas/index.d.ts +2 -2
- package/dist/features/seller/components/SellerAuctionsView.js +5 -1
- package/dist/features/seller/components/SellerBidsView.js +4 -1
- package/dist/features/seller/components/SellerCouponsView.js +4 -1
- package/dist/features/seller/components/SellerOffersView.js +5 -1
- package/dist/features/seller/components/SellerOrdersView.js +4 -1
- package/dist/features/seller/components/SellerPayoutsView.js +5 -1
- package/dist/features/seller/components/SellerPreOrdersView.js +4 -1
- package/dist/features/seller/components/SellerPrizeDrawsView.js +4 -1
- package/dist/features/seller/components/SellerProductsView.js +4 -1
- package/dist/features/seller/schemas/index.d.ts +10 -10
- package/dist/features/stores/api/route.js +10 -1
- package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
- package/dist/features/stores/components/StorePreOrdersListing.js +2 -1
- package/dist/features/stores/components/StoreReviewsListing.js +1 -1
- package/dist/features/stores/components/StoresIndexListing.js +45 -27
- package/dist/features/stores/schemas/firestore.d.ts +1 -0
- package/dist/features/stores/schemas/index.d.ts +4 -4
- package/dist/index.d.ts +8 -1
- package/dist/index.js +12 -1
- package/dist/react/hooks/useUrlTable.js +2 -1
- package/dist/seed/bids-seed-data.js +45 -44
- package/dist/seed/categories-seed-data.js +41 -40
- package/dist/seed/coupons-seed-data.js +41 -40
- package/dist/seed/products-auctions-seed-data.js +54 -53
- package/dist/seed/products-preorders-seed-data.js +25 -24
- package/dist/seed/products-prize-draws-seed-data.js +24 -23
- package/dist/seed/products-standard-seed-data.js +294 -293
- package/dist/seed/reviews-seed-data.js +61 -60
- package/dist/seed/scammers-seed-data.js +3 -2
- package/dist/seed/stores-seed-data.js +9 -8
- package/dist/seed/support-tickets-seed-data.js +13 -12
- package/dist/seed/users-seed-data.js +28 -27
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/ListingToolbar.d.ts +7 -4
- package/dist/ui/components/ListingToolbar.js +4 -3
- package/dist/ui/components/SiteLogo.js +1 -1
- package/dist/utils/sieve-builder.d.ts +41 -0
- package/dist/utils/sieve-builder.js +66 -0
- package/package.json +1 -1
|
@@ -7,14 +7,25 @@ import { Pagination, ListingToolbar } from "../../../ui";
|
|
|
7
7
|
import { ReviewCard } from "./ReviewsList";
|
|
8
8
|
import { ReviewFilters, REVIEW_PUBLIC_SORT_OPTIONS } from "./ReviewFilters";
|
|
9
9
|
import { useReviews } from "../hooks/useReviews";
|
|
10
|
+
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
11
|
+
import { sortBy } from "../../../constants/sort";
|
|
12
|
+
import { REVIEW_FIELDS } from "../../../constants/field-names";
|
|
10
13
|
const PAGE_SIZE = 12;
|
|
11
|
-
const
|
|
14
|
+
const DEFAULT_SORT = sortBy(REVIEW_FIELDS.CREATED_AT);
|
|
15
|
+
const FILTER_KEYS = ["rating", TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO, "minVotes", "maxVotes"];
|
|
12
16
|
export function ReviewsIndexListing({ initialData, variant = "public", }) {
|
|
13
|
-
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort:
|
|
14
|
-
const [searchInput, setSearchInput] = useState(table.get(
|
|
17
|
+
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
18
|
+
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
15
19
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
16
|
-
const
|
|
17
|
-
const
|
|
20
|
+
const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
|
|
21
|
+
const handleViewToggle = (next) => {
|
|
22
|
+
if (next === VIEW_MODE.TABLE)
|
|
23
|
+
return;
|
|
24
|
+
setView(next);
|
|
25
|
+
table.set(TABLE_KEYS.VIEW, next);
|
|
26
|
+
};
|
|
27
|
+
const sort = table.get(TABLE_KEYS.SORT) || DEFAULT_SORT;
|
|
28
|
+
const currentPage = table.getNumber(TABLE_KEYS.PAGE, 1);
|
|
18
29
|
// Pending filter state — buffered until "Apply Filters" clicked
|
|
19
30
|
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
20
31
|
const pendingTable = useMemo(() => ({
|
|
@@ -57,24 +68,24 @@ export function ReviewsIndexListing({ initialData, variant = "public", }) {
|
|
|
57
68
|
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
58
69
|
}, []);
|
|
59
70
|
const resetAll = useCallback(() => {
|
|
60
|
-
const updates = {
|
|
71
|
+
const updates = { [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" };
|
|
61
72
|
for (const k of FILTER_KEYS)
|
|
62
73
|
updates[k] = "";
|
|
63
74
|
table.setMany(updates);
|
|
64
75
|
setSearchInput("");
|
|
65
76
|
}, [table]);
|
|
66
77
|
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
67
|
-
const hasActiveState = !!table.get(
|
|
68
|
-
table.get(
|
|
78
|
+
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
79
|
+
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
69
80
|
activeFilterCount > 0;
|
|
70
81
|
const commitSearch = useCallback(() => {
|
|
71
|
-
table.set(
|
|
82
|
+
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
72
83
|
}, [searchInput, table]);
|
|
73
84
|
const { reviews, total, totalPages, isLoading } = useReviews({
|
|
74
|
-
q: table.get(
|
|
75
|
-
rating: table.get(
|
|
76
|
-
dateFrom: table.get(
|
|
77
|
-
dateTo: table.get(
|
|
85
|
+
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
86
|
+
rating: table.get(TABLE_KEYS.RATING) || undefined,
|
|
87
|
+
dateFrom: table.get(TABLE_KEYS.DATE_FROM) || undefined,
|
|
88
|
+
dateTo: table.get(TABLE_KEYS.DATE_TO) || undefined,
|
|
78
89
|
minVotes: table.get("minVotes") ? Number(table.get("minVotes")) : undefined,
|
|
79
90
|
maxVotes: table.get("maxVotes") ? Number(table.get("maxVotes")) : undefined,
|
|
80
91
|
sort,
|
|
@@ -85,5 +96,5 @@ export function ReviewsIndexListing({ initialData, variant = "public", }) {
|
|
|
85
96
|
value: opt.value,
|
|
86
97
|
label: opt.key,
|
|
87
98
|
}));
|
|
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(
|
|
99
|
+
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(TABLE_KEYS.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
100
|
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { Review } from "../types";
|
|
2
|
+
export type ReviewCardContext = "listing" | "store" | "general";
|
|
2
3
|
export interface ReviewCardProps {
|
|
3
4
|
review: Review;
|
|
5
|
+
context?: ReviewCardContext;
|
|
4
6
|
className?: string;
|
|
5
7
|
}
|
|
6
|
-
export declare function ReviewCard({ review, className }: ReviewCardProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare function ReviewCard({ review, context, className }: ReviewCardProps): import("react/jsx-runtime").JSX.Element;
|
|
7
9
|
export interface ReviewsListProps {
|
|
8
10
|
reviews: Review[];
|
|
11
|
+
context?: ReviewCardContext;
|
|
9
12
|
isLoading?: boolean;
|
|
10
13
|
totalPages?: number;
|
|
11
14
|
currentPage?: number;
|
|
12
15
|
onPageChange?: (page: number) => void;
|
|
13
16
|
emptyLabel?: string;
|
|
14
17
|
}
|
|
15
|
-
export declare function ReviewsList({ reviews, isLoading, totalPages, currentPage, onPageChange, emptyLabel, }: ReviewsListProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export declare function ReviewsList({ reviews, context, isLoading, totalPages, currentPage, onPageChange, emptyLabel, }: ReviewsListProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -7,7 +7,7 @@ import { maskName } from "../../../security";
|
|
|
7
7
|
import { getDefaultLocale } from "../../../core/baseline-resolver";
|
|
8
8
|
import { normalizeRichTextHtml } from "../../../utils/string.formatter";
|
|
9
9
|
import { ROUTES } from "../../../next";
|
|
10
|
-
export function ReviewCard({ review, className = "" }) {
|
|
10
|
+
export function ReviewCard({ review, context = "general", className = "" }) {
|
|
11
11
|
const date = review.createdAt
|
|
12
12
|
? new Date(review.createdAt).toLocaleDateString(getDefaultLocale(), {
|
|
13
13
|
year: "numeric",
|
|
@@ -21,15 +21,21 @@ export function ReviewCard({ review, className = "" }) {
|
|
|
21
21
|
const productHref = review.productId
|
|
22
22
|
? String(ROUTES.PUBLIC.PRODUCT_DETAIL(review.productId))
|
|
23
23
|
: null;
|
|
24
|
-
const
|
|
25
|
-
|
|
24
|
+
const profileHref = !review.isAnonymous && review.userId
|
|
25
|
+
? String(ROUTES.PUBLIC.PROFILE(review.userId))
|
|
26
|
+
: null;
|
|
27
|
+
const showStoreLink = context !== "store" && !!(review.storeSlug && review.storeName);
|
|
28
|
+
const showProductLink = context !== "listing" && !!productHref;
|
|
29
|
+
const showProfileLink = !!profileHref;
|
|
30
|
+
const hasFooter = showStoreLink || showProductLink || showProfileLink;
|
|
31
|
+
return (_jsxs(Div, { className: `group flex flex-col h-full rounded-xl border border-neutral-200 bg-white p-5 dark:border-zinc-700 dark:bg-zinc-900 transition-shadow hover:shadow-md ${className}`, children: [_jsxs(Link, { href: reviewHref, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Div, { className: "flex items-start gap-3", children: [review.userAvatar ? (_jsx(Div, { role: "img", "aria-label": displayName, className: "h-9 w-9 flex-shrink-0 rounded-full bg-center bg-cover", style: { backgroundImage: `url(${review.userAvatar})` } })) : (_jsx(Div, { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-neutral-200 text-sm font-medium text-neutral-600 dark:bg-zinc-700 dark:text-zinc-300", children: initials })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Span, { className: "font-medium text-neutral-900 dark:text-white", children: displayName }), review.verified && (_jsx(Span, { className: "rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-medium text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400", children: "Verified" })), date && (_jsx(Span, { className: "text-xs text-neutral-400 dark:text-zinc-500", children: date }))] }), _jsx(Div, { className: "mt-1", children: _jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }) })] })] }), review.title && (_jsx(Heading, { level: 4, className: "mt-3 font-semibold text-neutral-900 dark:text-white", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "mt-2 text-sm leading-relaxed text-neutral-600 dark:text-zinc-400" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mt-3", children: review.images.map((img, i) => (_jsx(Div, { role: "img", "aria-label": `Review image ${i + 1}`, className: "h-16 w-16 rounded-lg bg-center bg-cover border border-neutral-100 dark:border-zinc-700", style: { backgroundImage: `url(${img.thumbnailUrl ?? img.url})` } }, i))) })), (review.helpfulCount ?? 0) > 0 && (_jsxs(Text, { className: "mt-3 text-xs text-neutral-400 dark:text-zinc-500", children: [review.helpfulCount, " found this helpful"] }))] }), hasFooter && (_jsxs(Div, { className: "mt-3 pt-3 border-t border-neutral-100 dark:border-zinc-800 flex flex-col gap-1.5", children: [showStoreLink && (_jsxs(Link, { href: String(ROUTES.PUBLIC.STORE_DETAIL(review.storeSlug)), className: "flex items-center gap-1.5 text-xs font-medium text-primary hover:underline", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83C\uDFEA" }), _jsx("span", { className: THEME_CONSTANTS.utilities.textClamp1, children: review.storeName })] })), showProductLink && (_jsxs(Link, { href: productHref, className: "flex items-center gap-1.5 text-xs font-medium text-neutral-500 dark:text-zinc-400 hover:text-neutral-700 dark:hover:text-zinc-200", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83D\uDCE6" }), _jsx("span", { className: THEME_CONSTANTS.utilities.textClamp1, children: review.productTitle ?? "View Product" }), _jsx("span", { "aria-hidden": "true", className: "ml-auto text-primary group-hover:translate-x-0.5 transition-transform", children: "\u2192" })] })), showProfileLink && (_jsxs(Link, { href: profileHref, className: "flex items-center gap-1.5 text-xs font-medium text-neutral-500 dark:text-zinc-400 hover:text-neutral-700 dark:hover:text-zinc-200", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83D\uDC64" }), _jsx("span", { className: THEME_CONSTANTS.utilities.textClamp1, children: displayName })] }))] }))] }));
|
|
26
32
|
}
|
|
27
|
-
export function ReviewsList({ reviews, isLoading, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No reviews yet", }) {
|
|
33
|
+
export function ReviewsList({ reviews, context = "general", isLoading, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No reviews yet", }) {
|
|
28
34
|
if (isLoading) {
|
|
29
35
|
return (_jsx(Stack, { gap: "md", children: Array.from({ length: 4 }).map((_, i) => (_jsxs(Div, { className: "rounded-xl border border-neutral-200 dark:border-zinc-700 p-5", children: [_jsxs(Div, { className: "flex gap-3", children: [_jsx(Skeleton, { className: "h-9 w-9 rounded-full" }), _jsxs(Stack, { gap: "sm", className: "flex-1", children: [_jsx(Skeleton, { className: "h-4 w-24 rounded" }), _jsx(Skeleton, { className: "h-3 w-16 rounded" })] })] }), _jsxs(Stack, { gap: "xs", className: "mt-3", children: [_jsx(Skeleton, { className: "h-3 w-full rounded" }), _jsx(Skeleton, { className: "h-3 w-3/4 rounded" })] })] }, i))) }));
|
|
30
36
|
}
|
|
31
37
|
if (reviews.length === 0) {
|
|
32
38
|
return (_jsx(Text, { className: "py-12 text-center text-sm text-neutral-500 dark:text-zinc-500", children: emptyLabel }));
|
|
33
39
|
}
|
|
34
|
-
return (_jsxs(Stack, { gap: "lg", children: [_jsx(Stack, { gap: "md", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) }), totalPages > 1 && onPageChange && (_jsx(Div, { className: "flex justify-center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: onPageChange }) }))] }));
|
|
40
|
+
return (_jsxs(Stack, { gap: "lg", children: [_jsx(Stack, { gap: "md", children: reviews.map((review) => (_jsx(ReviewCard, { review: review, context: context }, review.id))) }), totalPages > 1 && onPageChange && (_jsx(Div, { className: "flex justify-center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: onPageChange }) }))] }));
|
|
35
41
|
}
|
|
@@ -109,18 +109,18 @@ export declare const reviewSchema: z.ZodObject<{
|
|
|
109
109
|
duration?: number | undefined;
|
|
110
110
|
} | undefined;
|
|
111
111
|
productTitle?: string | undefined;
|
|
112
|
+
featured?: boolean | undefined;
|
|
113
|
+
storeName?: string | undefined;
|
|
112
114
|
images?: {
|
|
113
115
|
url: string;
|
|
114
116
|
thumbnailUrl?: string | undefined;
|
|
115
117
|
}[] | undefined;
|
|
116
|
-
storeName?: string | undefined;
|
|
117
|
-
featured?: boolean | undefined;
|
|
118
118
|
storeSlug?: string | undefined;
|
|
119
|
-
verified?: boolean | undefined;
|
|
120
119
|
userAvatar?: string | undefined;
|
|
121
120
|
comment?: string | undefined;
|
|
122
121
|
helpfulCount?: number | undefined;
|
|
123
122
|
reportCount?: number | undefined;
|
|
123
|
+
verified?: boolean | undefined;
|
|
124
124
|
}, {
|
|
125
125
|
id: string;
|
|
126
126
|
status: "pending" | "approved" | "rejected";
|
|
@@ -139,18 +139,18 @@ export declare const reviewSchema: z.ZodObject<{
|
|
|
139
139
|
duration?: number | undefined;
|
|
140
140
|
} | undefined;
|
|
141
141
|
productTitle?: string | undefined;
|
|
142
|
+
featured?: boolean | undefined;
|
|
143
|
+
storeName?: string | undefined;
|
|
142
144
|
images?: {
|
|
143
145
|
url: string;
|
|
144
146
|
thumbnailUrl?: string | undefined;
|
|
145
147
|
}[] | undefined;
|
|
146
|
-
storeName?: string | undefined;
|
|
147
|
-
featured?: boolean | undefined;
|
|
148
148
|
storeSlug?: string | undefined;
|
|
149
|
-
verified?: boolean | undefined;
|
|
150
149
|
userAvatar?: string | undefined;
|
|
151
150
|
comment?: string | undefined;
|
|
152
151
|
helpfulCount?: number | undefined;
|
|
153
152
|
reportCount?: number | undefined;
|
|
153
|
+
verified?: boolean | undefined;
|
|
154
154
|
}>;
|
|
155
155
|
/** Base schema for list-query parameters. */
|
|
156
156
|
export declare const reviewListParamsSchema: z.ZodObject<{
|
|
@@ -170,9 +170,9 @@ export declare const reviewListParamsSchema: z.ZodObject<{
|
|
|
170
170
|
status?: "pending" | "approved" | "rejected" | undefined;
|
|
171
171
|
page?: number | undefined;
|
|
172
172
|
productId?: string | undefined;
|
|
173
|
+
featured?: boolean | undefined;
|
|
173
174
|
rating?: number | undefined;
|
|
174
175
|
userId?: string | undefined;
|
|
175
|
-
featured?: boolean | undefined;
|
|
176
176
|
}, {
|
|
177
177
|
sort?: string | undefined;
|
|
178
178
|
perPage?: number | undefined;
|
|
@@ -180,7 +180,7 @@ export declare const reviewListParamsSchema: z.ZodObject<{
|
|
|
180
180
|
status?: "pending" | "approved" | "rejected" | undefined;
|
|
181
181
|
page?: number | undefined;
|
|
182
182
|
productId?: string | undefined;
|
|
183
|
+
featured?: boolean | undefined;
|
|
183
184
|
rating?: number | undefined;
|
|
184
185
|
userId?: string | undefined;
|
|
185
|
-
featured?: boolean | undefined;
|
|
186
186
|
}>;
|
|
@@ -27,8 +27,8 @@ export declare const searchProductItemSchema: z.ZodObject<{
|
|
|
27
27
|
currency?: string | undefined;
|
|
28
28
|
status?: string | undefined;
|
|
29
29
|
price?: number | undefined;
|
|
30
|
-
listingType?: "auction" | "standard" | "pre-order" | "prize-draw" | "bundle" | undefined;
|
|
31
30
|
featured?: boolean | undefined;
|
|
31
|
+
listingType?: "auction" | "standard" | "pre-order" | "prize-draw" | "bundle" | undefined;
|
|
32
32
|
isPromoted?: boolean | undefined;
|
|
33
33
|
mainImage?: string | undefined;
|
|
34
34
|
}, {
|
|
@@ -38,8 +38,8 @@ export declare const searchProductItemSchema: z.ZodObject<{
|
|
|
38
38
|
currency?: string | undefined;
|
|
39
39
|
status?: string | undefined;
|
|
40
40
|
price?: number | undefined;
|
|
41
|
-
listingType?: "auction" | "standard" | "pre-order" | "prize-draw" | "bundle" | undefined;
|
|
42
41
|
featured?: boolean | undefined;
|
|
42
|
+
listingType?: "auction" | "standard" | "pre-order" | "prize-draw" | "bundle" | undefined;
|
|
43
43
|
isPromoted?: boolean | undefined;
|
|
44
44
|
mainImage?: string | undefined;
|
|
45
45
|
}>;
|
|
@@ -3,6 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
+
import { AdminViewCards } from "../../admin/components/AdminViewCards";
|
|
6
8
|
import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
|
|
7
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
10
|
import { SELLER_AUCTION_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
@@ -20,6 +22,7 @@ const SORT_OPTIONS = [
|
|
|
20
22
|
const STATUS_OPTIONS = SELLER_AUCTION_STATUS_TABS;
|
|
21
23
|
export function SellerAuctionsView({ renderHeader, children, ...props }) {
|
|
22
24
|
const hasChildren = React.Children.count(children) > 0;
|
|
25
|
+
const [view, setView] = useState("table");
|
|
23
26
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
24
27
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
25
28
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -74,8 +77,9 @@ export function SellerAuctionsView({ renderHeader, children, ...props }) {
|
|
|
74
77
|
});
|
|
75
78
|
const currentPage = table.getNumber("page", 1);
|
|
76
79
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
80
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
77
81
|
if (hasChildren) {
|
|
78
82
|
return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
|
|
79
83
|
}
|
|
80
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions by product name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
84
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions by product name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No auctions found" })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No auctions found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), 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: "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", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _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})` : ""] }) })] })] }))] }));
|
|
81
85
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { useState, useCallback } from "react";
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
7
|
import { Badge, Div, FilterChipGroup, ListingToolbar, Pagination } from "../../../ui";
|
|
7
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { SELLER_BID_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
@@ -34,6 +35,7 @@ const STATUS_BADGE = {
|
|
|
34
35
|
// ---------------------------------------------------------------------------
|
|
35
36
|
export function SellerBidsView({ endpoint = SELLER_ENDPOINTS.BIDS }) {
|
|
36
37
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
38
|
+
const [view, setView] = useState("table");
|
|
37
39
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
38
40
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
39
41
|
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
@@ -119,5 +121,6 @@ export function SellerBidsView({ endpoint = SELLER_ENDPOINTS.BIDS }) {
|
|
|
119
121
|
render: (row) => _jsx("span", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: row.bidDate }),
|
|
120
122
|
},
|
|
121
123
|
];
|
|
122
|
-
|
|
124
|
+
const selection = useBulkSelection({ items: rows ?? [], keyExtractor: (r) => r.id });
|
|
125
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by bidder name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No bids found for your 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: [_jsx("span", { className: "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 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Bid Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })), allId: "" }) }), _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-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
123
126
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { X, Plus } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
7
|
import { Button, Div, ListingToolbar, Pagination, ListingViewShell, Text } from "../../../ui";
|
|
7
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { toRecordArray, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
|
|
@@ -30,6 +31,7 @@ function getIsActive(item) {
|
|
|
30
31
|
// ---------------------------------------------------------------------------
|
|
31
32
|
export function SellerCouponsView({ onCreateClick, onEditClick, onToggle, onDelete, children, ...props }) {
|
|
32
33
|
const hasChildren = React.Children.count(children) > 0;
|
|
34
|
+
const [view, setView] = useState("table");
|
|
33
35
|
const [togglingId, setTogglingId] = useState(null);
|
|
34
36
|
const [deletingId, setDeletingId] = useState(null);
|
|
35
37
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
@@ -104,10 +106,11 @@ export function SellerCouponsView({ onCreateClick, onEditClick, onToggle, onDele
|
|
|
104
106
|
setDeletingId(null);
|
|
105
107
|
}
|
|
106
108
|
}, [onDelete, refetch]);
|
|
109
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
107
110
|
if (hasChildren) {
|
|
108
111
|
return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
|
|
109
112
|
}
|
|
110
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by coupon code", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
113
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by coupon code", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: onCreateClick
|
|
111
114
|
? (_jsxs(Button, { size: "sm", onClick: onCreateClick, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "Add Coupon" })] }))
|
|
112
115
|
: undefined }), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), isLoading ? (_jsx(Div, { className: "fluid-grid-card gap-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Div, { className: "rounded-xl border-2 border-zinc-100 dark:border-slate-700 p-4 animate-pulse space-y-3", children: [_jsx(Div, { className: "h-6 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx(Div, { className: "h-4 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))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No coupons found \u2014 create your first coupon" }) })) : (_jsx(Div, { className: "fluid-grid-card gap-3", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: onEditClick ? handleEdit : undefined, onToggleActive: onToggle ? handleToggle : undefined, onDelete: onDelete ? handleDelete : undefined, className: togglingId === row.id || deletingId === row.id ? "pointer-events-none opacity-60" : undefined }, row.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: "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 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
|
|
113
116
|
? "bg-[var(--appkit-color-primary)] text-white border-[var(--appkit-color-primary)]"
|
|
@@ -3,6 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
+
import { AdminViewCards } from "../../admin/components/AdminViewCards";
|
|
6
8
|
import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
|
|
7
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
10
|
import { SELLER_OFFER_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
@@ -18,6 +20,7 @@ const SORT_OPTIONS = [
|
|
|
18
20
|
const STATUS_OPTIONS = SELLER_OFFER_STATUS_TABS;
|
|
19
21
|
export function SellerOffersView({ children, ...props }) {
|
|
20
22
|
const hasChildren = React.Children.count(children) > 0;
|
|
23
|
+
const [view, setView] = useState("table");
|
|
21
24
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
22
25
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
23
26
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -72,8 +75,9 @@ export function SellerOffersView({ children, ...props }) {
|
|
|
72
75
|
});
|
|
73
76
|
const currentPage = table.getNumber("page", 1);
|
|
74
77
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
78
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
75
79
|
if (hasChildren) {
|
|
76
80
|
return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
|
|
77
81
|
}
|
|
78
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search offers by product or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
82
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search offers by product or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No offers received" })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No offers received", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), 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: "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", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _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})` : ""] }) })] })] }))] }));
|
|
79
83
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { X, Eye } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
7
|
import { Badge, Button, Div, FilterChipGroup, Heading, Input, ListingToolbar, Pagination, ListingViewShell, Select, SideDrawer, Stack, Text } from "../../../ui";
|
|
7
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { SELLER_ORDER_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
@@ -108,6 +109,7 @@ function OrderDetailDrawer({ orderId, apiBase, onClose, }) {
|
|
|
108
109
|
// ---------------------------------------------------------------------------
|
|
109
110
|
export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS, children, ...props }) {
|
|
110
111
|
const hasChildren = React.Children.count(children) > 0;
|
|
112
|
+
const [view, setView] = useState("table");
|
|
111
113
|
const [selectedOrderId, setSelectedOrderId] = useState(null);
|
|
112
114
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
113
115
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
@@ -190,8 +192,9 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
190
192
|
},
|
|
191
193
|
];
|
|
192
194
|
const renderRowActions = useCallback((row) => (_jsx("button", { type: "button", onClick: () => setSelectedOrderId(row.id), title: "View order details", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 hover:text-zinc-700 dark:hover:text-zinc-300 transition-colors", children: _jsx(Eye, { className: "h-4 w-4" }) })), []);
|
|
195
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
193
196
|
if (hasChildren) {
|
|
194
197
|
return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
|
|
195
198
|
}
|
|
196
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by order ID or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
199
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by order ID or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No orders yet", renderRowActions: renderRowActions })] }), 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: "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 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _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-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), selectedOrderId && (_jsx(OrderDetailDrawer, { orderId: selectedOrderId, apiBase: orderDetailApiBase, onClose: () => setSelectedOrderId(null) }))] }));
|
|
197
200
|
}
|
|
@@ -3,6 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
+
import { AdminViewCards } from "../../admin/components/AdminViewCards";
|
|
6
8
|
import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
|
|
7
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
10
|
import { ADMIN_PAYOUT_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
@@ -18,6 +20,7 @@ const SORT_OPTIONS = [
|
|
|
18
20
|
const STATUS_OPTIONS = ADMIN_PAYOUT_STATUS_TABS;
|
|
19
21
|
export function SellerPayoutsView({ children, ...props }) {
|
|
20
22
|
const hasChildren = React.Children.count(children) > 0;
|
|
23
|
+
const [view, setView] = useState("table");
|
|
21
24
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
22
25
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
23
26
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -72,8 +75,9 @@ export function SellerPayoutsView({ children, ...props }) {
|
|
|
72
75
|
});
|
|
73
76
|
const currentPage = table.getNumber("page", 1);
|
|
74
77
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
78
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
75
79
|
if (hasChildren) {
|
|
76
80
|
return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
|
|
77
81
|
}
|
|
78
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search payouts by payout # or amount", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
82
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search payouts by payout # or amount", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No payouts found" })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No payouts found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), 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: "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", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _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})` : ""] }) })] })] }))] }));
|
|
79
83
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { Pencil, X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
7
|
import { Alert, Badge, Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
|
|
7
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { SELLER_PRE_ORDER_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
@@ -70,6 +71,7 @@ const PRE_ORDER_COLUMNS = [
|
|
|
70
71
|
];
|
|
71
72
|
export function SellerPreOrdersView({ children, ...props }) {
|
|
72
73
|
const hasChildren = React.Children.count(children) > 0;
|
|
74
|
+
const [view, setView] = useState("table");
|
|
73
75
|
const dispatch = useActionDispatch();
|
|
74
76
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
75
77
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
@@ -131,10 +133,11 @@ export function SellerPreOrdersView({ children, ...props }) {
|
|
|
131
133
|
});
|
|
132
134
|
const currentPage = table.getNumber("page", 1);
|
|
133
135
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
136
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
134
137
|
if (hasChildren) {
|
|
135
138
|
return (_jsx(ListingViewShell, { portal: "seller", ...props, children: children }));
|
|
136
139
|
}
|
|
137
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders by name\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
140
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders by name\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky z-10 flex justify-center bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-1.5", style: { top: "calc(var(--header-height, 0px) + 44px)" }, children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), _jsx(DataTable, { columns: PRE_ORDER_COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: "No pre-orders listed yet", getRowHref: (row) => String(ROUTES.STORE.PRE_ORDERS_EDIT(row.id)), renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => {
|
|
138
141
|
e.stopPropagation();
|
|
139
142
|
void dispatch({ type: "NAVIGATE", href: String(ROUTES.STORE.PRE_ORDERS_EDIT(row.id)) });
|
|
140
143
|
}, "aria-label": "Edit", children: _jsx(Pencil, { className: "w-4 h-4" }) })) })] }), 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-[var(--appkit-color-surface)] shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-error)] transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-[var(--appkit-color-border)] px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white hover:opacity-90 transition-opacity active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { Pencil, X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
7
|
import { Alert, Badge, Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
|
|
7
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { SELLER_PRIZE_DRAW_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
@@ -67,6 +68,7 @@ const PRIZE_DRAW_COLUMNS = [
|
|
|
67
68
|
];
|
|
68
69
|
export function SellerPrizeDrawsView({ children, ...props }) {
|
|
69
70
|
const hasChildren = React.Children.count(children) > 0;
|
|
71
|
+
const [view, setView] = useState("table");
|
|
70
72
|
const dispatch = useActionDispatch();
|
|
71
73
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
72
74
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
@@ -128,10 +130,11 @@ export function SellerPrizeDrawsView({ children, ...props }) {
|
|
|
128
130
|
});
|
|
129
131
|
const currentPage = table.getNumber("page", 1);
|
|
130
132
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
133
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
131
134
|
if (hasChildren) {
|
|
132
135
|
return (_jsx(ListingViewShell, { portal: "seller", ...props, children: children }));
|
|
133
136
|
}
|
|
134
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search prize draws by name\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
137
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search prize draws by name\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky z-10 flex justify-center bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-1.5", style: { top: "calc(var(--header-height, 0px) + 44px)" }, children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), _jsx(DataTable, { columns: PRIZE_DRAW_COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: "No prize draws listed yet", getRowHref: (row) => String(ROUTES.STORE.PRIZE_DRAWS_EDIT(row.id)), renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => {
|
|
135
138
|
e.stopPropagation();
|
|
136
139
|
void dispatch({ type: "NAVIGATE", href: String(ROUTES.STORE.PRIZE_DRAWS_EDIT(row.id)) });
|
|
137
140
|
}, "aria-label": "Edit", children: _jsx(Pencil, { className: "w-4 h-4" }) })) })] }), 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-[var(--appkit-color-surface)] shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-error)] transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-[var(--appkit-color-border)] px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white hover:opacity-90 transition-opacity active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
|