@mohasinac/appkit 2.7.58 → 2.7.59
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/features/scams/og.js +1 -1
- package/dist/_internal/shared/actions/bulk-helpers.d.ts +3 -0
- package/dist/_internal/shared/actions/bulk-helpers.js +19 -0
- package/dist/_internal/shared/config/schema.d.ts +4 -4
- package/dist/client.d.ts +3 -0
- package/dist/client.js +2 -0
- package/dist/configs/next.js +11 -0
- package/dist/features/account/components/AddressesIndexListing.js +3 -4
- package/dist/features/account/components/UserSidebar.js +14 -10
- package/dist/features/admin/components/AdminAllEventEntriesView.js +3 -9
- package/dist/features/admin/components/AdminBidsView.js +2 -8
- package/dist/features/admin/components/AdminBlogView.js +4 -9
- package/dist/features/admin/components/AdminBrandEditorView.js +14 -17
- package/dist/features/admin/components/AdminBrandsView.js +4 -9
- package/dist/features/admin/components/AdminBundlesView.js +39 -58
- package/dist/features/admin/components/AdminCarouselView.js +7 -20
- package/dist/features/admin/components/AdminCartsView.js +3 -4
- package/dist/features/admin/components/AdminCategoriesView.js +4 -9
- package/dist/features/admin/components/AdminCategoryEditorView.js +1 -5
- package/dist/features/admin/components/AdminContactView.js +6 -24
- package/dist/features/admin/components/AdminCouponEditorView.js +14 -17
- package/dist/features/admin/components/AdminCouponsView.js +8 -16
- package/dist/features/admin/components/AdminFaqEditorView.js +14 -17
- package/dist/features/admin/components/AdminFaqsView.js +18 -69
- package/dist/features/admin/components/AdminFeatureEditorView.js +7 -9
- package/dist/features/admin/components/AdminNewsletterView.js +4 -14
- package/dist/features/admin/components/AdminNotificationsView.js +2 -8
- package/dist/features/admin/components/AdminOrdersView.js +2 -8
- package/dist/features/admin/components/AdminPayoutsView.js +2 -8
- package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -9
- package/dist/features/admin/components/AdminProductEditorView.js +1 -5
- package/dist/features/admin/components/AdminProductsView.js +4 -9
- package/dist/features/admin/components/AdminReviewsView.js +2 -8
- package/dist/features/admin/components/AdminScammersView.js +2 -8
- package/dist/features/admin/components/AdminSessionsView.js +2 -8
- package/dist/features/admin/components/AdminStoresView.js +2 -8
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -6
- package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +17 -20
- package/dist/features/admin/components/AdminSupportTicketsView.js +2 -8
- package/dist/features/admin/components/AdminTeamView.js +5 -14
- package/dist/features/admin/components/AdminUsersView.js +2 -8
- package/dist/features/admin/hooks/useAdminListing.d.ts +58 -0
- package/dist/features/admin/hooks/useAdminListing.js +71 -0
- package/dist/features/auth/schemas/firestore.d.ts +1 -0
- package/dist/features/blog/components/BlogIndexListing.js +3 -6
- package/dist/features/categories/components/CategoriesIndexListing.js +3 -6
- package/dist/features/events/components/AdminEventsView.js +4 -4
- package/dist/features/events/components/EventsIndexListing.js +3 -4
- package/dist/features/homepage/components/AnnouncementBar.d.ts +4 -1
- package/dist/features/homepage/components/AnnouncementBar.js +51 -4
- package/dist/features/homepage/components/MarketplaceHomepageView.d.ts +3 -1
- package/dist/features/homepage/components/MarketplaceHomepageView.js +2 -2
- package/dist/features/homepage/components/index.d.ts +1 -1
- package/dist/features/homepage/components/index.js +1 -1
- package/dist/features/homepage/lib/section-renderer.d.ts +2 -2
- package/dist/features/homepage/lib/section-renderer.js +2 -2
- package/dist/features/orders/repository/orders.repository.d.ts +0 -1
- package/dist/features/orders/repository/orders.repository.js +0 -1
- package/dist/features/orders/server.d.ts +1 -1
- package/dist/features/orders/server.js +1 -1
- package/dist/features/products/components/GroupSettingsPanel.js +64 -48
- package/dist/features/products/repository/products.repository.d.ts +0 -2
- package/dist/features/products/repository/products.repository.js +0 -2
- package/dist/features/products/server.d.ts +1 -1
- package/dist/features/products/server.js +1 -1
- package/dist/features/promotions/components/CouponCard.js +5 -5
- package/dist/features/promotions/components/CouponsIndexListing.js +3 -2
- package/dist/features/reviews/components/ReviewsIndexListing.js +3 -4
- package/dist/features/reviews/repository/reviews.repository.d.ts +0 -1
- package/dist/features/reviews/repository/reviews.repository.js +0 -1
- package/dist/features/reviews/server.d.ts +1 -1
- package/dist/features/reviews/server.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +10 -20
- package/dist/features/seller/components/SellerAuctionsView.js +13 -22
- package/dist/features/seller/components/SellerBidsView.js +5 -5
- package/dist/features/seller/components/SellerBundlesView.js +11 -19
- package/dist/features/seller/components/SellerClassifiedView.js +11 -19
- package/dist/features/seller/components/SellerCouponsView.js +14 -20
- package/dist/features/seller/components/SellerDigitalCodesView.js +11 -19
- package/dist/features/seller/components/SellerGroupedListingsView.js +6 -10
- package/dist/features/seller/components/SellerLiveView.js +11 -19
- package/dist/features/seller/components/SellerOffersView.js +3 -4
- package/dist/features/seller/components/SellerOrdersView.js +8 -22
- package/dist/features/seller/components/SellerPayoutMethodsView.js +11 -23
- package/dist/features/seller/components/SellerPayoutsView.js +3 -4
- package/dist/features/seller/components/SellerPreOrdersView.js +13 -22
- package/dist/features/seller/components/SellerPrizeDrawsView.js +13 -22
- package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +1 -1
- package/dist/features/seller/components/SellerProductsFilterDrawer.js +3 -6
- package/dist/features/seller/components/SellerProductsView.js +9 -22
- package/dist/features/seller/components/SellerShippingConfigsView.js +11 -23
- package/dist/features/seller/components/SellerStoreCategoriesView.js +14 -33
- package/dist/features/seller/components/SellerTemplatesView.js +25 -41
- package/dist/features/stores/components/StoreReviewsListing.js +3 -4
- package/dist/features/stores/components/StoresIndexListing.js +3 -4
- package/dist/index.d.ts +2 -6
- package/dist/index.js +3 -12
- package/dist/react/contexts/SessionContext.d.ts +2 -0
- package/dist/react/contexts/SessionContext.js +2 -0
- package/dist/react/hooks/useEntityDelete.d.ts +15 -0
- package/dist/react/hooks/useEntityDelete.js +39 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +2 -0
- package/dist/repositories/index.d.ts +3 -3
- package/dist/repositories/index.js +3 -3
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/ListingFilterDrawer.d.ts +10 -0
- package/dist/ui/components/ListingFilterDrawer.js +8 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/package.json +1 -1
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
3
|
-
import React
|
|
4
|
-
import { Plus
|
|
5
|
-
import {
|
|
6
|
-
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
-
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
8
|
-
import { BulkActionBar, Button, Heading, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text } from "../../../ui";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Plus } from "lucide-react";
|
|
5
|
+
import { BulkActionBar, Button, Heading, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text, ListingFilterDrawer } from "../../../ui";
|
|
9
6
|
import { useBottomActions } from "../../layout";
|
|
10
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
8
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
12
|
-
import {
|
|
9
|
+
import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
|
|
10
|
+
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
|
|
11
|
+
import { useAdminListing } from "../hooks/useAdminListing";
|
|
13
12
|
import { DataTable } from "./DataTable";
|
|
14
13
|
import { AdminViewCards } from "./AdminViewCards";
|
|
15
14
|
import { AdminFaqEditorView } from "./AdminFaqEditorView";
|
|
16
|
-
const PAGE_SIZE = 25;
|
|
17
15
|
const FILTER_KEYS = ["isActive"];
|
|
18
16
|
const DEFAULT_SORT = "priority";
|
|
19
17
|
const SORT_OPTIONS = [
|
|
@@ -21,55 +19,13 @@ const SORT_OPTIONS = [
|
|
|
21
19
|
{ value: "-createdAt", label: "Newest" },
|
|
22
20
|
{ value: "question", label: "Question A–Z" },
|
|
23
21
|
];
|
|
24
|
-
function FaqsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
25
|
-
if (!filterOpen)
|
|
26
|
-
return null;
|
|
27
|
-
return (_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 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-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(Text, { 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: "Published", value: "true" }, { label: "Draft", 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 ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] }) }), _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})` : ""] }) })] })] }));
|
|
28
|
-
}
|
|
29
22
|
export function AdminFaqsView({ children, getRowHref, onBulkArchive, onBulkDelete, ...props }) {
|
|
30
23
|
const hasChildren = React.Children.count(children) > 0;
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
35
|
-
const [filterOpen, setFilterOpen] = useState(false);
|
|
36
|
-
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
37
|
-
const openFilters = useCallback(() => {
|
|
38
|
-
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
39
|
-
setFilterOpen(true);
|
|
40
|
-
}, [table]);
|
|
41
|
-
const applyFilters = useCallback(() => {
|
|
42
|
-
const updates = { page: "1" };
|
|
43
|
-
for (const k of FILTER_KEYS)
|
|
44
|
-
updates[k] = pendingFilters[k] ?? "";
|
|
45
|
-
table.setMany(updates);
|
|
46
|
-
setFilterOpen(false);
|
|
47
|
-
}, [pendingFilters, table]);
|
|
48
|
-
const clearFilters = useCallback(() => {
|
|
49
|
-
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
50
|
-
}, []);
|
|
51
|
-
const resetAll = useCallback(() => {
|
|
52
|
-
const updates = { q: "", sort: "" };
|
|
53
|
-
for (const k of FILTER_KEYS)
|
|
54
|
-
updates[k] = "";
|
|
55
|
-
table.setMany(updates);
|
|
56
|
-
setSearchInput("");
|
|
57
|
-
}, [table]);
|
|
58
|
-
const commitSearch = useCallback(() => {
|
|
59
|
-
table.set("q", searchInput.trim());
|
|
60
|
-
}, [searchInput, table]);
|
|
61
|
-
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
62
|
-
const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
|
|
63
|
-
const isActiveRaw = table.get("isActive");
|
|
64
|
-
const filters = isActiveRaw ? `isActive==${isActiveRaw}` : undefined;
|
|
65
|
-
const { rows, total, isLoading, errorMessage, refetch } = useAdminListingData({
|
|
24
|
+
const listing = useAdminListing({
|
|
25
|
+
filterKeys: FILTER_KEYS,
|
|
26
|
+
defaultSort: DEFAULT_SORT,
|
|
66
27
|
queryKey: ["admin", "faqs", "listing"],
|
|
67
28
|
endpoint: ADMIN_ENDPOINTS.FAQS,
|
|
68
|
-
page: table.getNumber("page", 1),
|
|
69
|
-
pageSize: PAGE_SIZE,
|
|
70
|
-
sorts: table.get("sort") || DEFAULT_SORT,
|
|
71
|
-
filters,
|
|
72
|
-
q: table.get("q") || undefined,
|
|
73
29
|
mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
|
|
74
30
|
id: toStringValue(item.id, `faq-${index}`),
|
|
75
31
|
primary: toStringValue(item.question, "Untitled question"),
|
|
@@ -78,26 +34,19 @@ export function AdminFaqsView({ children, getRowHref, onBulkArchive, onBulkDelet
|
|
|
78
34
|
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
79
35
|
})),
|
|
80
36
|
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
37
|
+
buildFilters: (filterState) => {
|
|
38
|
+
const isActiveRaw = filterState.isActive;
|
|
39
|
+
return isActiveRaw ? `isActive==${isActiveRaw}` : undefined;
|
|
40
|
+
},
|
|
81
41
|
});
|
|
82
|
-
const
|
|
83
|
-
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
84
|
-
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
42
|
+
const { view, setView, table, panel, searchInput, setSearchInput, commitSearch, filterOpen, setFilterOpen, openFilters, applyFilters, clearFilters, pendingFilters, setPendingFilters, activeFilterCount, hasActiveState, resetAll, rows, isLoading, errorMessage, refetch, currentPage, totalPages, selection } = listing;
|
|
85
43
|
const bulkActions = [
|
|
86
|
-
...(onBulkArchive ? [{
|
|
87
|
-
|
|
88
|
-
label: ACTIONS.ADMIN["archive-faq"].label,
|
|
89
|
-
onClick: async () => { await onBulkArchive(selection.selectedIds); selection.clearSelection(); refetch?.(); },
|
|
90
|
-
}] : []),
|
|
91
|
-
...(onBulkDelete ? [{
|
|
92
|
-
id: "bulk-delete",
|
|
93
|
-
label: ACTIONS.ADMIN["delete-faq"].label,
|
|
94
|
-
variant: "danger",
|
|
95
|
-
onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); refetch?.(); },
|
|
96
|
-
}] : []),
|
|
44
|
+
...(onBulkArchive ? [buildBulkAction(ACTIONS.ADMIN["archive-faq"], async () => { await onBulkArchive(selection.selectedIds); selection.clearSelection(); refetch?.(); })] : []),
|
|
45
|
+
...(onBulkDelete ? [buildBulkAction(ACTIONS.ADMIN["delete-faq"], async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); refetch?.(); })] : []),
|
|
97
46
|
];
|
|
98
47
|
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
99
48
|
if (hasChildren) {
|
|
100
49
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
101
50
|
}
|
|
102
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(Heading, { level: 1, className: "sr-only", children: "FAQs" }), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search questions, categories, or tokens", 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: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add FAQ"] }) }), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No FAQs found", onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No FAQs found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(
|
|
51
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(Heading, { level: 1, className: "sr-only", children: "FAQs" }), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search questions, categories, or tokens", 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: _jsxs(Button, { size: "sm", onClick: panel.openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add FAQ"] }) }), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No FAQs found", onRowClick: (row) => panel.openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No FAQs found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { 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: "Published", value: "true" }, { label: "Draft", 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 ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] }) }), _jsx(SideDrawer, { isOpen: panel.isCreateOpen || panel.isEditOpen, onClose: panel.closePanel, title: panel.isCreateOpen ? "Add FAQ" : "Edit FAQ", mode: panel.isCreateOpen ? "create" : "edit", children: (panel.isCreateOpen || panel.isEditOpen) && (_jsx(AdminFaqEditorView, { faqId: panel.editId ?? undefined, onSaved: panel.closePanel, onDeleted: panel.closePanel, embedded: true })) })] }));
|
|
103
52
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
5
|
-
import { Button, Div, Form, Grid, Input, Select, Stack, StackedViewShell, Text, Toggle, useToast, } from "../../../ui";
|
|
5
|
+
import { Button, ConfirmDeleteModal, Div, Form, Grid, Input, Select, Stack, StackedViewShell, Text, Toggle, useToast, } from "../../../ui";
|
|
6
6
|
import { apiClient } from "../../../http";
|
|
7
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
8
|
import { ERROR_MESSAGES } from "../../../errors/messages";
|
|
@@ -29,6 +29,7 @@ export function AdminFeatureEditorView({ featureId, fixedScope, fixedStoreId, em
|
|
|
29
29
|
const [productTypes, setProductTypes] = React.useState(["all"]);
|
|
30
30
|
const [storeId, setStoreId] = React.useState(fixedStoreId ?? "");
|
|
31
31
|
const [isActive, setIsActive] = React.useState(true);
|
|
32
|
+
const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
|
|
32
33
|
const [displayOrder, setDisplayOrder] = React.useState(String(PRODUCT_FEATURE_DEFAULT_DISPLAY_ORDER));
|
|
33
34
|
const createEndpoint = endpointOverride?.create ?? ADMIN_ENDPOINTS.PRODUCT_FEATURES;
|
|
34
35
|
const byIdEndpoint = endpointOverride?.byId ?? ADMIN_ENDPOINTS.PRODUCT_FEATURE_BY_ID;
|
|
@@ -138,13 +139,10 @@ export function AdminFeatureEditorView({ featureId, fixedScope, fixedStoreId, em
|
|
|
138
139
|
const checked = productTypes.includes(opt.value);
|
|
139
140
|
const pillClass = `${PILL_BASE_CLASS} ${checked ? PILL_CHECKED_CLASS : PILL_UNCHECKED_CLASS}`;
|
|
140
141
|
return (_jsxs("label", { className: pillClass, children: [_jsx("input", { type: "checkbox", className: "sr-only", checked: checked, onChange: () => toggleProductType(opt.value) }), opt.label] }, opt.value));
|
|
141
|
-
}) })] }), !fixedScope && (_jsx(Select, { label: "Scope", value: scope, onChange: (e) => setScope(e.target.value), options: PRODUCT_FEATURE_SCOPE_OPTIONS })), scope === "store" && !fixedStoreId && (_jsx(Select, { label: "Store", value: storeId, onChange: (e) => setStoreId(e.target.value), options: storeOptions, required: true, helperText: "Store-scope features are visible only on this store's listings." })), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsxs(Div, { className: "flex gap-3 pt-2", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: isDisabled, children: isEdit ? "Save changes" : "Create feature" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () =>
|
|
142
|
-
|
|
143
|
-
deleteMutation.mutate();
|
|
144
|
-
}
|
|
145
|
-
}, children: "Delete" }))] })] }) }, "feature-form"));
|
|
142
|
+
}) })] }), !fixedScope && (_jsx(Select, { label: "Scope", value: scope, onChange: (e) => setScope(e.target.value), options: PRODUCT_FEATURE_SCOPE_OPTIONS })), scope === "store" && !fixedStoreId && (_jsx(Select, { label: "Store", value: storeId, onChange: (e) => setStoreId(e.target.value), options: storeOptions, required: true, helperText: "Store-scope features are visible only on this store's listings." })), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsxs(Div, { className: "flex gap-3 pt-2", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: isDisabled, children: isEdit ? "Save changes" : "Create feature" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => setDeleteConfirmOpen(true), children: "Delete" }))] })] }) }, "feature-form"));
|
|
143
|
+
const deleteModal = deleteConfirmOpen && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Feature", message: DELETE_CONFIRM_TEXT, onConfirm: () => { deleteMutation.mutate(); setDeleteConfirmOpen(false); }, onClose: () => setDeleteConfirmOpen(false), isDeleting: deleteMutation.isPending }));
|
|
146
144
|
if (embedded) {
|
|
147
|
-
return
|
|
145
|
+
return _jsxs(Div, { className: "overflow-y-auto p-4", children: [formSection, deleteModal] });
|
|
148
146
|
}
|
|
149
|
-
return (_jsx(StackedViewShell, { portal: "admin", ...rest, title: isEdit ? "Edit Feature" : "New Feature", sections: [formSection] }));
|
|
147
|
+
return (_jsxs(_Fragment, { children: [_jsx(StackedViewShell, { portal: "admin", ...rest, title: isEdit ? "Edit Feature" : "New Feature", sections: [formSection] }), deleteModal] }));
|
|
150
148
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
4
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { BulkActionBar, Button, ConfirmDeleteModal, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, useToast, } from "../../../ui";
|
|
7
|
+
import { BulkActionBar, Button, ConfirmDeleteModal, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, useToast, ListingFilterDrawer } from "../../../ui";
|
|
9
8
|
import { useBottomActions } from "../../layout";
|
|
10
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
|
+
import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
|
|
12
12
|
import { ADMIN_NEWSLETTER_STATUS_TABS } from "../constants/filter-tabs";
|
|
13
13
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
14
14
|
import { DataTable } from "./DataTable";
|
|
@@ -21,11 +21,6 @@ const SORT_OPTIONS = [
|
|
|
21
21
|
{ value: "subscribedAt", label: "Oldest" },
|
|
22
22
|
];
|
|
23
23
|
const STATUS_OPTIONS = ADMIN_NEWSLETTER_STATUS_TABS;
|
|
24
|
-
function NewsletterFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
25
|
-
if (!filterOpen)
|
|
26
|
-
return null;
|
|
27
|
-
return (_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 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-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})` : ""] }) })] })] }));
|
|
28
|
-
}
|
|
29
24
|
export function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }) {
|
|
30
25
|
const hasChildren = React.Children.count(children) > 0;
|
|
31
26
|
const [view, setView] = useState("table");
|
|
@@ -126,12 +121,7 @@ export function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }) {
|
|
|
126
121
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
127
122
|
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
128
123
|
const bulkActions = [
|
|
129
|
-
...(onBulkUnsubscribe ? [{
|
|
130
|
-
id: "bulk-unsubscribe",
|
|
131
|
-
label: ACTIONS.ADMIN["unsubscribe-newsletter"].label,
|
|
132
|
-
variant: "danger",
|
|
133
|
-
onClick: async () => { await onBulkUnsubscribe(selection.selectedIds); selection.clearSelection(); },
|
|
134
|
-
}] : []),
|
|
124
|
+
...(onBulkUnsubscribe ? [buildBulkAction(ACTIONS.ADMIN["unsubscribe-newsletter"], async () => { await onBulkUnsubscribe(selection.selectedIds); selection.clearSelection(); })] : []),
|
|
135
125
|
];
|
|
136
126
|
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
137
127
|
if (hasChildren) {
|
|
@@ -147,6 +137,6 @@ export function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }) {
|
|
|
147
137
|
onClick: () => { setSelectedRow(nr); setUnsubscribeOpen(true); },
|
|
148
138
|
},
|
|
149
139
|
] }));
|
|
150
|
-
} })] }), _jsx(
|
|
140
|
+
} })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) })] }), _jsx(ConfirmDeleteModal, { isOpen: unsubscribeOpen, onClose: () => { setUnsubscribeOpen(false); setSelectedRow(null); }, onConfirm: () => { if (selectedRow)
|
|
151
141
|
unsubscribeMutation.mutate(selectedRow.id); }, isDeleting: unsubscribeMutation.isPending, title: `Unsubscribe ${selectedRow?.primary ?? "subscriber"}?`, message: "The subscriber will be marked as unsubscribed and will no longer receive newsletter emails.", confirmText: "Unsubscribe", variant: "warning" })] }));
|
|
152
142
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
4
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { BulkActionBar, ConfirmDeleteModal, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text, useToast } from "../../../ui";
|
|
7
|
+
import { BulkActionBar, ConfirmDeleteModal, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text, useToast, ListingFilterDrawer } from "../../../ui";
|
|
9
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
9
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
10
|
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
|
|
@@ -24,11 +23,6 @@ const NOTIF_TYPES = [
|
|
|
24
23
|
"All", "order_placed", "order_shipped", "order_delivered", "order_cancelled",
|
|
25
24
|
"bid_placed", "bid_outbid", "bid_won", "review_posted", "payout_processed",
|
|
26
25
|
];
|
|
27
|
-
function NotificationsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
28
|
-
if (!filterOpen)
|
|
29
|
-
return null;
|
|
30
|
-
return (_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 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-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(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Type" }), _jsx("div", { className: "flex flex-wrap gap-2", children: NOTIF_TYPES.map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, type: opt === "All" ? "" : opt })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.type || "All") === opt ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt }, opt))) })] }) }), _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})` : ""] }) })] })] }));
|
|
31
|
-
}
|
|
32
26
|
export function AdminNotificationsView({ children, ...props }) {
|
|
33
27
|
const hasChildren = React.Children.count(children) > 0;
|
|
34
28
|
const [view, setView] = useState("table");
|
|
@@ -132,6 +126,6 @@ export function AdminNotificationsView({ children, ...props }) {
|
|
|
132
126
|
{ label: ACTIONS.ADMIN["resend-notification"].label, onClick: () => resendMutation.mutate(nr.id) },
|
|
133
127
|
{ label: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label, destructive: ROW_ACTION_META[ROW_ACTION_ID.DELETE].destructive, onClick: () => setDeleteTarget(nr) },
|
|
134
128
|
] }));
|
|
135
|
-
} })] }), _jsx(
|
|
129
|
+
} })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Type" }), _jsx("div", { className: "flex flex-wrap gap-2", children: NOTIF_TYPES.map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, type: opt === "All" ? "" : opt })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.type || "All") === opt ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt }, opt))) })] }) })] }), _jsx(ConfirmDeleteModal, { isOpen: Boolean(deleteTarget), onClose: () => setDeleteTarget(null), onConfirm: () => { if (deleteTarget)
|
|
136
130
|
deleteMutation.mutate(deleteTarget.id); }, isDeleting: deleteMutation.isPending, title: "Delete notification?", message: "This notification will be permanently removed.", confirmText: "Delete", variant: "danger" })] }));
|
|
137
131
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
5
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
-
import { BulkActionBar, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, useToast } from "../../../ui";
|
|
6
|
+
import { BulkActionBar, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, useToast, ListingFilterDrawer } from "../../../ui";
|
|
8
7
|
import { apiClient } from "../../../http";
|
|
9
8
|
import { QuickEditMenu } from "./QuickEditMenu";
|
|
10
9
|
import { AdminViewCards } from "./AdminViewCards";
|
|
@@ -23,11 +22,6 @@ const SORT_OPTIONS = [
|
|
|
23
22
|
{ value: "createdAt", label: "Oldest" },
|
|
24
23
|
];
|
|
25
24
|
const STATUS_OPTIONS = ADMIN_ORDER_STATUS_TABS;
|
|
26
|
-
function OrdersFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
27
|
-
if (!filterOpen)
|
|
28
|
-
return null;
|
|
29
|
-
return (_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 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-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})` : ""] }) })] })] }));
|
|
30
|
-
}
|
|
31
25
|
export function AdminOrdersView({ children, ...props }) {
|
|
32
26
|
const hasChildren = React.Children.count(children) > 0;
|
|
33
27
|
const { showToast } = useToast();
|
|
@@ -135,5 +129,5 @@ export function AdminOrdersView({ children, ...props }) {
|
|
|
135
129
|
onSubmit: (vals) => handleQuickStatus(row.id, String(vals.status ?? "")),
|
|
136
130
|
submitLabel: "Update",
|
|
137
131
|
},
|
|
138
|
-
] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No orders found", onRowClick: (row) => { setSelectedRow(row); setDrawerOpen(true); }, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(
|
|
132
|
+
] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No orders found", onRowClick: (row) => { setSelectedRow(row); setDrawerOpen(true); }, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) })] }), _jsx(AdminOrderEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), orderId: selectedRow?.id, orderLabel: selectedRow?.primary, currentStatus: selectedRow?.status })] }));
|
|
139
133
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
4
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { BulkActionBar, Button, Form, FormActions, FilterChipGroup, Input, ListingToolbar, ListingViewShell, Modal, Pagination, RowActionMenu, useToast, } from "../../../ui";
|
|
7
|
+
import { BulkActionBar, Button, Form, FormActions, FilterChipGroup, Input, ListingToolbar, ListingViewShell, Modal, Pagination, RowActionMenu, useToast, ListingFilterDrawer } from "../../../ui";
|
|
9
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
9
|
import { ADMIN_PAYOUT_STATUS_TABS } from "../constants/filter-tabs";
|
|
11
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
@@ -22,11 +21,6 @@ const SORT_OPTIONS = [
|
|
|
22
21
|
{ value: "-amount", label: "Highest amount" },
|
|
23
22
|
];
|
|
24
23
|
const STATUS_OPTIONS = ADMIN_PAYOUT_STATUS_TABS;
|
|
25
|
-
function PayoutsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
26
|
-
if (!filterOpen)
|
|
27
|
-
return null;
|
|
28
|
-
return (_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 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-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})` : ""] }) })] })] }));
|
|
29
|
-
}
|
|
30
24
|
export function AdminPayoutsView({ children, ...props }) {
|
|
31
25
|
const hasChildren = React.Children.count(children) > 0;
|
|
32
26
|
const [view, setView] = useState("table");
|
|
@@ -149,7 +143,7 @@ export function AdminPayoutsView({ children, ...props }) {
|
|
|
149
143
|
pr.status.toLowerCase() === "cancelled",
|
|
150
144
|
},
|
|
151
145
|
] }));
|
|
152
|
-
} })] }), _jsx(
|
|
146
|
+
} })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) })] }), _jsx(Modal, { isOpen: markPaidOpen, onClose: closePaidModal, title: "Mark payout as paid", children: _jsxs(Form, { className: "space-y-4", onSubmit: (e) => {
|
|
153
147
|
e.preventDefault();
|
|
154
148
|
markPaid.mutate();
|
|
155
149
|
}, children: [_jsx(Input, { label: "Transaction / reference ID", value: transactionId, onChange: (e) => setTransactionId(e.target.value), placeholder: "UTR, UPI ref, or bank transfer ID (optional)" }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "outline", onClick: closePaidModal, children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", disabled: markPaid.isPending, children: markPaid.isPending ? "Saving..." : "Confirm paid" })] })] }) })] }));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { Pencil
|
|
4
|
+
import { Pencil } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
-
import { Alert, Badge, BulkActionBar, Button, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Text } from "../../../ui";
|
|
7
|
+
import { Alert, Badge, BulkActionBar, Button, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Text, ListingFilterDrawer } from "../../../ui";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
9
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
10
|
import { ADMIN_PRODUCT_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -66,11 +66,6 @@ const PRIZE_DRAW_COLUMNS = [
|
|
|
66
66
|
render: (row) => (_jsx("span", { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.updatedAt })),
|
|
67
67
|
},
|
|
68
68
|
];
|
|
69
|
-
function PrizeDrawsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
70
|
-
if (!filterOpen)
|
|
71
|
-
return null;
|
|
72
|
-
return (_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})` : ""] }) })] })] }));
|
|
73
|
-
}
|
|
74
69
|
export function AdminPrizeDrawsView({ children, ...props }) {
|
|
75
70
|
const hasChildren = React.Children.count(children) > 0;
|
|
76
71
|
const [view, setView] = useState("table");
|
|
@@ -145,5 +140,5 @@ export function AdminPrizeDrawsView({ children, ...props }) {
|
|
|
145
140
|
table.set("sort", v);
|
|
146
141
|
}, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
147
142
|
{ id: "delete", label: ACTIONS.ADMIN["delete-prize-draw"].label, variant: "secondary", onClick: () => { selection.clearSelection(); } },
|
|
148
|
-
] }), 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" }) }) })) })] }), _jsx(
|
|
143
|
+
] }), 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" }) }) })) })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) })] }));
|
|
149
144
|
}
|
|
@@ -147,11 +147,7 @@ export function AdminProductEditorView({ productId, onSaved, onDeleted, embedded
|
|
|
147
147
|
saveMutation.mutate();
|
|
148
148
|
}, className: "space-y-6", children: [_jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400 mb-4", children: "Listing Type" }), _jsxs(Tabs, { value: mode, onChange: handleModeChange, children: [_jsxs(TabsList, { children: [(!enabledListingTypes || enabledListingTypes.includes("standard")) && (_jsx(TabsTrigger, { value: "standard", children: "Standard" })), (!enabledListingTypes || enabledListingTypes.includes("auction")) && (_jsx(TabsTrigger, { value: "auction", children: "Auction" })), (!enabledListingTypes || enabledListingTypes.includes("pre-order")) && (_jsx(TabsTrigger, { value: "preorder", children: "Pre-order" }))] }), _jsx(TabsContent, { value: "standard" }), _jsx(TabsContent, { value: "auction" }), _jsx(TabsContent, { value: "preorder" })] })] }), _jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400 mb-4", children: "Classification" }), _jsxs("div", { className: "space-y-4", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: "Store" }), _jsx(DynamicSelect, { value: product.storeId ?? null, onChange: handleStoreSelect, loadOptions: loadStoreOptions, placeholder: "Search stores\u2026", searchPlaceholder: "Type store name\u2026", noResultsText: "No stores found", ariaLabel: "Store" })] }), _jsx(ProductForm, { product: product, onChange: setProduct, renderGroupSettings: isEdit && productId
|
|
149
149
|
? (p) => (_jsx(GroupSettingsPanel, { productId: productId, productSlug: p.slug ?? productId, groupId: p.groupId, isGroupParent: p.isGroupParent, groupParentSlug: p.groupParentSlug, groupChildSlugs: p.groupChildSlugs, groupTitle: p.groupTitle, isAuction: modeFromProduct(p) === "auction", storeProductsEndpoint: "/api/admin/products", onGroupChanged: () => productQuery.refetch() }))
|
|
150
|
-
: undefined, renderCategorySelector: ({ label, value, onChange, disabled }) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: label }), _jsx(InlineCreateSelect, { value: value || null, onChange: (v) => onChange(v ?? ""), loadOptions: loadCategoryOptions, placeholder: "Search categories\u2026", searchPlaceholder: "Type category name\u2026", noResultsText: "No categories found", ariaLabel: label, disabled: disabled, createLabel: "Category", renderCreateForm: ({ onCreated, onCancel }) => (_jsx(CategoryQuickCreateForm, { onSaved: (id, name) => onCreated({ value: id, label: name }), onCancel: onCancel })) })] })), renderBrandSelector: (args) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: args.label }), _jsx(InlineCreateSelect, { value: args.multi ? null : (args.value || null), onChange: (v) => args.onValueChange(v ?? ""), loadOptions: loadBrandOptions, placeholder: "Search brands\u2026", searchPlaceholder: "Type brand name\u2026", noResultsText: "No brands found", ariaLabel: args.label, disabled: args.disabled, createLabel: "Brand", renderCreateForm: ({ onCreated, onCancel }) => (_jsx(BrandQuickCreateForm, { onSaved: (id, name) => onCreated({ value: id, label: name }), onCancel: onCancel })) })] })) })] })] }), _jsxs("div", { className: "flex gap-3 lg:hidden", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !product.title || isSubmitting, children: isEdit ? "Save changes" : "Create product" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () =>
|
|
151
|
-
if (confirm("Delete this product? This cannot be undone.")) {
|
|
152
|
-
deleteMutation.mutate();
|
|
153
|
-
}
|
|
154
|
-
}, children: "Delete product" }))] })] }, "product-form"));
|
|
150
|
+
: undefined, renderCategorySelector: ({ label, value, onChange, disabled }) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: label }), _jsx(InlineCreateSelect, { value: value || null, onChange: (v) => onChange(v ?? ""), loadOptions: loadCategoryOptions, placeholder: "Search categories\u2026", searchPlaceholder: "Type category name\u2026", noResultsText: "No categories found", ariaLabel: label, disabled: disabled, createLabel: "Category", renderCreateForm: ({ onCreated, onCancel }) => (_jsx(CategoryQuickCreateForm, { onSaved: (id, name) => onCreated({ value: id, label: name }), onCancel: onCancel })) })] })), renderBrandSelector: (args) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: args.label }), _jsx(InlineCreateSelect, { value: args.multi ? null : (args.value || null), onChange: (v) => args.onValueChange(v ?? ""), loadOptions: loadBrandOptions, placeholder: "Search brands\u2026", searchPlaceholder: "Type brand name\u2026", noResultsText: "No brands found", ariaLabel: args.label, disabled: args.disabled, createLabel: "Brand", renderCreateForm: ({ onCreated, onCancel }) => (_jsx(BrandQuickCreateForm, { onSaved: (id, name) => onCreated({ value: id, label: name }), onCancel: onCancel })) })] })) })] })] }), _jsxs("div", { className: "flex gap-3 lg:hidden", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !product.title || isSubmitting, children: isEdit ? "Save changes" : "Create product" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => setDeleteOpen(true), children: "Delete product" }))] })] }, "product-form"));
|
|
155
151
|
if (embedded) {
|
|
156
152
|
return _jsx("div", { className: "overflow-y-auto p-4", children: formContent });
|
|
157
153
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { Plus
|
|
4
|
+
import { Plus } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
8
|
import { useBottomActions } from "../../layout";
|
|
9
|
-
import { BulkActionBar, Button, FilterChipGroup, Heading, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text, Toggle, useToast } from "../../../ui";
|
|
9
|
+
import { BulkActionBar, Button, FilterChipGroup, Heading, ListingFilterDrawer, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text, Toggle, useToast } from "../../../ui";
|
|
10
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
11
|
import { ADMIN_PRODUCT_STATUS_TABS, ADMIN_PRODUCT_LISTING_TYPE_TABS, } from "../constants/filter-tabs";
|
|
12
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
@@ -57,11 +57,6 @@ function buildBaseColumns() {
|
|
|
57
57
|
},
|
|
58
58
|
];
|
|
59
59
|
}
|
|
60
|
-
function ProductsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
61
|
-
if (!filterOpen)
|
|
62
|
-
return null;
|
|
63
|
-
return (_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 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Type", tabs: TYPE_OPTIONS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: 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})` : ""] }) })] })] }));
|
|
64
|
-
}
|
|
65
60
|
export function AdminProductsView({ children, actionHref, getRowHref, ...props }) {
|
|
66
61
|
const hasChildren = React.Children.count(children) > 0;
|
|
67
62
|
const [overrides, setOverrides] = useState({});
|
|
@@ -231,5 +226,5 @@ export function AdminProductsView({ children, actionHref, getRowHref, ...props }
|
|
|
231
226
|
separator: true,
|
|
232
227
|
onClick: () => openEditPanel(row.id),
|
|
233
228
|
},
|
|
234
|
-
] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No products found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }),
|
|
229
|
+
] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No products found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsxs(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Type", tabs: TYPE_OPTIONS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) })] }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Product" : "Edit Product", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminProductEditorView, { productId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
235
230
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
4
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { BulkActionBar, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, Modal, RowActionMenu, Button, useToast } from "../../../ui";
|
|
7
|
+
import { BulkActionBar, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, Modal, RowActionMenu, Button, useToast, ListingFilterDrawer } from "../../../ui";
|
|
9
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
9
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
10
|
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
|
|
@@ -26,11 +25,6 @@ const SORT_OPTIONS = [
|
|
|
26
25
|
];
|
|
27
26
|
const STATUS_OPTIONS = ADMIN_REVIEW_STATUS_TABS;
|
|
28
27
|
const RATING_OPTIONS = ADMIN_REVIEW_RATING_TABS;
|
|
29
|
-
function ReviewsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
30
|
-
if (!filterOpen)
|
|
31
|
-
return null;
|
|
32
|
-
return (_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 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Rating", tabs: RATING_OPTIONS, value: pendingFilters.rating ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, rating: 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})` : ""] }) })] })] }));
|
|
33
|
-
}
|
|
34
28
|
export function AdminReviewsView({ renderDetailView, children, ...props }) {
|
|
35
29
|
const hasChildren = React.Children.count(children) > 0;
|
|
36
30
|
const [view, setView] = useState("table");
|
|
@@ -177,5 +171,5 @@ export function AdminReviewsView({ renderDetailView, children, ...props }) {
|
|
|
177
171
|
onClick: () => handleViewReview(rr),
|
|
178
172
|
},
|
|
179
173
|
] }));
|
|
180
|
-
} })] }),
|
|
174
|
+
} })] }), _jsxs(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Rating", tabs: RATING_OPTIONS, value: pendingFilters.rating ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, rating: id })) })] })] }), _jsx(ViewReviewModal, { review: viewReview, isOpen: Boolean(viewReview), onClose: () => setViewReview(null) }), _jsx(Modal, { isOpen: replyOpen, onClose: () => { setReplyOpen(false); setReplyText(""); setReplyTarget(null); }, title: "Reply to review", children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Admin reply" }), _jsx("textarea", { value: replyText, onChange: (e) => setReplyText(e.target.value), rows: 4, placeholder: "Write a public reply to this review\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "secondary", onClick: () => { setReplyOpen(false); setReplyText(""); setReplyTarget(null); }, children: "Cancel" }), _jsx(Button, { onClick: () => replyMutation.mutate(), isLoading: replyMutation.isPending, disabled: !replyText.trim() || replyMutation.isPending, children: "Save reply" })] })] }) })] }));
|
|
181
175
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
5
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
-
import { FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text } from "../../../ui";
|
|
6
|
+
import { FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text, ListingFilterDrawer } from "../../../ui";
|
|
8
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
8
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
9
|
import { ADMIN_SCAMMER_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -20,11 +19,6 @@ const SORT_OPTIONS = [
|
|
|
20
19
|
{ value: "-views", label: "Most viewed" },
|
|
21
20
|
{ value: "-incidentCount", label: "Most incidents" },
|
|
22
21
|
];
|
|
23
|
-
function ScammersFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
24
|
-
if (!filterOpen)
|
|
25
|
-
return null;
|
|
26
|
-
return (_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 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-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: ADMIN_SCAMMER_STATUS_TABS, 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})` : ""] }) })] })] }));
|
|
27
|
-
}
|
|
28
22
|
function buildScammerColumns(setSelectedRow, setDrawerOpen) {
|
|
29
23
|
return [
|
|
30
24
|
{
|
|
@@ -142,7 +136,7 @@ export function AdminScammersView({ children, ...props }) {
|
|
|
142
136
|
setDrawerOpen(true);
|
|
143
137
|
},
|
|
144
138
|
},
|
|
145
|
-
] })), columns: buildScammerColumns(setSelectedRow, setDrawerOpen) })] }), _jsx(
|
|
139
|
+
] })), columns: buildScammerColumns(setSelectedRow, setDrawerOpen) })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_SCAMMER_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) })] }), _jsx(AdminScammerEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), scammerId: selectedRow?.id, displayNames: Array.isArray(selectedRow?._raw?.displayNames)
|
|
146
140
|
? selectedRow._raw.displayNames
|
|
147
141
|
: [selectedRow?.primary ?? ""], scamType: toStringValue(selectedRow?._raw?.scamType, ""), description: toStringValue(selectedRow?._raw?.description, ""), phones: Array.isArray(selectedRow?._raw?.phones)
|
|
148
142
|
? selectedRow._raw.phones
|