@mohasinac/appkit 2.7.11 → 2.7.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/server/jobs/core/assignSpinPrize.js +8 -7
- package/dist/_internal/server/jobs/core/bundleStockSync.js +17 -20
- package/dist/_internal/server/jobs/core/countersReconcile.js +3 -2
- package/dist/_internal/server/jobs/core/onProductStockChange.js +18 -20
- package/dist/_internal/server/jobs/core/onProductWrite.js +3 -2
- package/dist/_internal/server/jobs/core/prizeRevealClose.js +7 -5
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +11 -6
- package/dist/_internal/server/jobs/core/prizeRevealOpen.js +13 -7
- package/dist/_internal/server/jobs/core/prizeRevealReminder.js +9 -4
- package/dist/_internal/server/jobs/core/triggerEventRaffle.js +3 -2
- package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +3 -6
- package/dist/_internal/shared/features/categories/bundle-copy.js +3 -6
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
- package/dist/_internal/shared/features/events/schema.d.ts +4 -4
- package/dist/_internal/shared/features/products/schema.d.ts +20 -20
- package/dist/_internal/shared/features/promotions/schema.d.ts +2 -2
- package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
- package/dist/constants/field-names.d.ts +824 -0
- package/dist/constants/field-names.js +929 -0
- package/dist/constants/sort.d.ts +7 -0
- package/dist/constants/sort.js +5 -0
- package/dist/constants/table-keys.d.ts +48 -0
- package/dist/constants/table-keys.js +46 -0
- package/dist/features/account/components/AddressesIndexListing.js +9 -6
- package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -1
- package/dist/features/admin/components/AdminBidsView.js +4 -1
- package/dist/features/admin/components/AdminBlogView.js +9 -2
- package/dist/features/admin/components/AdminBrandsView.js +9 -2
- package/dist/features/admin/components/AdminBundlesView.js +0 -1
- package/dist/features/admin/components/AdminCarouselView.js +4 -1
- package/dist/features/admin/components/AdminCartsView.js +4 -1
- package/dist/features/admin/components/AdminCategoriesView.js +9 -2
- package/dist/features/admin/components/AdminContactView.js +4 -1
- package/dist/features/admin/components/AdminCouponsView.js +4 -1
- package/dist/features/admin/components/AdminFaqsView.js +5 -1
- package/dist/features/admin/components/AdminFeaturesView.js +5 -2
- package/dist/features/admin/components/AdminHistoryView.js +5 -1
- package/dist/features/admin/components/AdminNewsletterView.js +4 -1
- package/dist/features/admin/components/AdminNotificationsView.js +4 -1
- package/dist/features/admin/components/AdminOrdersView.js +14 -12
- package/dist/features/admin/components/AdminPayoutsView.js +4 -1
- package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -1
- package/dist/features/admin/components/AdminProductsView.js +14 -3
- package/dist/features/admin/components/AdminReturnRequestsView.js +4 -1
- package/dist/features/admin/components/AdminReviewsView.js +4 -1
- package/dist/features/admin/components/AdminScammersView.js +4 -1
- package/dist/features/admin/components/AdminSessionsView.js +4 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +5 -1
- package/dist/features/admin/components/AdminStoresView.js +10 -3
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +4 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +4 -1
- package/dist/features/admin/components/AdminTeamView.js +4 -1
- package/dist/features/admin/components/AdminUsersView.js +9 -12
- package/dist/features/admin/components/AdminViewCards.d.ts +12 -0
- package/dist/features/admin/components/AdminViewCards.js +53 -0
- package/dist/features/admin/components/AdminWishlistsView.js +4 -1
- package/dist/features/auctions/repository/bid.repository.js +1 -6
- package/dist/features/auctions/schemas/index.d.ts +4 -4
- package/dist/features/blog/actions/blog-actions.d.ts +16 -16
- package/dist/features/blog/components/BlogFilters.d.ts +28 -28
- package/dist/features/blog/components/BlogFilters.js +18 -15
- package/dist/features/blog/components/BlogIndexListing.js +20 -16
- package/dist/features/blog/schemas/index.d.ts +10 -10
- package/dist/features/categories/components/BundleDetailView.js +0 -1
- package/dist/features/categories/components/CategoriesIndexListing.js +25 -21
- package/dist/features/categories/components/CategoryBundlesListing.js +2 -4
- package/dist/features/categories/schemas/firestore.d.ts +1 -1
- package/dist/features/categories/schemas/index.d.ts +2 -2
- package/dist/features/events/components/AdminEventsView.js +5 -1
- package/dist/features/events/components/EventFilters.d.ts +24 -24
- package/dist/features/events/components/EventFilters.js +16 -13
- package/dist/features/events/components/EventStatusBadge.js +1 -0
- package/dist/features/events/components/EventsIndexListing.js +31 -26
- package/dist/features/events/components/EventsListPageView.js +13 -10
- package/dist/features/events/schemas/firestore.d.ts +1 -0
- package/dist/features/events/schemas/firestore.js +1 -0
- package/dist/features/events/schemas/index.d.ts +1 -1
- package/dist/features/events/types/index.d.ts +1 -1
- package/dist/features/faq/actions/faq-actions.d.ts +10 -10
- package/dist/features/faq/schemas/index.d.ts +4 -4
- package/dist/features/homepage/components/FeaturedBundlesSection.js +1 -3
- package/dist/features/orders/components/OrderFilters.d.ts +20 -20
- package/dist/features/orders/components/OrderFilters.js +33 -30
- package/dist/features/orders/repository/orders.repository.js +1 -15
- package/dist/features/orders/schemas/index.d.ts +2 -2
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +35 -31
- package/dist/features/pre-orders/repository/preorders.repository.js +8 -4
- package/dist/features/pre-orders/schemas/index.d.ts +3 -3
- package/dist/features/products/components/AuctionsIndexListing.js +34 -30
- package/dist/features/products/components/BidHistory.d.ts +0 -1
- package/dist/features/products/components/BidHistory.js +1 -1
- package/dist/features/products/components/PrizeDrawsIndexListing.js +44 -40
- package/dist/features/products/components/ProductDetailPageView.js +1 -1
- package/dist/features/products/components/ProductFilters.d.ts +20 -20
- package/dist/features/products/components/ProductFilters.js +62 -59
- package/dist/features/products/components/ProductsIndexListing.js +28 -24
- package/dist/features/products/components/ProductsIndexPageView.js +18 -10
- package/dist/features/products/hooks/useProducts.js +2 -0
- package/dist/features/products/repository/products.repository.js +2 -22
- package/dist/features/products/schemas/index.d.ts +28 -28
- package/dist/features/products/schemas/product-features.validators.d.ts +12 -12
- package/dist/features/products/types/index.d.ts +2 -0
- package/dist/features/promotions/components/CouponsIndexListing.js +57 -32
- package/dist/features/promotions/schemas/index.d.ts +4 -4
- package/dist/features/reviews/components/ReviewDetailShell.js +6 -5
- package/dist/features/reviews/components/ReviewFilters.d.ts +16 -16
- package/dist/features/reviews/components/ReviewFilters.js +10 -7
- package/dist/features/reviews/components/ReviewsIndexListing.js +21 -17
- package/dist/features/reviews/components/ReviewsList.d.ts +5 -2
- package/dist/features/reviews/components/ReviewsList.js +11 -5
- package/dist/features/reviews/schemas/firestore.d.ts +1 -0
- package/dist/features/reviews/schemas/index.d.ts +8 -8
- package/dist/features/reviews/types/index.d.ts +1 -0
- package/dist/features/search/schemas/index.d.ts +2 -2
- package/dist/features/seller/components/SellerAuctionsView.js +5 -1
- package/dist/features/seller/components/SellerBidsView.js +4 -1
- package/dist/features/seller/components/SellerCouponsView.js +4 -1
- package/dist/features/seller/components/SellerOffersView.js +5 -1
- package/dist/features/seller/components/SellerOrdersView.js +4 -1
- package/dist/features/seller/components/SellerPayoutsView.js +5 -1
- package/dist/features/seller/components/SellerPreOrdersView.js +4 -1
- package/dist/features/seller/components/SellerPrizeDrawsView.js +4 -1
- package/dist/features/seller/components/SellerProductsView.js +4 -1
- package/dist/features/seller/schemas/index.d.ts +12 -12
- package/dist/features/stores/api/route.js +10 -1
- package/dist/features/stores/components/StoreReviewsListing.js +1 -1
- package/dist/features/stores/components/StoresIndexListing.js +24 -20
- package/dist/features/stores/repository/stores.repository.js +8 -5
- package/dist/features/stores/schemas/firestore.d.ts +1 -0
- package/dist/features/stores/schemas/index.d.ts +4 -4
- package/dist/index.d.ts +8 -1
- package/dist/index.js +12 -1
- package/dist/providers/firebase-client/auth.d.ts +7 -0
- package/dist/providers/firebase-client/auth.js +15 -8
- package/dist/react/hooks/useUrlTable.js +2 -1
- package/dist/seed/bids-seed-data.js +45 -44
- package/dist/seed/categories-seed-data.js +41 -40
- package/dist/seed/coupons-seed-data.js +41 -40
- package/dist/seed/products-auctions-seed-data.js +54 -53
- package/dist/seed/products-preorders-seed-data.js +25 -24
- package/dist/seed/products-prize-draws-seed-data.js +24 -23
- package/dist/seed/products-standard-seed-data.js +294 -293
- package/dist/seed/reviews-seed-data.js +61 -60
- package/dist/seed/scammers-seed-data.js +3 -2
- package/dist/seed/stores-seed-data.js +9 -8
- package/dist/seed/support-tickets-seed-data.js +13 -12
- package/dist/seed/users-seed-data.js +28 -27
- package/dist/tailwind-utilities.css +1 -1
- package/dist/utils/sieve-builder.d.ts +41 -0
- package/dist/utils/sieve-builder.js +66 -0
- package/package.json +2 -2
|
@@ -3,11 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { Plus, X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
7
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
7
8
|
import { Button, ListingToolbar, Pagination, ListingViewShell, SideDrawer } from "../../../ui";
|
|
8
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
10
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
10
11
|
import { DataTable } from "./DataTable";
|
|
12
|
+
import { AdminViewCards } from "./AdminViewCards";
|
|
11
13
|
import { AdminFaqEditorView } from "./AdminFaqEditorView";
|
|
12
14
|
const PAGE_SIZE = 25;
|
|
13
15
|
const FILTER_KEYS = ["isActive"];
|
|
@@ -19,6 +21,7 @@ const SORT_OPTIONS = [
|
|
|
19
21
|
];
|
|
20
22
|
export function AdminFaqsView({ children, getRowHref, ...props }) {
|
|
21
23
|
const hasChildren = React.Children.count(children) > 0;
|
|
24
|
+
const [view, setView] = useState("table");
|
|
22
25
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
23
26
|
const { openCreatePanel, openEditPanel, closePanel, isCreateOpen, isEditOpen, editId } = usePanelUrlSync();
|
|
24
27
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
@@ -71,8 +74,9 @@ export function AdminFaqsView({ children, getRowHref, ...props }) {
|
|
|
71
74
|
});
|
|
72
75
|
const currentPage = table.getNumber("page", 1);
|
|
73
76
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
77
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
74
78
|
if (hasChildren) {
|
|
75
79
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
76
80
|
}
|
|
77
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_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); },
|
|
81
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No 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 }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ label: "All", value: "" }, { label: "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})` : ""] }) })] })] })), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add FAQ" : "Edit FAQ", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminFaqEditorView, { faqId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
78
82
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import React from "react";
|
|
3
|
+
import React, { useState } from "react";
|
|
4
4
|
import { Plus } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
7
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
7
8
|
import { Button, Div, ListingToolbar, ListingViewShell, Pagination, Row, SideDrawer, Text, } from "../../../ui";
|
|
8
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
@@ -48,6 +49,7 @@ function mapFeatureRow(item, index) {
|
|
|
48
49
|
}
|
|
49
50
|
export function AdminFeaturesView({ children, ...props }) {
|
|
50
51
|
const hasChildren = React.Children.count(children) > 0;
|
|
52
|
+
const [view, setView] = useState("table");
|
|
51
53
|
const table = useUrlTable({
|
|
52
54
|
defaults: {
|
|
53
55
|
pageSize: String(PAGE_SIZE),
|
|
@@ -81,13 +83,14 @@ export function AdminFeaturesView({ children, ...props }) {
|
|
|
81
83
|
});
|
|
82
84
|
const currentPage = table.getNumber("page", 1);
|
|
83
85
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
86
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
84
87
|
if (hasChildren) {
|
|
85
88
|
return (_jsx(ListingViewShell, { portal: "admin", ...props, children: children }));
|
|
86
89
|
}
|
|
87
90
|
const tabClass = (value) => `${TAB_BASE_CLASS} ${scopeFilter === value ? TAB_ACTIVE_CLASS : TAB_INACTIVE_CLASS}`;
|
|
88
91
|
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { searchValue: searchInput, searchPlaceholder: "Search features by label", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
|
|
89
92
|
table.set("sort", v);
|
|
90
|
-
},
|
|
93
|
+
}, 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 Feature"] }) }), _jsx(Row, { className: STICKY_TABS_CLASS, gap: "xs", children: PRODUCT_FEATURE_SCOPE_TABS.map((tab) => (_jsx("button", { type: "button", onClick: () => {
|
|
91
94
|
table.set("scope", tab.value);
|
|
92
95
|
}, className: tabClass(tab.value), children: tab.label }, tab.value))) }), totalPages > 1 && (_jsx(Div, { className: PAGINATION_BAR_CLASS, 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(Text, { as: "div", className: ERROR_BANNER_CLASS, children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No features found", onRowClick: (row) => openEditPanel(row.id) })] }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Feature" : "Edit Feature", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminFeatureEditorView, { featureId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
93
96
|
}
|
|
@@ -6,10 +6,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
6
6
|
*/
|
|
7
7
|
import React, { useState, useCallback } from "react";
|
|
8
8
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
9
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
9
10
|
import { ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
|
|
10
11
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
12
13
|
import { DataTable } from "./DataTable";
|
|
14
|
+
import { AdminViewCards } from "./AdminViewCards";
|
|
13
15
|
const PAGE_SIZE = 25;
|
|
14
16
|
const DEFAULT_SORT = "-updatedAt";
|
|
15
17
|
const SORT_OPTIONS = [
|
|
@@ -18,6 +20,7 @@ const SORT_OPTIONS = [
|
|
|
18
20
|
];
|
|
19
21
|
export function AdminHistoryView({ children, ...props }) {
|
|
20
22
|
const hasChildren = React.Children.count(children) > 0;
|
|
23
|
+
const [view, setView] = useState("table");
|
|
21
24
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
22
25
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
23
26
|
const resetAll = useCallback(() => {
|
|
@@ -50,8 +53,9 @@ export function AdminHistoryView({ children, ...props }) {
|
|
|
50
53
|
});
|
|
51
54
|
const currentPage = table.getNumber("page", 1);
|
|
52
55
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
56
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
53
57
|
if (hasChildren) {
|
|
54
58
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
55
59
|
}
|
|
56
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search by user ID", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
60
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search by user ID", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No user history records found" })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No user history records found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] })] }));
|
|
57
61
|
}
|
|
@@ -4,6 +4,7 @@ import React, { useState, useCallback } from "react";
|
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
8
|
import { Button, ConfirmDeleteModal, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, useToast, } from "../../../ui";
|
|
8
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
10
|
import { ADMIN_NEWSLETTER_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -20,6 +21,7 @@ const SORT_OPTIONS = [
|
|
|
20
21
|
const STATUS_OPTIONS = ADMIN_NEWSLETTER_STATUS_TABS;
|
|
21
22
|
export function AdminNewsletterView({ children, ...props }) {
|
|
22
23
|
const hasChildren = React.Children.count(children) > 0;
|
|
24
|
+
const [view, setView] = useState("table");
|
|
23
25
|
const queryClient = useQueryClient();
|
|
24
26
|
const { showToast } = useToast();
|
|
25
27
|
const [unsubscribeOpen, setUnsubscribeOpen] = useState(false);
|
|
@@ -115,10 +117,11 @@ export function AdminNewsletterView({ children, ...props }) {
|
|
|
115
117
|
}, [showToast]);
|
|
116
118
|
const currentPage = table.getNumber("page", 1);
|
|
117
119
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
120
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
118
121
|
if (hasChildren) {
|
|
119
122
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
120
123
|
}
|
|
121
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by email or source", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
124
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by email or source", 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: _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleExportCsv, children: "Export CSV" }) }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No subscribers found", renderRowActions: (row) => {
|
|
122
125
|
const nr = row;
|
|
123
126
|
return (_jsx(RowActionMenu, { actions: [
|
|
124
127
|
{
|
|
@@ -4,6 +4,7 @@ import React, { useState, useCallback } from "react";
|
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
8
|
import { ListingToolbar, Pagination, ListingViewShell, RowActionMenu, ConfirmDeleteModal, useToast } from "../../../ui";
|
|
8
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
10
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
@@ -22,6 +23,7 @@ const NOTIF_TYPES = [
|
|
|
22
23
|
];
|
|
23
24
|
export function AdminNotificationsView({ children, ...props }) {
|
|
24
25
|
const hasChildren = React.Children.count(children) > 0;
|
|
26
|
+
const [view, setView] = useState("table");
|
|
25
27
|
const queryClient = useQueryClient();
|
|
26
28
|
const { showToast } = useToast();
|
|
27
29
|
const [deleteTarget, setDeleteTarget] = useState(null);
|
|
@@ -105,10 +107,11 @@ export function AdminNotificationsView({ children, ...props }) {
|
|
|
105
107
|
});
|
|
106
108
|
const currentPage = table.getNumber("page", 1);
|
|
107
109
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
110
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
108
111
|
if (hasChildren) {
|
|
109
112
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
110
113
|
}
|
|
111
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by title or user ID", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
114
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by title or user ID", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No notifications found", renderRowActions: (row) => {
|
|
112
115
|
const nr = row;
|
|
113
116
|
return (_jsx(RowActionMenu, { actions: [
|
|
114
117
|
{ label: "Resend", onClick: () => resendMutation.mutate(nr.id) },
|
|
@@ -3,9 +3,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
-
import {
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
+
import { BulkActionBar, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, useToast } from "../../../ui";
|
|
7
8
|
import { apiClient } from "../../../http";
|
|
8
9
|
import { QuickEditMenu } from "./QuickEditMenu";
|
|
10
|
+
import { AdminViewCards } from "./AdminViewCards";
|
|
9
11
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
12
|
import { ADMIN_ORDER_STATUS_TABS } from "../constants/filter-tabs";
|
|
11
13
|
import { toRecordArray, toRelativeDate, toRupees, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
@@ -22,15 +24,7 @@ const STATUS_OPTIONS = ADMIN_ORDER_STATUS_TABS;
|
|
|
22
24
|
export function AdminOrdersView({ children, ...props }) {
|
|
23
25
|
const hasChildren = React.Children.count(children) > 0;
|
|
24
26
|
const { showToast } = useToast();
|
|
25
|
-
const [
|
|
26
|
-
const toggleSelect = (id, next) => setSelectedIds((prev) => {
|
|
27
|
-
const s = new Set(prev);
|
|
28
|
-
if (next)
|
|
29
|
-
s.add(id);
|
|
30
|
-
else
|
|
31
|
-
s.delete(id);
|
|
32
|
-
return s;
|
|
33
|
-
});
|
|
27
|
+
const [view, setView] = useState("table");
|
|
34
28
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
35
29
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
36
30
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -98,10 +92,18 @@ export function AdminOrdersView({ children, ...props }) {
|
|
|
98
92
|
}, [showToast]);
|
|
99
93
|
const currentPage = table.getNumber("page", 1);
|
|
100
94
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
95
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
101
96
|
if (hasChildren) {
|
|
102
97
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
103
98
|
}
|
|
104
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search orders, buyers, or tracking IDs", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
99
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search orders, buyers, or tracking IDs", 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 }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
100
|
+
{ id: "mark-shipped", label: "Mark as Shipped", variant: "secondary", onClick: () => { for (const id of selection.selectedIds)
|
|
101
|
+
void handleQuickStatus(id, "SHIPPED"); selection.clearSelection(); } },
|
|
102
|
+
{ id: "mark-delivered", label: "Mark as Delivered", variant: "primary", onClick: () => { for (const id of selection.selectedIds)
|
|
103
|
+
void handleQuickStatus(id, "DELIVERED"); selection.clearSelection(); } },
|
|
104
|
+
{ id: "mark-cancelled", label: "Cancel Orders", variant: "danger", onClick: () => { for (const id of selection.selectedIds)
|
|
105
|
+
void handleQuickStatus(id, "CANCELLED"); selection.clearSelection(); } },
|
|
106
|
+
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No orders found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), renderRowActions: (row) => (_jsx(QuickEditMenu, { actions: [
|
|
105
107
|
{
|
|
106
108
|
label: "View full details",
|
|
107
109
|
onClick: () => { setSelectedRow(row); setDrawerOpen(true); },
|
|
@@ -118,5 +120,5 @@ export function AdminOrdersView({ children, ...props }) {
|
|
|
118
120
|
onSubmit: (vals) => handleQuickStatus(row.id, String(vals.status ?? "")),
|
|
119
121
|
submitLabel: "Update",
|
|
120
122
|
},
|
|
121
|
-
] })) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }), _jsx(AdminOrderEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), orderId: selectedRow?.id, orderLabel: selectedRow?.primary, currentStatus: selectedRow?.status })] }));
|
|
123
|
+
] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No orders found", onRowClick: (row) => { setSelectedRow(row); setDrawerOpen(true); }, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }), _jsx(AdminOrderEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), orderId: selectedRow?.id, orderLabel: selectedRow?.primary, currentStatus: selectedRow?.status })] }));
|
|
122
124
|
}
|
|
@@ -4,6 +4,7 @@ import React, { useState, useCallback } from "react";
|
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
8
|
import { Button, Form, FormActions, FilterChipGroup, Input, ListingToolbar, ListingViewShell, Modal, Pagination, RowActionMenu, useToast, } from "../../../ui";
|
|
8
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
10
|
import { ADMIN_PAYOUT_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -21,6 +22,7 @@ const SORT_OPTIONS = [
|
|
|
21
22
|
const STATUS_OPTIONS = ADMIN_PAYOUT_STATUS_TABS;
|
|
22
23
|
export function AdminPayoutsView({ children, ...props }) {
|
|
23
24
|
const hasChildren = React.Children.count(children) > 0;
|
|
25
|
+
const [view, setView] = useState("table");
|
|
24
26
|
const [markPaidOpen, setMarkPaidOpen] = useState(false);
|
|
25
27
|
const [selectedPayoutId, setSelectedPayoutId] = useState(null);
|
|
26
28
|
const [transactionId, setTransactionId] = useState("");
|
|
@@ -118,10 +120,11 @@ export function AdminPayoutsView({ children, ...props }) {
|
|
|
118
120
|
};
|
|
119
121
|
const currentPage = table.getNumber("page", 1);
|
|
120
122
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
123
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
121
124
|
if (hasChildren) {
|
|
122
125
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
123
126
|
}
|
|
124
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search stores, payout IDs, or order groups", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
127
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search stores, payout IDs, or order groups", 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: _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleExportCsv, children: "Export CSV" }) }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No payouts found", renderRowActions: (row) => {
|
|
125
128
|
const pr = row;
|
|
126
129
|
return (_jsx(RowActionMenu, { actions: [
|
|
127
130
|
{
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { Pencil, X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
7
|
import { Alert, Badge, Button, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, } from "../../../ui";
|
|
7
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { ADMIN_PRODUCT_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -65,6 +66,7 @@ const PRIZE_DRAW_COLUMNS = [
|
|
|
65
66
|
];
|
|
66
67
|
export function AdminPrizeDrawsView({ children, ...props }) {
|
|
67
68
|
const hasChildren = React.Children.count(children) > 0;
|
|
69
|
+
const [view, setView] = useState("table");
|
|
68
70
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
69
71
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
70
72
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -125,10 +127,11 @@ export function AdminPrizeDrawsView({ children, ...props }) {
|
|
|
125
127
|
});
|
|
126
128
|
const currentPage = table.getNumber("page", 1);
|
|
127
129
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
130
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
128
131
|
if (hasChildren) {
|
|
129
132
|
return (_jsx(ListingViewShell, { portal: "admin", ...props, children: children }));
|
|
130
133
|
}
|
|
131
134
|
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
135
|
table.set("sort", v);
|
|
133
|
-
},
|
|
136
|
+
}, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky z-10 flex justify-center bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-1.5", style: { top: "calc(var(--header-height, 0px) + 44px)" }, children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), _jsx(DataTable, { columns: PRIZE_DRAW_COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: "No prize draws 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})` : ""] }) })] })] }))] }));
|
|
134
137
|
}
|
|
@@ -4,11 +4,13 @@ import React, { useState, useCallback } from "react";
|
|
|
4
4
|
import { Plus, X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
7
|
-
import {
|
|
7
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
+
import { Button, BulkActionBar, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Toggle, useToast } from "../../../ui";
|
|
8
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
10
|
import { ADMIN_PRODUCT_STATUS_TABS, ADMIN_PRODUCT_LISTING_TYPE_TABS, } from "../constants/filter-tabs";
|
|
10
11
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
11
12
|
import { DataTable } from "./DataTable";
|
|
13
|
+
import { AdminViewCards } from "./AdminViewCards";
|
|
12
14
|
import { apiClient } from "../../../http";
|
|
13
15
|
import { AdminProductEditorView } from "./AdminProductEditorView";
|
|
14
16
|
import { QuickEditMenu } from "./QuickEditMenu";
|
|
@@ -57,6 +59,7 @@ export function AdminProductsView({ children, actionHref, getRowHref, ...props }
|
|
|
57
59
|
const hasChildren = React.Children.count(children) > 0;
|
|
58
60
|
const [overrides, setOverrides] = useState({});
|
|
59
61
|
const { showToast } = useToast();
|
|
62
|
+
const [view, setView] = useState("table");
|
|
60
63
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
61
64
|
const { openCreatePanel, openEditPanel, closePanel, isCreateOpen, isEditOpen, editId } = usePanelUrlSync();
|
|
62
65
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
@@ -129,6 +132,7 @@ export function AdminProductsView({ children, actionHref, getRowHref, ...props }
|
|
|
129
132
|
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
130
133
|
});
|
|
131
134
|
const rows = fetchedRows.map((r) => overrides[r.id] ? { ...r, ...overrides[r.id] } : r);
|
|
135
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
132
136
|
const handleToggle = async (id, field, value) => {
|
|
133
137
|
const prev = fetchedRows.find((r) => r.id === id)?.[field];
|
|
134
138
|
setOverrides((o) => ({ ...o, [id]: { ...o[id], [field]: value } }));
|
|
@@ -165,7 +169,14 @@ export function AdminProductsView({ children, actionHref, getRowHref, ...props }
|
|
|
165
169
|
className: "w-56",
|
|
166
170
|
render: (row) => (_jsx("div", { className: "flex flex-wrap gap-x-3 gap-y-1", onClick: (e) => e.stopPropagation(), onKeyDown: (e) => e.stopPropagation(), role: "presentation", children: FLAG_DEFS.map(({ key, label }) => (_jsx(Toggle, { size: "sm", label: label, checked: !!row[key], onChange: (v) => { void handleToggle(row.id, key, v); } }, key))) })),
|
|
167
171
|
};
|
|
168
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search products, SKUs, or seller names", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
172
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search products, SKUs, or seller names", 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 Product"] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
173
|
+
{ id: "feature", label: "Toggle Featured", variant: "secondary", onClick: () => { for (const id of selection.selectedIds)
|
|
174
|
+
void handleToggle(id, "featured", !rows.find(r => r.id === id)?.featured); selection.clearSelection(); } },
|
|
175
|
+
{ id: "promote", label: "Toggle Promoted", variant: "secondary", onClick: () => { for (const id of selection.selectedIds)
|
|
176
|
+
void handleToggle(id, "isPromoted", !rows.find(r => r.id === id)?.isPromoted); selection.clearSelection(); } },
|
|
177
|
+
{ id: "sale", label: "Toggle On Sale", variant: "secondary", onClick: () => { for (const id of selection.selectedIds)
|
|
178
|
+
void handleToggle(id, "isOnSale", !rows.find(r => r.id === id)?.isOnSale); selection.clearSelection(); } },
|
|
179
|
+
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, columns: [...buildBaseColumns(), flagColumn], isLoading: isLoading, emptyLabel: "No products found", onRowClick: (row) => openEditPanel(row.id), selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), renderRowActions: (row) => (_jsx(QuickEditMenu, { actions: [
|
|
169
180
|
{
|
|
170
181
|
label: "Quick edit",
|
|
171
182
|
formTitle: "Quick Edit Product",
|
|
@@ -184,5 +195,5 @@ export function AdminProductsView({ children, actionHref, getRowHref, ...props }
|
|
|
184
195
|
separator: true,
|
|
185
196
|
onClick: () => openEditPanel(row.id),
|
|
186
197
|
},
|
|
187
|
-
] })) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _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})` : ""] }) })] })] })), _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 })) })] }));
|
|
198
|
+
] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No products found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _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})` : ""] }) })] })] })), _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 })) })] }));
|
|
188
199
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { useState, useCallback } from "react";
|
|
4
4
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
7
|
import { ListingToolbar, Pagination, ConfirmDeleteModal, RowActionMenu, useToast } from "../../../ui";
|
|
7
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { toRecordArray, toRelativeDate, toRupees, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
@@ -22,6 +23,7 @@ export function AdminReturnRequestsView({ children: _children }) {
|
|
|
22
23
|
const [selectedRow, setSelectedRow] = useState(null);
|
|
23
24
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
24
25
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
26
|
+
const [view, setView] = useState("table");
|
|
25
27
|
const resetAll = useCallback(() => {
|
|
26
28
|
table.setMany({ q: "", sort: "" });
|
|
27
29
|
setSearchInput("");
|
|
@@ -82,7 +84,8 @@ export function AdminReturnRequestsView({ children: _children }) {
|
|
|
82
84
|
});
|
|
83
85
|
const currentPage = table.getNumber("page", 1);
|
|
84
86
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
85
|
-
|
|
87
|
+
const selection = useBulkSelection({ items: rows ?? [], keyExtractor: (r) => r.id });
|
|
88
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search by order ID or buyer", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No return requests", renderRowActions: (row) => {
|
|
86
89
|
const rr = row;
|
|
87
90
|
return (_jsx(RowActionMenu, { actions: [
|
|
88
91
|
{
|
|
@@ -4,6 +4,7 @@ import React, { useState, useCallback } from "react";
|
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
8
|
import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, Modal, RowActionMenu, Button, useToast } from "../../../ui";
|
|
8
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
10
|
import { ADMIN_REVIEW_STATUS_TABS, ADMIN_REVIEW_RATING_TABS } from "../constants/filter-tabs";
|
|
@@ -24,6 +25,7 @@ const STATUS_OPTIONS = ADMIN_REVIEW_STATUS_TABS;
|
|
|
24
25
|
const RATING_OPTIONS = ADMIN_REVIEW_RATING_TABS;
|
|
25
26
|
export function AdminReviewsView({ renderDetailView, children, ...props }) {
|
|
26
27
|
const hasChildren = React.Children.count(children) > 0;
|
|
28
|
+
const [view, setView] = useState("table");
|
|
27
29
|
const hasDetailView = Boolean(renderDetailView);
|
|
28
30
|
const queryClient = useQueryClient();
|
|
29
31
|
const { showToast } = useToast();
|
|
@@ -120,7 +122,8 @@ export function AdminReviewsView({ renderDetailView, children, ...props }) {
|
|
|
120
122
|
if (hasChildren || hasDetailView) {
|
|
121
123
|
return (_jsx(ListingViewShell, { portal: "admin", ...props, detailView: renderDetailView?.(), children: children }));
|
|
122
124
|
}
|
|
123
|
-
|
|
125
|
+
const selection = useBulkSelection({ items: rows ?? [], keyExtractor: (r) => r.id });
|
|
126
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews, products, or seller names", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No reviews found", renderRowActions: (row) => {
|
|
124
127
|
const rr = row;
|
|
125
128
|
return (_jsx(RowActionMenu, { actions: [
|
|
126
129
|
{ label: "Approve", onClick: () => patchMutation.mutate({ id: rr.id, payload: { status: "approved" } }) },
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
7
|
import { FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, } from "../../../ui";
|
|
7
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { ADMIN_SCAMMER_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -26,6 +27,7 @@ const STATUS_BADGE = {
|
|
|
26
27
|
};
|
|
27
28
|
export function AdminScammersView({ children, ...props }) {
|
|
28
29
|
const hasChildren = React.Children.count(children) > 0;
|
|
30
|
+
const [view, setView] = useState("table");
|
|
29
31
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
30
32
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
31
33
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -99,12 +101,13 @@ export function AdminScammersView({ children, ...props }) {
|
|
|
99
101
|
});
|
|
100
102
|
const currentPage = table.getNumber("page", 1);
|
|
101
103
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
104
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
102
105
|
if (hasChildren) {
|
|
103
106
|
return (_jsx(ListingViewShell, { portal: "admin", ...props, children: children }));
|
|
104
107
|
}
|
|
105
108
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by name, phone, UPI ID", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
|
|
106
109
|
table.set("sort", v);
|
|
107
|
-
},
|
|
110
|
+
}, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No scammer profiles found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
108
111
|
{
|
|
109
112
|
label: "Review",
|
|
110
113
|
onClick: () => {
|
|
@@ -4,6 +4,7 @@ import React, { useState, useCallback } from "react";
|
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
8
|
import { ListingToolbar, Pagination, ListingViewShell, RowActionMenu, ConfirmDeleteModal, useToast } from "../../../ui";
|
|
8
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
10
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
@@ -27,6 +28,7 @@ function maskIp(ip) {
|
|
|
27
28
|
}
|
|
28
29
|
export function AdminSessionsView({ children, ...props }) {
|
|
29
30
|
const hasChildren = React.Children.count(children) > 0;
|
|
31
|
+
const [view, setView] = useState("table");
|
|
30
32
|
const queryClient = useQueryClient();
|
|
31
33
|
const { showToast } = useToast();
|
|
32
34
|
const [revokeTarget, setRevokeTarget] = useState(null);
|
|
@@ -105,10 +107,11 @@ export function AdminSessionsView({ children, ...props }) {
|
|
|
105
107
|
});
|
|
106
108
|
const currentPage = table.getNumber("page", 1);
|
|
107
109
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
110
|
+
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
108
111
|
if (hasChildren) {
|
|
109
112
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
110
113
|
}
|
|
111
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by user or device", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); },
|
|
114
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by user or device", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No sessions found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
112
115
|
{
|
|
113
116
|
label: "Revoke",
|
|
114
117
|
destructive: true,
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback } from "react";
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
|
+
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
5
6
|
import { ListingToolbar, Pagination } from "../../../ui";
|
|
6
7
|
import { toRecordArray, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
7
8
|
import { DataTable } from "./DataTable";
|
|
9
|
+
import { AdminViewCards } from "./AdminViewCards";
|
|
8
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
11
|
const PAGE_SIZE = 25;
|
|
10
12
|
const DEFAULT_SORT = "storeId";
|
|
@@ -15,6 +17,7 @@ const SORT_OPTIONS = [
|
|
|
15
17
|
export function AdminStoreAddressesView({ children: _children }) {
|
|
16
18
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
17
19
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
20
|
+
const [view, setView] = useState("table");
|
|
18
21
|
const resetAll = useCallback(() => {
|
|
19
22
|
table.setMany({ q: "", sort: "" });
|
|
20
23
|
setSearchInput("");
|
|
@@ -48,5 +51,6 @@ export function AdminStoreAddressesView({ children: _children }) {
|
|
|
48
51
|
});
|
|
49
52
|
const currentPage = table.getNumber("page", 1);
|
|
50
53
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
51
|
-
|
|
54
|
+
const selection = useBulkSelection({ items: rows ?? [], keyExtractor: (r) => r.id });
|
|
55
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search by label, city, or store ID", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No store addresses found" })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No store addresses found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] })] }));
|
|
52
56
|
}
|