@mohasinac/appkit 2.6.4 → 2.6.5
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/features/events/schema.d.ts +4 -4
- package/dist/client.d.ts +4 -4
- package/dist/client.js +2 -2
- package/dist/features/admin/components/AdminPrizeDrawsView.d.ts +4 -0
- package/dist/features/admin/components/AdminPrizeDrawsView.js +135 -0
- package/dist/features/admin/components/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +2 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +34 -0
- package/dist/features/admin/constants/filter-tabs.js +16 -0
- package/dist/features/seller/components/SellerPreOrdersView.d.ts +4 -0
- package/dist/features/seller/components/SellerPreOrdersView.js +141 -0
- package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +4 -0
- package/dist/features/seller/components/SellerPrizeDrawsView.js +138 -0
- package/dist/features/seller/components/index.d.ts +4 -0
- package/dist/features/seller/components/index.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +9 -0
- package/package.json +1 -1
|
@@ -19,8 +19,8 @@ export declare const eventInputSchema: z.ZodObject<{
|
|
|
19
19
|
tags: string[];
|
|
20
20
|
startsAt: string;
|
|
21
21
|
endsAt: string;
|
|
22
|
-
isOnline: boolean;
|
|
23
22
|
entryFee: number;
|
|
23
|
+
isOnline: boolean;
|
|
24
24
|
location?: string | undefined;
|
|
25
25
|
maxEntries?: number | undefined;
|
|
26
26
|
imageUrl?: string | undefined;
|
|
@@ -36,8 +36,8 @@ export declare const eventInputSchema: z.ZodObject<{
|
|
|
36
36
|
imageUrl?: string | undefined;
|
|
37
37
|
description?: string | undefined;
|
|
38
38
|
tags?: string[] | undefined;
|
|
39
|
-
isOnline?: boolean | undefined;
|
|
40
39
|
entryFee?: number | undefined;
|
|
40
|
+
isOnline?: boolean | undefined;
|
|
41
41
|
}>;
|
|
42
42
|
export declare const eventUpdateSchema: z.ZodObject<Omit<{
|
|
43
43
|
title: z.ZodOptional<z.ZodString>;
|
|
@@ -62,8 +62,8 @@ export declare const eventUpdateSchema: z.ZodObject<Omit<{
|
|
|
62
62
|
tags?: string[] | undefined;
|
|
63
63
|
startsAt?: string | undefined;
|
|
64
64
|
endsAt?: string | undefined;
|
|
65
|
-
isOnline?: boolean | undefined;
|
|
66
65
|
entryFee?: number | undefined;
|
|
66
|
+
isOnline?: boolean | undefined;
|
|
67
67
|
}, {
|
|
68
68
|
type?: "TOURNAMENT" | "CONVENTION" | "MEETUP" | "SALE" | undefined;
|
|
69
69
|
title?: string | undefined;
|
|
@@ -74,8 +74,8 @@ export declare const eventUpdateSchema: z.ZodObject<Omit<{
|
|
|
74
74
|
tags?: string[] | undefined;
|
|
75
75
|
startsAt?: string | undefined;
|
|
76
76
|
endsAt?: string | undefined;
|
|
77
|
-
isOnline?: boolean | undefined;
|
|
78
77
|
entryFee?: number | undefined;
|
|
78
|
+
isOnline?: boolean | undefined;
|
|
79
79
|
}>;
|
|
80
80
|
export declare const registerForEventSchema: z.ZodObject<{
|
|
81
81
|
eventId: z.ZodString;
|
package/dist/client.d.ts
CHANGED
|
@@ -66,8 +66,8 @@ export { ZodSetup } from "./validation/ZodSetup";
|
|
|
66
66
|
export type { ZodSetupProps } from "./validation/ZodSetup";
|
|
67
67
|
export { AdminSidebar } from "./features/admin/components/AdminSidebar";
|
|
68
68
|
export type { AdminSidebarProps, AdminNavItem, AdminNavGroup } from "./features/admin/components/AdminSidebar";
|
|
69
|
-
export { AdminDashboardView, AdminAnalyticsView, AdminListingScaffold, useAdminListingData, toRecordArray, toStringValue, toRelativeDate, toRupees } from "./features/admin/index";
|
|
70
|
-
export type { AdminDashboardViewProps, AdminAnalyticsViewProps, AdminAnalyticsViewLabels } from "./features/admin/index";
|
|
69
|
+
export { AdminDashboardView, AdminAnalyticsView, AdminListingScaffold, AdminPrizeDrawsView, useAdminListingData, toRecordArray, toStringValue, toRelativeDate, toRupees } from "./features/admin/index";
|
|
70
|
+
export type { AdminDashboardViewProps, AdminAnalyticsViewProps, AdminAnalyticsViewLabels, AdminPrizeDrawsViewProps } from "./features/admin/index";
|
|
71
71
|
export { ADMIN_ENDPOINTS } from "./constants/index";
|
|
72
72
|
export { UserSidebar } from "./features/account/components/UserSidebar";
|
|
73
73
|
export type { UserSidebarProps, UserNavItem, UserNavGroup } from "./features/account/components/UserSidebar";
|
|
@@ -148,8 +148,8 @@ export { UserOffersPanel } from "./features/account/components/UserOffersPanel";
|
|
|
148
148
|
export type { UserOffersPanelProps } from "./features/account/components/UserOffersPanel";
|
|
149
149
|
export { SellerDashboardView as StoreDashboardView, SellerDashboardView, useSellerDashboard as useStoreDashboard, useSellerDashboard } from "./features/seller/index";
|
|
150
150
|
export type { SellerDashboardViewProps as StoreDashboardViewProps, SellerDashboardViewProps } from "./features/seller/index";
|
|
151
|
-
export { SellerPayoutSettingsView, SellerShippingView, SellerReviewsView, SellerPayoutRequestView, SellerAnalyticsStats, SellerTopProducts, SellerAnalyticsView, SellerPayoutsView, SellerCouponEditorView, SellerBidsView, SellerAddressesView } from "./features/seller/components/index";
|
|
152
|
-
export type { SellerPayoutSettingsViewProps, SellerShippingViewProps, SellerReviewsViewProps, SellerPayoutRequestViewProps, SellerAnalyticsViewProps, SellerPayoutsViewProps, SellerCouponEditorViewProps, CouponEditorDraft, SellerBidsViewProps, SellerAddressesViewProps } from "./features/seller/components/index";
|
|
151
|
+
export { SellerPayoutSettingsView, SellerShippingView, SellerReviewsView, SellerPayoutRequestView, SellerAnalyticsStats, SellerTopProducts, SellerAnalyticsView, SellerPayoutsView, SellerCouponEditorView, SellerBidsView, SellerAddressesView, SellerPreOrdersView, SellerPrizeDrawsView } from "./features/seller/components/index";
|
|
152
|
+
export type { SellerPayoutSettingsViewProps, SellerShippingViewProps, SellerReviewsViewProps, SellerPayoutRequestViewProps, SellerAnalyticsViewProps, SellerPayoutsViewProps, SellerCouponEditorViewProps, CouponEditorDraft, SellerBidsViewProps, SellerAddressesViewProps, SellerPreOrdersViewProps, SellerPrizeDrawsViewProps } from "./features/seller/components/index";
|
|
153
153
|
export type { SellerAnalyticsSummary, SellerAnalyticsTopProduct } from "./features/seller/types/index";
|
|
154
154
|
export { UserAccountHubView, UserOrdersView, OrderDetailView, UserNotificationsView, UserReturnsView } from "./features/account/index";
|
|
155
155
|
export type { UserAccountHubViewProps, UserAccountHubViewLabels, UserOrdersViewProps, UserOrdersViewLabels, OrderDetailViewProps, OrderDetailViewLabels, UserNotificationsViewProps, UserNotificationsViewLabels, UserReturnsViewProps, UserReturnsViewLabels } from "./features/account/index";
|
package/dist/client.js
CHANGED
|
@@ -126,7 +126,7 @@ export { Search } from "./features/search/components";
|
|
|
126
126
|
export { ToastProvider, SkipToMain, NavigationLoader } from "./ui/index";
|
|
127
127
|
export { ZodSetup } from "./validation/ZodSetup";
|
|
128
128
|
export { AdminSidebar } from "./features/admin/components/AdminSidebar";
|
|
129
|
-
export { AdminDashboardView, AdminAnalyticsView, AdminListingScaffold, useAdminListingData, toRecordArray, toStringValue, toRelativeDate, toRupees } from "./features/admin/index";
|
|
129
|
+
export { AdminDashboardView, AdminAnalyticsView, AdminListingScaffold, AdminPrizeDrawsView, useAdminListingData, toRecordArray, toStringValue, toRelativeDate, toRupees } from "./features/admin/index";
|
|
130
130
|
export { ADMIN_ENDPOINTS } from "./constants/index";
|
|
131
131
|
export { UserSidebar } from "./features/account/components/UserSidebar";
|
|
132
132
|
export { CouponsIndexListing } from "./features/promotions/components/CouponsIndexListing";
|
|
@@ -180,7 +180,7 @@ export { FeatureBadge, FeatureBadgeList } from "./features/products/components/F
|
|
|
180
180
|
export { SellerOffersPanel } from "./features/seller/components/SellerOffersPanel";
|
|
181
181
|
export { UserOffersPanel } from "./features/account/components/UserOffersPanel";
|
|
182
182
|
export { SellerDashboardView as StoreDashboardView, SellerDashboardView, useSellerDashboard as useStoreDashboard, useSellerDashboard } from "./features/seller/index";
|
|
183
|
-
export { SellerPayoutSettingsView, SellerShippingView, SellerReviewsView, SellerPayoutRequestView, SellerAnalyticsStats, SellerTopProducts, SellerAnalyticsView, SellerPayoutsView, SellerCouponEditorView, SellerBidsView, SellerAddressesView } from "./features/seller/components/index";
|
|
183
|
+
export { SellerPayoutSettingsView, SellerShippingView, SellerReviewsView, SellerPayoutRequestView, SellerAnalyticsStats, SellerTopProducts, SellerAnalyticsView, SellerPayoutsView, SellerCouponEditorView, SellerBidsView, SellerAddressesView, SellerPreOrdersView, SellerPrizeDrawsView } from "./features/seller/components/index";
|
|
184
184
|
export { UserAccountHubView, UserOrdersView, OrderDetailView, UserNotificationsView, UserReturnsView } from "./features/account/index";
|
|
185
185
|
export { useOrders, useOrder, OrdersList } from "./features/orders/index";
|
|
186
186
|
export { useCouponValidate } from "./features/promotions/hooks/useCouponValidate";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ListingViewShellProps } from "../../../ui";
|
|
2
|
+
export interface AdminPrizeDrawsViewProps extends ListingViewShellProps {
|
|
3
|
+
}
|
|
4
|
+
export declare function AdminPrizeDrawsView({ children, ...props }: AdminPrizeDrawsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import React, { useState, useCallback } from "react";
|
|
4
|
+
import { Pencil, X } from "lucide-react";
|
|
5
|
+
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { Alert, Badge, Button, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, } from "../../../ui";
|
|
7
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
|
+
import { ADMIN_PRODUCT_STATUS_TABS } from "../constants/filter-tabs";
|
|
9
|
+
import { ROUTES } from "../../../constants";
|
|
10
|
+
import { toRecordArray, toRelativeDate, toRupees, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
11
|
+
import { DataTable } from "./DataTable";
|
|
12
|
+
const PAGE_SIZE = 25;
|
|
13
|
+
const FILTER_KEYS = ["status"];
|
|
14
|
+
const DEFAULT_SORT = "-createdAt";
|
|
15
|
+
// Hardcoded sieve clause — this view only ever shows prize draws.
|
|
16
|
+
const LISTING_TYPE_FILTER = "listingType==prize-draw";
|
|
17
|
+
const SORT_OPTIONS = [
|
|
18
|
+
{ value: "-createdAt", label: "Newest" },
|
|
19
|
+
{ value: "createdAt", label: "Oldest" },
|
|
20
|
+
{ value: "title", label: "Title A–Z" },
|
|
21
|
+
{ value: "prizeDrawEndDate", label: "Draw Date Soon" },
|
|
22
|
+
];
|
|
23
|
+
const STATUS_OPTIONS = ADMIN_PRODUCT_STATUS_TABS;
|
|
24
|
+
const STATUS_VARIANT = {
|
|
25
|
+
published: "success",
|
|
26
|
+
active: "success",
|
|
27
|
+
draft: "default",
|
|
28
|
+
pending: "warning",
|
|
29
|
+
archived: "secondary",
|
|
30
|
+
ended: "secondary",
|
|
31
|
+
cancelled: "danger",
|
|
32
|
+
};
|
|
33
|
+
const PRIZE_DRAW_COLUMNS = [
|
|
34
|
+
{
|
|
35
|
+
key: "primary",
|
|
36
|
+
header: "Prize Draw",
|
|
37
|
+
render: (row) => (_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "font-semibold text-[var(--appkit-color-text)] line-clamp-1", children: row.primary }), _jsx("p", { className: "text-xs text-[var(--appkit-color-text-muted)]", children: row.storeName })] })),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: "entryFee",
|
|
41
|
+
header: "Entry Fee",
|
|
42
|
+
className: "w-28 text-right",
|
|
43
|
+
render: (row) => (_jsx("span", { className: "text-sm font-medium text-[var(--appkit-color-text)]", children: row.entryFee })),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
key: "status",
|
|
47
|
+
header: "Status",
|
|
48
|
+
sortable: true,
|
|
49
|
+
className: "w-28",
|
|
50
|
+
render: (row) => (_jsx(Badge, { variant: STATUS_VARIANT[row.status] ?? "default", children: row.status })),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
key: "drawDate",
|
|
54
|
+
header: "Draw Date",
|
|
55
|
+
className: "w-32",
|
|
56
|
+
render: (row) => (_jsx("span", { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.drawDate })),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
key: "updatedAt",
|
|
60
|
+
header: "Updated",
|
|
61
|
+
sortable: true,
|
|
62
|
+
className: "w-32",
|
|
63
|
+
render: (row) => (_jsx("span", { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.updatedAt })),
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
export function AdminPrizeDrawsView({ children, ...props }) {
|
|
67
|
+
const hasChildren = React.Children.count(children) > 0;
|
|
68
|
+
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
69
|
+
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
70
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
71
|
+
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
72
|
+
const openFilters = useCallback(() => {
|
|
73
|
+
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
74
|
+
setFilterOpen(true);
|
|
75
|
+
}, [table]);
|
|
76
|
+
const applyFilters = useCallback(() => {
|
|
77
|
+
const updates = { page: "1" };
|
|
78
|
+
for (const k of FILTER_KEYS)
|
|
79
|
+
updates[k] = pendingFilters[k] ?? "";
|
|
80
|
+
table.setMany(updates);
|
|
81
|
+
setFilterOpen(false);
|
|
82
|
+
}, [pendingFilters, table]);
|
|
83
|
+
const clearFilters = useCallback(() => {
|
|
84
|
+
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
85
|
+
}, []);
|
|
86
|
+
const resetAll = useCallback(() => {
|
|
87
|
+
const updates = { q: "", sort: "" };
|
|
88
|
+
for (const k of FILTER_KEYS)
|
|
89
|
+
updates[k] = "";
|
|
90
|
+
table.setMany(updates);
|
|
91
|
+
setSearchInput("");
|
|
92
|
+
}, [table]);
|
|
93
|
+
const commitSearch = useCallback(() => {
|
|
94
|
+
table.set("q", searchInput.trim());
|
|
95
|
+
}, [searchInput, table]);
|
|
96
|
+
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
97
|
+
const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
|
|
98
|
+
const statusRaw = table.get("status");
|
|
99
|
+
const statusFilter = statusRaw && statusRaw !== "All" ? `status==${statusRaw}` : undefined;
|
|
100
|
+
const filters = [LISTING_TYPE_FILTER, statusFilter].filter(Boolean).join(",") || undefined;
|
|
101
|
+
const { rows, total, isLoading, errorMessage } = useAdminListingData({
|
|
102
|
+
queryKey: ["admin", "prize-draws", "listing"],
|
|
103
|
+
endpoint: ADMIN_ENDPOINTS.PRODUCTS,
|
|
104
|
+
page: table.getNumber("page", 1),
|
|
105
|
+
pageSize: PAGE_SIZE,
|
|
106
|
+
sorts: table.get("sort") || DEFAULT_SORT,
|
|
107
|
+
filters,
|
|
108
|
+
q: table.get("q") || undefined,
|
|
109
|
+
mapRows: (response) => toRecordArray(response.items).map((item, index) => {
|
|
110
|
+
const priceRaw = typeof item.price === "number" ? item.price : 0;
|
|
111
|
+
return {
|
|
112
|
+
id: toStringValue(item.id, `prize-draw-${index}`),
|
|
113
|
+
primary: toStringValue(item.title ?? item.name, "Untitled prize draw"),
|
|
114
|
+
secondary: toStringValue(item.sellerName, ""),
|
|
115
|
+
storeName: toStringValue(item.sellerName ?? item.storeId, "Unknown store"),
|
|
116
|
+
status: toStringValue(item.status, "draft"),
|
|
117
|
+
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
118
|
+
entryFee: priceRaw ? toRupees(priceRaw) : "Free",
|
|
119
|
+
drawDate: item.prizeDrawEndDate
|
|
120
|
+
? toRelativeDate(item.prizeDrawEndDate)
|
|
121
|
+
: "TBA",
|
|
122
|
+
};
|
|
123
|
+
}),
|
|
124
|
+
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
125
|
+
});
|
|
126
|
+
const currentPage = table.getNumber("page", 1);
|
|
127
|
+
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
128
|
+
if (hasChildren) {
|
|
129
|
+
return (_jsx(ListingViewShell, { portal: "admin", ...props, children: children }));
|
|
130
|
+
}
|
|
131
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search prize draws by name or store\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
|
|
132
|
+
table.set("sort", v);
|
|
133
|
+
table.setPage(1);
|
|
134
|
+
}, hideViewToggle: true, 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 found", getRowHref: (row) => String(ROUTES.ADMIN.PRIZE_DRAWS_EDIT(row.id)), renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsx("a", { href: String(ROUTES.ADMIN.PRIZE_DRAWS_EDIT(row.id)), "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})` : ""] }) })] })] }))] }));
|
|
135
|
+
}
|
|
@@ -30,6 +30,8 @@ export { AdminBrandsView } from "./AdminBrandsView";
|
|
|
30
30
|
export type { AdminBrandsViewProps } from "./AdminBrandsView";
|
|
31
31
|
export { AdminBrandEditorView } from "./AdminBrandEditorView";
|
|
32
32
|
export type { AdminBrandEditorViewProps } from "./AdminBrandEditorView";
|
|
33
|
+
export { AdminPrizeDrawsView } from "./AdminPrizeDrawsView";
|
|
34
|
+
export type { AdminPrizeDrawsViewProps } from "./AdminPrizeDrawsView";
|
|
33
35
|
export { AdminBundlesView } from "./AdminBundlesView";
|
|
34
36
|
export type { AdminBundlesViewProps } from "./AdminBundlesView";
|
|
35
37
|
export { AdminBundleEditorView } from "./AdminBundleEditorView";
|
|
@@ -15,6 +15,8 @@ export { AdminCategoriesView } from "./AdminCategoriesView";
|
|
|
15
15
|
export { AdminCategoryEditorView } from "./AdminCategoryEditorView";
|
|
16
16
|
export { AdminBrandsView } from "./AdminBrandsView";
|
|
17
17
|
export { AdminBrandEditorView } from "./AdminBrandEditorView";
|
|
18
|
+
// SB4-E 2026-05-14 — admin prize draws listing view.
|
|
19
|
+
export { AdminPrizeDrawsView } from "./AdminPrizeDrawsView";
|
|
18
20
|
// S-SBUNI-4 2026-05-13 — admin bundle CRUD views.
|
|
19
21
|
export { AdminBundlesView } from "./AdminBundlesView";
|
|
20
22
|
export { AdminBundleEditorView } from "./AdminBundleEditorView";
|
|
@@ -336,6 +336,40 @@ export declare const SELLER_AUCTION_STATUS_TABS: readonly [{
|
|
|
336
336
|
readonly id: "cancelled";
|
|
337
337
|
readonly label: "Cancelled";
|
|
338
338
|
}];
|
|
339
|
+
/** Seller > Pre-orders — pre-order-state filter chip set. */
|
|
340
|
+
export declare const SELLER_PRE_ORDER_STATUS_TABS: readonly [{
|
|
341
|
+
readonly id: "All";
|
|
342
|
+
readonly label: "All";
|
|
343
|
+
}, {
|
|
344
|
+
readonly id: "active";
|
|
345
|
+
readonly label: "Active";
|
|
346
|
+
}, {
|
|
347
|
+
readonly id: "draft";
|
|
348
|
+
readonly label: "Draft";
|
|
349
|
+
}, {
|
|
350
|
+
readonly id: "archived";
|
|
351
|
+
readonly label: "Archived";
|
|
352
|
+
}, {
|
|
353
|
+
readonly id: "cancelled";
|
|
354
|
+
readonly label: "Cancelled";
|
|
355
|
+
}];
|
|
356
|
+
/** Seller > Prize Draws — draw-state filter chip set. */
|
|
357
|
+
export declare const SELLER_PRIZE_DRAW_STATUS_TABS: readonly [{
|
|
358
|
+
readonly id: "All";
|
|
359
|
+
readonly label: "All";
|
|
360
|
+
}, {
|
|
361
|
+
readonly id: "active";
|
|
362
|
+
readonly label: "Active";
|
|
363
|
+
}, {
|
|
364
|
+
readonly id: "draft";
|
|
365
|
+
readonly label: "Draft";
|
|
366
|
+
}, {
|
|
367
|
+
readonly id: "ended";
|
|
368
|
+
readonly label: "Ended";
|
|
369
|
+
}, {
|
|
370
|
+
readonly id: "cancelled";
|
|
371
|
+
readonly label: "Cancelled";
|
|
372
|
+
}];
|
|
339
373
|
/** Seller > Orders — order-state filter chip set. Subset of
|
|
340
374
|
* `ADMIN_ORDER_STATUS_TABS` — sellers don't see `RETURN_REQUESTED` until
|
|
341
375
|
* the buyer initiates one through support. */
|
|
@@ -168,6 +168,22 @@ export const SELLER_AUCTION_STATUS_TABS = [
|
|
|
168
168
|
{ id: "ended", label: "Ended" },
|
|
169
169
|
{ id: "cancelled", label: "Cancelled" },
|
|
170
170
|
];
|
|
171
|
+
/** Seller > Pre-orders — pre-order-state filter chip set. */
|
|
172
|
+
export const SELLER_PRE_ORDER_STATUS_TABS = [
|
|
173
|
+
ALL_TAB,
|
|
174
|
+
{ id: "active", label: "Active" },
|
|
175
|
+
{ id: "draft", label: "Draft" },
|
|
176
|
+
{ id: "archived", label: "Archived" },
|
|
177
|
+
{ id: "cancelled", label: "Cancelled" },
|
|
178
|
+
];
|
|
179
|
+
/** Seller > Prize Draws — draw-state filter chip set. */
|
|
180
|
+
export const SELLER_PRIZE_DRAW_STATUS_TABS = [
|
|
181
|
+
ALL_TAB,
|
|
182
|
+
{ id: "active", label: "Active" },
|
|
183
|
+
{ id: "draft", label: "Draft" },
|
|
184
|
+
{ id: "ended", label: "Ended" },
|
|
185
|
+
{ id: "cancelled", label: "Cancelled" },
|
|
186
|
+
];
|
|
171
187
|
/** Seller > Orders — order-state filter chip set. Subset of
|
|
172
188
|
* `ADMIN_ORDER_STATUS_TABS` — sellers don't see `RETURN_REQUESTED` until
|
|
173
189
|
* the buyer initiates one through support. */
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ListingViewShellProps } from "../../../ui";
|
|
2
|
+
export interface SellerPreOrdersViewProps extends ListingViewShellProps {
|
|
3
|
+
}
|
|
4
|
+
export declare function SellerPreOrdersView({ children, ...props }: SellerPreOrdersViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import React, { useState, useCallback } from "react";
|
|
4
|
+
import { Pencil, X } from "lucide-react";
|
|
5
|
+
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { Alert, Badge, Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
|
|
7
|
+
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
|
+
import { SELLER_PRE_ORDER_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
9
|
+
import { ROUTES } from "../../../constants";
|
|
10
|
+
import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
|
|
11
|
+
import { DataTable } from "../../admin/components/DataTable";
|
|
12
|
+
import { useActionDispatch } from "../../../react/hooks/use-action-dispatch";
|
|
13
|
+
const PAGE_SIZE = 25;
|
|
14
|
+
const FILTER_KEYS = ["status"];
|
|
15
|
+
const DEFAULT_SORT = "-createdAt";
|
|
16
|
+
// Hardcoded sieve clause — this view only ever shows pre-orders.
|
|
17
|
+
const LISTING_TYPE_FILTER = "listingType==pre-order";
|
|
18
|
+
const SORT_OPTIONS = [
|
|
19
|
+
{ value: "-createdAt", label: "Newest" },
|
|
20
|
+
{ value: "createdAt", label: "Oldest" },
|
|
21
|
+
{ value: "title", label: "Title A–Z" },
|
|
22
|
+
{ value: "preorderAvailableDate", label: "Delivery Soon" },
|
|
23
|
+
];
|
|
24
|
+
const STATUS_OPTIONS = SELLER_PRE_ORDER_STATUS_TABS;
|
|
25
|
+
const PRE_ORDER_COLUMNS = [
|
|
26
|
+
{
|
|
27
|
+
key: "thumbnail",
|
|
28
|
+
header: "",
|
|
29
|
+
className: "w-12",
|
|
30
|
+
render: (row) => row.imageUrl ? (_jsx("img", { src: row.imageUrl, alt: "", className: "w-10 h-10 rounded-lg object-cover border border-[var(--appkit-color-border)]" })) : (_jsx("div", { className: "w-10 h-10 rounded-lg bg-[var(--appkit-color-surface-raised)] border border-[var(--appkit-color-border)] flex items-center justify-center", children: _jsx("span", { className: "text-xs text-[var(--appkit-color-text-faint)]", children: "\u2013" }) })),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
key: "primary",
|
|
34
|
+
header: "Pre-order",
|
|
35
|
+
render: (row) => (_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "font-medium text-[var(--appkit-color-text)] line-clamp-1", children: row.primary }), _jsx("span", { className: "text-xs text-[var(--appkit-color-text-muted)]", children: row.secondary })] })),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: "price",
|
|
39
|
+
header: "Price",
|
|
40
|
+
className: "w-28 text-right",
|
|
41
|
+
render: (row) => (_jsx("span", { className: "text-sm font-medium text-[var(--appkit-color-text)]", children: row.price })),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
key: "status",
|
|
45
|
+
header: "Status",
|
|
46
|
+
className: "w-28",
|
|
47
|
+
render: (row) => {
|
|
48
|
+
const variant = row.status === "active"
|
|
49
|
+
? "success"
|
|
50
|
+
: row.status === "draft"
|
|
51
|
+
? "default"
|
|
52
|
+
: row.status === "cancelled"
|
|
53
|
+
? "danger"
|
|
54
|
+
: "warning";
|
|
55
|
+
return _jsx(Badge, { variant: variant, children: row.status });
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
key: "deliveryDate",
|
|
60
|
+
header: "Est. Delivery",
|
|
61
|
+
className: "w-36",
|
|
62
|
+
render: (row) => (_jsx("span", { className: "text-xs text-[var(--appkit-color-text-muted)]", children: row.deliveryDate })),
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: "updatedAt",
|
|
66
|
+
header: "Updated",
|
|
67
|
+
className: "w-28",
|
|
68
|
+
render: (row) => (_jsx("span", { className: "text-xs text-[var(--appkit-color-text-muted)]", children: row.updatedAt })),
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
export function SellerPreOrdersView({ children, ...props }) {
|
|
72
|
+
const hasChildren = React.Children.count(children) > 0;
|
|
73
|
+
const dispatch = useActionDispatch();
|
|
74
|
+
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
75
|
+
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
76
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
77
|
+
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
78
|
+
const openFilters = useCallback(() => {
|
|
79
|
+
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
80
|
+
setFilterOpen(true);
|
|
81
|
+
}, [table]);
|
|
82
|
+
const applyFilters = useCallback(() => {
|
|
83
|
+
const updates = { page: "1" };
|
|
84
|
+
for (const k of FILTER_KEYS)
|
|
85
|
+
updates[k] = pendingFilters[k] ?? "";
|
|
86
|
+
table.setMany(updates);
|
|
87
|
+
setFilterOpen(false);
|
|
88
|
+
}, [pendingFilters, table]);
|
|
89
|
+
const clearFilters = useCallback(() => {
|
|
90
|
+
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
91
|
+
}, []);
|
|
92
|
+
const resetAll = useCallback(() => {
|
|
93
|
+
const updates = { q: "", sort: "" };
|
|
94
|
+
for (const k of FILTER_KEYS)
|
|
95
|
+
updates[k] = "";
|
|
96
|
+
table.setMany(updates);
|
|
97
|
+
setSearchInput("");
|
|
98
|
+
}, [table]);
|
|
99
|
+
const commitSearch = useCallback(() => {
|
|
100
|
+
table.set("q", searchInput.trim());
|
|
101
|
+
}, [searchInput, table]);
|
|
102
|
+
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
103
|
+
const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
|
|
104
|
+
const statusRaw = table.get("status");
|
|
105
|
+
const statusFilter = statusRaw && statusRaw !== "All" ? `status==${statusRaw}` : undefined;
|
|
106
|
+
const filters = [LISTING_TYPE_FILTER, statusFilter].filter(Boolean).join(",");
|
|
107
|
+
const { rows, total, isLoading, errorMessage } = useSellerListingData({
|
|
108
|
+
queryKey: ["seller", "pre-orders", "listing"],
|
|
109
|
+
endpoint: SELLER_ENDPOINTS.PRODUCTS,
|
|
110
|
+
page: table.getNumber("page", 1),
|
|
111
|
+
pageSize: PAGE_SIZE,
|
|
112
|
+
sorts: table.get("sort") || DEFAULT_SORT,
|
|
113
|
+
filters,
|
|
114
|
+
q: table.get("q") || undefined,
|
|
115
|
+
mapRows: (response) => toRecordArray(response.products).map((item, index) => {
|
|
116
|
+
const priceRaw = typeof item.price === "number" ? item.price : 0;
|
|
117
|
+
return {
|
|
118
|
+
id: toStringValue(item.id, `preorder-${index}`),
|
|
119
|
+
primary: toStringValue(item.title ?? item.name, "Untitled pre-order"),
|
|
120
|
+
secondary: toStringValue(item.condition, ""),
|
|
121
|
+
status: toStringValue(item.status, "draft"),
|
|
122
|
+
price: priceRaw ? toRupees(priceRaw) : "—",
|
|
123
|
+
deliveryDate: item.preorderAvailableDate
|
|
124
|
+
? toRelativeDate(item.preorderAvailableDate)
|
|
125
|
+
: "TBA",
|
|
126
|
+
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
127
|
+
imageUrl: toStringValue(item.mainImage ?? item.images?.[0], undefined),
|
|
128
|
+
};
|
|
129
|
+
}),
|
|
130
|
+
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
131
|
+
});
|
|
132
|
+
const currentPage = table.getNumber("page", 1);
|
|
133
|
+
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
134
|
+
if (hasChildren) {
|
|
135
|
+
return (_jsx(ListingViewShell, { portal: "seller", ...props, children: children }));
|
|
136
|
+
}
|
|
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); table.setPage(1); }, hideViewToggle: true, 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
|
+
e.stopPropagation();
|
|
139
|
+
void dispatch({ type: "NAVIGATE", href: String(ROUTES.STORE.PRE_ORDERS_EDIT(row.id)) });
|
|
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})` : ""] }) })] })] }))] }));
|
|
141
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ListingViewShellProps } from "../../../ui";
|
|
2
|
+
export interface SellerPrizeDrawsViewProps extends ListingViewShellProps {
|
|
3
|
+
}
|
|
4
|
+
export declare function SellerPrizeDrawsView({ children, ...props }: SellerPrizeDrawsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import React, { useState, useCallback } from "react";
|
|
4
|
+
import { Pencil, X } from "lucide-react";
|
|
5
|
+
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { Alert, Badge, Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
|
|
7
|
+
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
|
+
import { SELLER_PRIZE_DRAW_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
9
|
+
import { ROUTES } from "../../../constants";
|
|
10
|
+
import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
|
|
11
|
+
import { DataTable } from "../../admin/components/DataTable";
|
|
12
|
+
import { useActionDispatch } from "../../../react/hooks/use-action-dispatch";
|
|
13
|
+
const PAGE_SIZE = 25;
|
|
14
|
+
const FILTER_KEYS = ["status"];
|
|
15
|
+
const DEFAULT_SORT = "-createdAt";
|
|
16
|
+
// Hardcoded sieve clause — this view only ever shows prize draws.
|
|
17
|
+
const LISTING_TYPE_FILTER = "listingType==prize-draw";
|
|
18
|
+
const SORT_OPTIONS = [
|
|
19
|
+
{ value: "-createdAt", label: "Newest" },
|
|
20
|
+
{ value: "createdAt", label: "Oldest" },
|
|
21
|
+
{ value: "title", label: "Title A–Z" },
|
|
22
|
+
{ value: "prizeDrawEndDate", label: "Draw Date Soon" },
|
|
23
|
+
];
|
|
24
|
+
const STATUS_OPTIONS = SELLER_PRIZE_DRAW_STATUS_TABS;
|
|
25
|
+
const STATUS_VARIANT = {
|
|
26
|
+
active: "success",
|
|
27
|
+
draft: "default",
|
|
28
|
+
ended: "secondary",
|
|
29
|
+
cancelled: "danger",
|
|
30
|
+
};
|
|
31
|
+
const PRIZE_DRAW_COLUMNS = [
|
|
32
|
+
{
|
|
33
|
+
key: "thumbnail",
|
|
34
|
+
header: "",
|
|
35
|
+
className: "w-12",
|
|
36
|
+
render: (row) => row.imageUrl ? (_jsx("img", { src: row.imageUrl, alt: "", className: "w-10 h-10 rounded-lg object-cover border border-[var(--appkit-color-border)]" })) : (_jsx("div", { className: "w-10 h-10 rounded-lg bg-[var(--appkit-color-surface-raised)] border border-[var(--appkit-color-border)] flex items-center justify-center", children: _jsx("span", { className: "text-xs text-[var(--appkit-color-text-faint)]", children: "\u2013" }) })),
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
key: "primary",
|
|
40
|
+
header: "Prize Draw",
|
|
41
|
+
render: (row) => (_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "font-medium text-[var(--appkit-color-text)] line-clamp-1", children: row.primary }), _jsx("span", { className: "text-xs text-[var(--appkit-color-text-muted)]", children: row.secondary })] })),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
key: "entryFee",
|
|
45
|
+
header: "Entry Fee",
|
|
46
|
+
className: "w-28 text-right",
|
|
47
|
+
render: (row) => (_jsx("span", { className: "text-sm font-medium text-[var(--appkit-color-text)]", children: row.entryFee })),
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: "status",
|
|
51
|
+
header: "Status",
|
|
52
|
+
className: "w-28",
|
|
53
|
+
render: (row) => (_jsx(Badge, { variant: STATUS_VARIANT[row.status] ?? "default", children: row.status })),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key: "drawDate",
|
|
57
|
+
header: "Draw Date",
|
|
58
|
+
className: "w-32",
|
|
59
|
+
render: (row) => (_jsx("span", { className: "text-xs text-[var(--appkit-color-text-muted)]", children: row.drawDate })),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
key: "updatedAt",
|
|
63
|
+
header: "Updated",
|
|
64
|
+
className: "w-28",
|
|
65
|
+
render: (row) => (_jsx("span", { className: "text-xs text-[var(--appkit-color-text-muted)]", children: row.updatedAt })),
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
export function SellerPrizeDrawsView({ children, ...props }) {
|
|
69
|
+
const hasChildren = React.Children.count(children) > 0;
|
|
70
|
+
const dispatch = useActionDispatch();
|
|
71
|
+
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
72
|
+
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
73
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
74
|
+
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
75
|
+
const openFilters = useCallback(() => {
|
|
76
|
+
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
77
|
+
setFilterOpen(true);
|
|
78
|
+
}, [table]);
|
|
79
|
+
const applyFilters = useCallback(() => {
|
|
80
|
+
const updates = { page: "1" };
|
|
81
|
+
for (const k of FILTER_KEYS)
|
|
82
|
+
updates[k] = pendingFilters[k] ?? "";
|
|
83
|
+
table.setMany(updates);
|
|
84
|
+
setFilterOpen(false);
|
|
85
|
+
}, [pendingFilters, table]);
|
|
86
|
+
const clearFilters = useCallback(() => {
|
|
87
|
+
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
88
|
+
}, []);
|
|
89
|
+
const resetAll = useCallback(() => {
|
|
90
|
+
const updates = { q: "", sort: "" };
|
|
91
|
+
for (const k of FILTER_KEYS)
|
|
92
|
+
updates[k] = "";
|
|
93
|
+
table.setMany(updates);
|
|
94
|
+
setSearchInput("");
|
|
95
|
+
}, [table]);
|
|
96
|
+
const commitSearch = useCallback(() => {
|
|
97
|
+
table.set("q", searchInput.trim());
|
|
98
|
+
}, [searchInput, table]);
|
|
99
|
+
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
100
|
+
const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
|
|
101
|
+
const statusRaw = table.get("status");
|
|
102
|
+
const statusFilter = statusRaw && statusRaw !== "All" ? `status==${statusRaw}` : undefined;
|
|
103
|
+
const filters = [LISTING_TYPE_FILTER, statusFilter].filter(Boolean).join(",");
|
|
104
|
+
const { rows, total, isLoading, errorMessage } = useSellerListingData({
|
|
105
|
+
queryKey: ["seller", "prize-draws", "listing"],
|
|
106
|
+
endpoint: SELLER_ENDPOINTS.PRODUCTS,
|
|
107
|
+
page: table.getNumber("page", 1),
|
|
108
|
+
pageSize: PAGE_SIZE,
|
|
109
|
+
sorts: table.get("sort") || DEFAULT_SORT,
|
|
110
|
+
filters,
|
|
111
|
+
q: table.get("q") || undefined,
|
|
112
|
+
mapRows: (response) => toRecordArray(response.products).map((item, index) => {
|
|
113
|
+
const priceRaw = typeof item.price === "number" ? item.price : 0;
|
|
114
|
+
return {
|
|
115
|
+
id: toStringValue(item.id, `prize-draw-${index}`),
|
|
116
|
+
primary: toStringValue(item.title ?? item.name, "Untitled prize draw"),
|
|
117
|
+
secondary: toStringValue(item.condition, ""),
|
|
118
|
+
status: toStringValue(item.status, "draft"),
|
|
119
|
+
entryFee: priceRaw ? toRupees(priceRaw) : "Free",
|
|
120
|
+
drawDate: item.prizeDrawEndDate
|
|
121
|
+
? toRelativeDate(item.prizeDrawEndDate)
|
|
122
|
+
: "TBA",
|
|
123
|
+
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
124
|
+
imageUrl: toStringValue(item.mainImage ?? item.images?.[0], undefined),
|
|
125
|
+
};
|
|
126
|
+
}),
|
|
127
|
+
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
128
|
+
});
|
|
129
|
+
const currentPage = table.getNumber("page", 1);
|
|
130
|
+
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
131
|
+
if (hasChildren) {
|
|
132
|
+
return (_jsx(ListingViewShell, { portal: "seller", ...props, children: children }));
|
|
133
|
+
}
|
|
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); table.setPage(1); }, hideViewToggle: true, 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
|
+
e.stopPropagation();
|
|
136
|
+
void dispatch({ type: "NAVIGATE", href: String(ROUTES.STORE.PRIZE_DRAWS_EDIT(row.id)) });
|
|
137
|
+
}, "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})` : ""] }) })] })] }))] }));
|
|
138
|
+
}
|
|
@@ -7,6 +7,10 @@ export { SellerProductsView } from "./SellerProductsView";
|
|
|
7
7
|
export type { SellerProductsViewProps } from "./SellerProductsView";
|
|
8
8
|
export { SellerAuctionsView } from "./SellerAuctionsView";
|
|
9
9
|
export type { SellerAuctionsViewProps } from "./SellerAuctionsView";
|
|
10
|
+
export { SellerPreOrdersView } from "./SellerPreOrdersView";
|
|
11
|
+
export type { SellerPreOrdersViewProps } from "./SellerPreOrdersView";
|
|
12
|
+
export { SellerPrizeDrawsView } from "./SellerPrizeDrawsView";
|
|
13
|
+
export type { SellerPrizeDrawsViewProps } from "./SellerPrizeDrawsView";
|
|
10
14
|
export { SellerOrdersView } from "./SellerOrdersView";
|
|
11
15
|
export type { SellerOrdersViewProps } from "./SellerOrdersView";
|
|
12
16
|
export { SellerOffersView } from "./SellerOffersView";
|
|
@@ -3,6 +3,8 @@ export { SellerSidebar } from "./SellerSidebar";
|
|
|
3
3
|
export { SellerDashboardView } from "./SellerDashboardView";
|
|
4
4
|
export { SellerProductsView } from "./SellerProductsView";
|
|
5
5
|
export { SellerAuctionsView } from "./SellerAuctionsView";
|
|
6
|
+
export { SellerPreOrdersView } from "./SellerPreOrdersView";
|
|
7
|
+
export { SellerPrizeDrawsView } from "./SellerPrizeDrawsView";
|
|
6
8
|
export { SellerOrdersView } from "./SellerOrdersView";
|
|
7
9
|
export { SellerOffersView } from "./SellerOffersView";
|
|
8
10
|
export { SellerOffersPanel } from "./SellerOffersPanel";
|
package/dist/index.d.ts
CHANGED
|
@@ -1082,6 +1082,8 @@ export { QuickEditMenu } from "./features/admin/index";
|
|
|
1082
1082
|
export type { QuickEditAction, QuickEditMenuProps } from "./features/admin/index";
|
|
1083
1083
|
export { AdminPageHeader } from "./features/admin/index";
|
|
1084
1084
|
export { AdminPayoutsView } from "./features/admin/index";
|
|
1085
|
+
export { AdminPrizeDrawsView } from "./features/admin/index";
|
|
1086
|
+
export type { AdminPrizeDrawsViewProps } from "./features/admin/index";
|
|
1085
1087
|
export { AdminProductsView } from "./features/admin/index";
|
|
1086
1088
|
export { AdminProductEditorView } from "./features/admin/index";
|
|
1087
1089
|
export type { AdminProductEditorViewProps } from "./features/admin/index";
|
|
@@ -2605,6 +2607,10 @@ export { SellerPayoutHistoryTable } from "./features/seller/index";
|
|
|
2605
2607
|
export { SellerPayoutSettingsView } from "./features/seller/index";
|
|
2606
2608
|
export { SellerPayoutStats } from "./features/seller/index";
|
|
2607
2609
|
export { SellerPayoutsView } from "./features/seller/index";
|
|
2610
|
+
export { SellerPreOrdersView } from "./features/seller/index";
|
|
2611
|
+
export type { SellerPreOrdersViewProps } from "./features/seller/index";
|
|
2612
|
+
export { SellerPrizeDrawsView } from "./features/seller/index";
|
|
2613
|
+
export type { SellerPrizeDrawsViewProps } from "./features/seller/index";
|
|
2608
2614
|
export { SellerProductsView } from "./features/seller/index";
|
|
2609
2615
|
export { SellerRevenueChart } from "./features/seller/index";
|
|
2610
2616
|
export { SellerShippingView } from "./features/seller/index";
|
package/dist/index.js
CHANGED
|
@@ -2129,6 +2129,9 @@ export { AdminPageHeader } from "./features/admin/index";
|
|
|
2129
2129
|
// AdminPayoutsView - Component for admin payouts view.
|
|
2130
2130
|
export { AdminPayoutsView } from "./features/admin/index";
|
|
2131
2131
|
// [CLIENT-SSR]-Runs in both SSR and browser — React component or hook that does not depend on browser-only APIs.
|
|
2132
|
+
// AdminPrizeDrawsView - Component for admin prize draws listing view (SB4-E).
|
|
2133
|
+
export { AdminPrizeDrawsView } from "./features/admin/index";
|
|
2134
|
+
// [CLIENT-SSR]-Runs in both SSR and browser — React component or hook that does not depend on browser-only APIs.
|
|
2132
2135
|
// AdminProductsView - Component for admin products view.
|
|
2133
2136
|
export { AdminProductsView } from "./features/admin/index";
|
|
2134
2137
|
// AdminProductEditorView - Component for admin product create/edit form (3-mode: standard/auction/pre-order).
|
|
@@ -4877,6 +4880,12 @@ export { SellerPayoutStats } from "./features/seller/index";
|
|
|
4877
4880
|
// SellerPayoutsView - Component for seller payouts view.
|
|
4878
4881
|
export { SellerPayoutsView } from "./features/seller/index";
|
|
4879
4882
|
// [CLIENT-SSR]-Runs in both SSR and browser — React component or hook that does not depend on browser-only APIs.
|
|
4883
|
+
// SellerPreOrdersView - Component for seller pre-orders listing view (SB4-E).
|
|
4884
|
+
export { SellerPreOrdersView } from "./features/seller/index";
|
|
4885
|
+
// [CLIENT-SSR]-Runs in both SSR and browser — React component or hook that does not depend on browser-only APIs.
|
|
4886
|
+
// SellerPrizeDrawsView - Component for seller prize draws listing view (SB4-E).
|
|
4887
|
+
export { SellerPrizeDrawsView } from "./features/seller/index";
|
|
4888
|
+
// [CLIENT-SSR]-Runs in both SSR and browser — React component or hook that does not depend on browser-only APIs.
|
|
4880
4889
|
// SellerProductsView - Component for seller products view.
|
|
4881
4890
|
export { SellerProductsView } from "./features/seller/index";
|
|
4882
4891
|
// [CLIENT-SSR]-Runs in both SSR and browser — React component or hook that does not depend on browser-only APIs.
|