@mohasinac/appkit 2.7.53 → 2.7.55
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/client/features/layout/DashboardLayoutClient.js +1 -1
- package/dist/_internal/server/jobs/core/auctionSettlement.js +6 -3
- package/dist/_internal/server/jobs/core/bundleStockSync.js +9 -7
- package/dist/_internal/server/jobs/core/onProductStockChange.d.ts +12 -3
- package/dist/_internal/server/jobs/core/onProductStockChange.js +12 -13
- package/dist/_internal/server/jobs/core/onProductWrite.js +1 -3
- package/dist/_internal/server/jobs/handlers/onProductStockChange.js +11 -2
- package/dist/_internal/server/jobs/runtime/adapters/firebase.js +5 -14
- package/dist/_internal/shared/actions/action-registry.js +827 -4
- package/dist/_internal/shared/features/events/schema.d.ts +4 -4
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/api-endpoints.d.ts +3 -0
- package/dist/constants/api-endpoints.js +1 -0
- package/dist/constants/field-names.d.ts +4 -3
- package/dist/constants/field-names.js +8 -8
- package/dist/errors/error-codes.d.ts +3 -0
- package/dist/errors/error-codes.js +3 -0
- package/dist/errors/messages.d.ts +3 -0
- package/dist/errors/messages.js +3 -0
- package/dist/features/account/components/UserOffersPanel.js +2 -1
- package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -3
- package/dist/features/admin/components/AdminBidsView.js +5 -3
- package/dist/features/admin/components/AdminBlogView.js +3 -2
- package/dist/features/admin/components/AdminBrandsView.js +2 -1
- package/dist/features/admin/components/AdminBundleEditorView.js +4 -5
- package/dist/features/admin/components/AdminBundlesView.js +3 -3
- package/dist/features/admin/components/AdminCarouselView.d.ts +2 -1
- package/dist/features/admin/components/AdminCarouselView.js +14 -5
- package/dist/features/admin/components/AdminCategoriesView.js +2 -1
- package/dist/features/admin/components/AdminContactView.d.ts +4 -1
- package/dist/features/admin/components/AdminContactView.js +27 -7
- package/dist/features/admin/components/AdminCouponsView.d.ts +3 -1
- package/dist/features/admin/components/AdminCouponsView.js +17 -3
- package/dist/features/admin/components/AdminFaqsView.d.ts +3 -1
- package/dist/features/admin/components/AdminFaqsView.js +18 -4
- package/dist/features/admin/components/AdminFeaturesView.js +2 -1
- package/dist/features/admin/components/AdminMediaView.js +3 -2
- package/dist/features/admin/components/AdminNavigationView.js +3 -2
- package/dist/features/admin/components/AdminNewsletterView.d.ts +2 -1
- package/dist/features/admin/components/AdminNewsletterView.js +13 -4
- package/dist/features/admin/components/AdminNotificationsView.js +6 -4
- package/dist/features/admin/components/AdminOrdersView.js +4 -3
- package/dist/features/admin/components/AdminPayoutsView.js +2 -2
- package/dist/features/admin/components/AdminPrizeDrawsView.js +2 -1
- package/dist/features/admin/components/AdminProductsView.js +3 -3
- package/dist/features/admin/components/AdminReviewsView.js +4 -3
- package/dist/features/admin/components/AdminScammersView.js +2 -1
- package/dist/features/admin/components/AdminSessionsView.js +5 -3
- package/dist/features/admin/components/AdminStoresView.js +2 -2
- package/dist/features/admin/components/AdminSublistingCategoriesView.d.ts +5 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +12 -3
- package/dist/features/admin/components/AdminSupportTicketsView.js +2 -1
- package/dist/features/admin/components/AdminTeamView.d.ts +2 -1
- package/dist/features/admin/components/AdminTeamView.js +13 -4
- package/dist/features/admin/components/AdminUsersView.js +2 -2
- package/dist/features/admin/types/product.types.d.ts +1 -1
- package/dist/features/admin/types/product.types.js +2 -3
- package/dist/features/auctions/actions/bid-actions.d.ts +9 -0
- package/dist/features/auctions/actions/bid-actions.js +61 -0
- package/dist/features/auctions/components/AuctionDetailPageView.d.ts +2 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +5 -4
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +3 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +32 -2
- package/dist/features/auctions/types/index.d.ts +1 -1
- package/dist/features/layout/AppLayoutShell.js +1 -1
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -11
- package/dist/features/products/api/[id]/route.js +4 -8
- package/dist/features/products/api/route.js +1 -3
- package/dist/features/products/components/AuctionsIndexListing.js +3 -11
- package/dist/features/products/components/PrizeDrawsIndexListing.js +2 -10
- package/dist/features/products/components/ProductFilters.d.ts +33 -9
- package/dist/features/products/components/ProductFilters.js +6 -29
- package/dist/features/products/components/ProductForm.js +2 -3
- package/dist/features/products/constants/action-defs.d.ts +86 -7
- package/dist/features/products/constants/action-defs.js +99 -7
- package/dist/features/products/constants/sieve.d.ts +162 -0
- package/dist/features/products/constants/sieve.js +95 -0
- package/dist/features/products/repository/products.repository.d.ts +5 -0
- package/dist/features/products/repository/products.repository.js +23 -3
- package/dist/features/products/schemas/firestore.d.ts +4 -4
- package/dist/features/products/schemas/firestore.js +2 -3
- package/dist/features/products/schemas/index.d.ts +6 -6
- package/dist/features/products/schemas/index.js +2 -6
- package/dist/features/products/types/index.d.ts +1 -1
- package/dist/features/seller/actions/offer-actions.js +2 -2
- package/dist/features/seller/components/QuickProductForm.d.ts +13 -0
- package/dist/features/seller/components/QuickProductForm.js +50 -0
- package/dist/features/seller/components/SellerAuctionsView.d.ts +4 -1
- package/dist/features/seller/components/SellerAuctionsView.js +37 -3
- package/dist/features/seller/components/SellerBidsView.js +2 -1
- package/dist/features/seller/components/SellerBundlesView.d.ts +3 -1
- package/dist/features/seller/components/SellerBundlesView.js +38 -5
- package/dist/features/seller/components/SellerClassifiedView.d.ts +3 -1
- package/dist/features/seller/components/SellerClassifiedView.js +37 -4
- package/dist/features/seller/components/SellerDigitalCodesView.d.ts +3 -1
- package/dist/features/seller/components/SellerDigitalCodesView.js +37 -4
- package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -1
- package/dist/features/seller/components/SellerGroupedListingsView.js +3 -2
- package/dist/features/seller/components/SellerLiveView.d.ts +3 -1
- package/dist/features/seller/components/SellerLiveView.js +37 -4
- package/dist/features/seller/components/SellerOffersPanel.js +2 -1
- package/dist/features/seller/components/SellerOffersView.d.ts +4 -1
- package/dist/features/seller/components/SellerOffersView.js +8 -3
- package/dist/features/seller/components/SellerPayoutsView.d.ts +3 -1
- package/dist/features/seller/components/SellerPayoutsView.js +7 -3
- package/dist/features/seller/components/SellerPreOrdersView.d.ts +2 -1
- package/dist/features/seller/components/SellerPreOrdersView.js +30 -7
- package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +2 -1
- package/dist/features/seller/components/SellerPrizeDrawsView.js +30 -7
- package/dist/features/seller/components/SellerProductShell.js +12 -0
- package/dist/features/seller/components/SellerReviewsView.js +2 -1
- package/dist/features/seller/components/SellerShippingConfigsView.js +3 -2
- package/dist/features/seller/components/SellerStoreCategoriesView.js +6 -5
- package/dist/features/seller/components/SellerTemplatesView.js +4 -3
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +30 -16
- package/dist/features/wishlist/types/index.d.ts +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -4
- package/dist/seed/addresses-seed-data.d.ts +2 -18
- package/dist/seed/addresses-seed-data.js +137 -203
- package/dist/seed/bids-seed-data.d.ts +2 -8
- package/dist/seed/bids-seed-data.js +172 -648
- package/dist/seed/blog-posts-seed-data.d.ts +0 -5
- package/dist/seed/blog-posts-seed-data.js +341 -596
- package/dist/seed/carousel-slides-seed-data.d.ts +0 -5
- package/dist/seed/carousel-slides-seed-data.js +61 -192
- package/dist/seed/carousels-seed-data.js +18 -1
- package/dist/seed/cart-seed-data.d.ts +1 -18
- package/dist/seed/cart-seed-data.js +116 -293
- package/dist/seed/categories-seed-data.d.ts +0 -6
- package/dist/seed/categories-seed-data.js +1409 -1537
- package/dist/seed/claimed-coupons-seed-data.d.ts +0 -11
- package/dist/seed/claimed-coupons-seed-data.js +32 -29
- package/dist/seed/conversations-seed-data.d.ts +0 -6
- package/dist/seed/conversations-seed-data.js +132 -280
- package/dist/seed/coupon-usage-seed-data.d.ts +0 -16
- package/dist/seed/coupon-usage-seed-data.js +30 -44
- package/dist/seed/coupons-seed-data.d.ts +0 -6
- package/dist/seed/coupons-seed-data.js +141 -489
- package/dist/seed/events-seed-data.d.ts +0 -6
- package/dist/seed/events-seed-data.js +201 -379
- package/dist/seed/faq-seed-data.d.ts +0 -14
- package/dist/seed/faq-seed-data.js +609 -1184
- package/dist/seed/grouped-listings-seed-data.d.ts +119 -8
- package/dist/seed/grouped-listings-seed-data.js +148 -233
- package/dist/seed/history-seed-data.d.ts +0 -7
- package/dist/seed/history-seed-data.js +50 -54
- package/dist/seed/homepage-sections-seed-data.d.ts +0 -5
- package/dist/seed/homepage-sections-seed-data.js +83 -175
- package/dist/seed/index.d.ts +1 -1
- package/dist/seed/index.js +1 -1
- package/dist/seed/manifest.js +2 -2
- package/dist/seed/notifications-seed-data.d.ts +0 -6
- package/dist/seed/notifications-seed-data.js +309 -196
- package/dist/seed/offers-seed-data.d.ts +0 -8
- package/dist/seed/offers-seed-data.js +118 -197
- package/dist/seed/orders-seed-data.d.ts +2 -8
- package/dist/seed/orders-seed-data.js +248 -1510
- package/dist/seed/payouts-seed-data.d.ts +0 -6
- package/dist/seed/payouts-seed-data.js +102 -441
- package/dist/seed/products-auctions-seed-data.d.ts +1 -14
- package/dist/seed/products-auctions-seed-data.js +612 -1084
- package/dist/seed/products-preorders-seed-data.d.ts +119 -14
- package/dist/seed/products-preorders-seed-data.js +154 -455
- package/dist/seed/products-standard-seed-data.d.ts +1 -15
- package/dist/seed/products-standard-seed-data.js +1355 -4128
- package/dist/seed/reviews-seed-data.d.ts +2 -8
- package/dist/seed/reviews-seed-data.js +99 -1182
- package/dist/seed/scammers-seed-data.d.ts +0 -15
- package/dist/seed/scammers-seed-data.js +36 -37
- package/dist/seed/sessions-seed-data.d.ts +0 -13
- package/dist/seed/sessions-seed-data.js +58 -335
- package/dist/seed/site-settings-seed-data.d.ts +0 -4
- package/dist/seed/site-settings-seed-data.js +61 -52
- package/dist/seed/store-addresses-seed-data.js +27 -151
- package/dist/seed/store-extensions-seed-data.d.ts +0 -7
- package/dist/seed/store-extensions-seed-data.js +147 -261
- package/dist/seed/stores-seed-data.d.ts +0 -5
- package/dist/seed/stores-seed-data.js +222 -184
- package/dist/seed/sub-listings-seed-data.d.ts +119 -0
- package/dist/seed/sub-listings-seed-data.js +447 -0
- package/dist/seed/support-tickets-seed-data.d.ts +0 -7
- package/dist/seed/support-tickets-seed-data.js +82 -75
- package/dist/seed/users-seed-data.d.ts +0 -5
- package/dist/seed/users-seed-data.js +279 -634
- package/dist/seed/wishlists-seed-data.d.ts +0 -9
- package/dist/seed/wishlists-seed-data.js +36 -52
- package/dist/seo/json-ld.js +6 -4
- package/dist/styles.css +8052 -3
- package/dist/tailwind-utilities.css +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/search-tokens.d.ts +2 -0
- package/dist/utils/search-tokens.js +21 -0
- package/package.json +2 -2
- package/scripts/seed-cli.mjs +2 -2
|
@@ -3,5 +3,7 @@ import type { AdminListingScaffoldRow } from "./AdminListingScaffold";
|
|
|
3
3
|
export interface AdminCouponsViewProps extends ListingViewShellProps {
|
|
4
4
|
actionHref?: string;
|
|
5
5
|
getRowHref?: (row: AdminListingScaffoldRow) => string;
|
|
6
|
+
onBulkArchive?: (ids: string[]) => Promise<void>;
|
|
7
|
+
onBulkDelete?: (ids: string[]) => Promise<void>;
|
|
6
8
|
}
|
|
7
|
-
export declare function AdminCouponsView({ children, getRowHref, ...props }: AdminCouponsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function AdminCouponsView({ children, getRowHref, onBulkArchive, onBulkDelete, ...props }: AdminCouponsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -5,8 +5,9 @@ import { Plus, X } from "lucide-react";
|
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
7
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
8
|
-
import { Button, Div, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, SideDrawer, Text, useToast } from "../../../ui";
|
|
8
|
+
import { BulkActionBar, Button, Div, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, SideDrawer, Text, useToast } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
11
|
import { ADMIN_COUPON_TYPE_TABS } from "../constants/filter-tabs";
|
|
11
12
|
import { apiClient } from "../../../http";
|
|
12
13
|
import { toRecordArray, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
@@ -26,7 +27,7 @@ function CouponsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, cle
|
|
|
26
27
|
return null;
|
|
27
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 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: "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})` : ""] }) })] })] }));
|
|
28
29
|
}
|
|
29
|
-
export function AdminCouponsView({ children, getRowHref, ...props }) {
|
|
30
|
+
export function AdminCouponsView({ children, getRowHref, onBulkArchive, onBulkDelete, ...props }) {
|
|
30
31
|
const hasChildren = React.Children.count(children) > 0;
|
|
31
32
|
const [view, setView] = useState("table");
|
|
32
33
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
@@ -102,8 +103,21 @@ export function AdminCouponsView({ children, getRowHref, ...props }) {
|
|
|
102
103
|
const currentPage = table.getNumber("page", 1);
|
|
103
104
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
104
105
|
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
106
|
+
const bulkActions = [
|
|
107
|
+
...(onBulkArchive ? [{
|
|
108
|
+
id: "bulk-archive",
|
|
109
|
+
label: "Archive",
|
|
110
|
+
onClick: async () => { await onBulkArchive(selection.selectedIds); selection.clearSelection(); },
|
|
111
|
+
}] : []),
|
|
112
|
+
...(onBulkDelete ? [{
|
|
113
|
+
id: "bulk-delete",
|
|
114
|
+
label: ACTIONS.ADMIN["delete-coupon"].label,
|
|
115
|
+
variant: "danger",
|
|
116
|
+
onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
|
|
117
|
+
}] : []),
|
|
118
|
+
];
|
|
105
119
|
if (hasChildren) {
|
|
106
120
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
107
121
|
}
|
|
108
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search codes, campaigns, or seller scopes", 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 Coupon"] }) }), 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 })), isLoading ? (_jsx(Div, { className: "fluid-grid-card gap-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Div, { className: "rounded-xl border-2 border-zinc-100 dark:border-slate-700 p-4 animate-pulse space-y-3", children: [_jsx(Div, { className: "h-6 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx(Div, { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx(Div, { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No coupons found" }) })) : (_jsx(Div, { className: "fluid-grid-card gap-3", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: openEditPanel, onToggleActive: handleToggle, onDelete: handleDelete }, row.id))) }))] }), _jsx(CouponsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Coupon" : "Edit Coupon", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminCouponEditorView, { couponId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
122
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search codes, campaigns, or seller scopes", 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 Coupon"] }) }), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), 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 })), isLoading ? (_jsx(Div, { className: "fluid-grid-card gap-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Div, { className: "rounded-xl border-2 border-zinc-100 dark:border-slate-700 p-4 animate-pulse space-y-3", children: [_jsx(Div, { className: "h-6 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx(Div, { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx(Div, { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No coupons found" }) })) : (_jsx(Div, { className: "fluid-grid-card gap-3", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: openEditPanel, onToggleActive: handleToggle, onDelete: handleDelete }, row.id))) }))] }), _jsx(CouponsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Coupon" : "Edit Coupon", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminCouponEditorView, { couponId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
109
123
|
}
|
|
@@ -3,5 +3,7 @@ import type { AdminListingScaffoldRow } from "./AdminListingScaffold";
|
|
|
3
3
|
export interface AdminFaqsViewProps extends ListingViewShellProps {
|
|
4
4
|
actionHref?: string;
|
|
5
5
|
getRowHref?: (row: AdminListingScaffoldRow) => string;
|
|
6
|
+
onBulkArchive?: (ids: string[]) => Promise<void>;
|
|
7
|
+
onBulkDelete?: (ids: string[]) => Promise<void>;
|
|
6
8
|
}
|
|
7
|
-
export declare function AdminFaqsView({ children, getRowHref, ...props }: AdminFaqsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function AdminFaqsView({ children, getRowHref, onBulkArchive, onBulkDelete, ...props }: AdminFaqsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -5,8 +5,9 @@ import { Plus, X } from "lucide-react";
|
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
7
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
8
|
-
import { Button, Heading, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text } from "../../../ui";
|
|
8
|
+
import { BulkActionBar, Button, Heading, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
11
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
11
12
|
import { DataTable } from "./DataTable";
|
|
12
13
|
import { AdminViewCards } from "./AdminViewCards";
|
|
@@ -24,7 +25,7 @@ function FaqsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearF
|
|
|
24
25
|
return null;
|
|
25
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 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(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})` : ""] }) })] })] }));
|
|
26
27
|
}
|
|
27
|
-
export function AdminFaqsView({ children, getRowHref, ...props }) {
|
|
28
|
+
export function AdminFaqsView({ children, getRowHref, onBulkArchive, onBulkDelete, ...props }) {
|
|
28
29
|
const hasChildren = React.Children.count(children) > 0;
|
|
29
30
|
const [view, setView] = useState("table");
|
|
30
31
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
@@ -60,7 +61,7 @@ export function AdminFaqsView({ children, getRowHref, ...props }) {
|
|
|
60
61
|
const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
|
|
61
62
|
const isActiveRaw = table.get("isActive");
|
|
62
63
|
const filters = isActiveRaw ? `isActive==${isActiveRaw}` : undefined;
|
|
63
|
-
const { rows, total, isLoading, errorMessage } = useAdminListingData({
|
|
64
|
+
const { rows, total, isLoading, errorMessage, refetch } = useAdminListingData({
|
|
64
65
|
queryKey: ["admin", "faqs", "listing"],
|
|
65
66
|
endpoint: ADMIN_ENDPOINTS.FAQS,
|
|
66
67
|
page: table.getNumber("page", 1),
|
|
@@ -80,8 +81,21 @@ export function AdminFaqsView({ children, getRowHref, ...props }) {
|
|
|
80
81
|
const currentPage = table.getNumber("page", 1);
|
|
81
82
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
82
83
|
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
84
|
+
const bulkActions = [
|
|
85
|
+
...(onBulkArchive ? [{
|
|
86
|
+
id: "bulk-archive",
|
|
87
|
+
label: ACTIONS.ADMIN["archive-faq"].label,
|
|
88
|
+
onClick: async () => { await onBulkArchive(selection.selectedIds); selection.clearSelection(); refetch?.(); },
|
|
89
|
+
}] : []),
|
|
90
|
+
...(onBulkDelete ? [{
|
|
91
|
+
id: "bulk-delete",
|
|
92
|
+
label: ACTIONS.ADMIN["delete-faq"].label,
|
|
93
|
+
variant: "danger",
|
|
94
|
+
onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); refetch?.(); },
|
|
95
|
+
}] : []),
|
|
96
|
+
];
|
|
83
97
|
if (hasChildren) {
|
|
84
98
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
85
99
|
}
|
|
86
|
-
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) }) })), _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 }))] }), _jsx(FaqsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _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 })) })] }));
|
|
100
|
+
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-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 }))] }), _jsx(FaqsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _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 })) })] }));
|
|
87
101
|
}
|
|
@@ -7,6 +7,7 @@ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
|
7
7
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
8
8
|
import { BulkActionBar, Button, Div, ListingToolbar, ListingViewShell, Pagination, Row, SideDrawer, Text, } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
11
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
11
12
|
import { DataTable } from "./DataTable";
|
|
12
13
|
import { AdminFeatureEditorView } from "./AdminFeatureEditorView";
|
|
@@ -92,6 +93,6 @@ export function AdminFeaturesView({ children, ...props }) {
|
|
|
92
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: () => {
|
|
93
94
|
table.set("scope", tab.value);
|
|
94
95
|
}, className: tabClass(tab.value), children: tab.label }, tab.value))) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
95
|
-
{ id: "delete", label: "
|
|
96
|
+
{ id: "delete", label: ACTIONS.ADMIN["delete-feature"].label, variant: "secondary", onClick: () => { selection.clearSelection(); } },
|
|
96
97
|
] }), 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 })) })] }));
|
|
97
98
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { Alert, Button, Div, Input, Select, StackedViewShell, Text, } from "../../../ui";
|
|
5
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
5
6
|
import { MediaUploadField, MediaUploadList, useMediaCleanup, useMediaUpload, } from "../../media";
|
|
6
7
|
const PREFIX_OPTIONS = [
|
|
7
8
|
{ value: "", label: "All" },
|
|
@@ -61,11 +62,11 @@ function MediaBrowser({ onCopy }) {
|
|
|
61
62
|
const isImage = (f.contentType ?? "").startsWith("image/");
|
|
62
63
|
return (_jsxs(Div, { className: "group rounded-lg border border-zinc-200 dark:border-slate-700 overflow-hidden bg-zinc-50 dark:bg-slate-800", children: [_jsx(Div, { className: "aspect-square flex items-center justify-center bg-zinc-100 dark:bg-slate-700 text-xs text-zinc-500 dark:text-zinc-400", children: isImage ? (
|
|
63
64
|
// eslint-disable-next-line @next/next/no-img-element
|
|
64
|
-
_jsx("img", { src: f.downloadURL, alt: f.name, className: "h-full w-full object-cover", loading: "lazy" })) : (_jsx(Text, { className: "px-2 text-center break-all", children: f.contentType ?? "file" })) }), _jsxs(Div, { className: "p-1.5", children: [_jsx(Text, { className: "truncate text-[10px] text-zinc-600 dark:text-zinc-400 font-mono", children: f.name.split("/").pop() }), _jsx(Button, { type: "button", variant: "ghost", className: "mt-1 w-full text-xs", onClick: () => onCopy(f.downloadURL), children: "
|
|
65
|
+
_jsx("img", { src: f.downloadURL, alt: f.name, className: "h-full w-full object-cover", loading: "lazy" })) : (_jsx(Text, { className: "px-2 text-center break-all", children: f.contentType ?? "file" })) }), _jsxs(Div, { className: "p-1.5", children: [_jsx(Text, { className: "truncate text-[10px] text-zinc-600 dark:text-zinc-400 font-mono", children: f.name.split("/").pop() }), _jsx(Button, { type: "button", variant: "ghost", className: "mt-1 w-full text-xs", onClick: () => onCopy(f.downloadURL), children: ACTIONS.MEDIA["copy-url"].label })] })] }, f.name));
|
|
65
66
|
}), filtered.length === 0 && !isLoading && (_jsx(Text, { className: "col-span-full py-6 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No files found." }))] }), _jsxs(Div, { className: "flex items-center justify-between", children: [_jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: [filtered.length, " file(s)", nextPageToken ? " · more available" : ""] }), nextPageToken && (_jsx(Button, { type: "button", variant: "outline", onClick: () => void load(nextPageToken), disabled: isLoading, children: isLoading ? "Loading…" : "Load more" }))] })] }));
|
|
66
67
|
}
|
|
67
68
|
function MediaUploaderPanel({ onUpload, isUploadPending, isCleanupPending, heroAssetUrl, setHeroAssetUrl, galleryAssets, setGalleryAssets, stagedUrls, setHeroStagedUrls, setGalleryStagedUrls, handleAbort, copiedUrl, onCopy, onClearPreviews, onDiscardStaged, }) {
|
|
68
|
-
return (_jsxs(Div, { className: "space-y-5 rounded-xl border border-zinc-200 bg-white p-4 dark:border-slate-700 dark:bg-slate-900", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: "Upload & Copy URL" }), _jsx(MediaUploadField, { label: "Primary media asset", value: heroAssetUrl, onChange: setHeroAssetUrl, onUpload: onUpload, helperText: "Uploads via signed URL (sign \u2192 PUT \u2192 finalize) and previews the returned URL.", onAbort: handleAbort, onStagedUrlsChange: setHeroStagedUrls }), heroAssetUrl && (_jsxs(Div, { className: "flex items-center gap-2 rounded-lg bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 px-3 py-2", children: [_jsx(Text, { className: "flex-1 truncate text-xs text-zinc-600 dark:text-zinc-400 font-mono", children: heroAssetUrl }), _jsx(Button, { type: "button", variant: "outline", onClick: () => onCopy(heroAssetUrl), children: copiedUrl === heroAssetUrl ? "Copied!" : "
|
|
69
|
+
return (_jsxs(Div, { className: "space-y-5 rounded-xl border border-zinc-200 bg-white p-4 dark:border-slate-700 dark:bg-slate-900", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: "Upload & Copy URL" }), _jsx(MediaUploadField, { label: "Primary media asset", value: heroAssetUrl, onChange: setHeroAssetUrl, onUpload: onUpload, helperText: "Uploads via signed URL (sign \u2192 PUT \u2192 finalize) and previews the returned URL.", onAbort: handleAbort, onStagedUrlsChange: setHeroStagedUrls }), heroAssetUrl && (_jsxs(Div, { className: "flex items-center gap-2 rounded-lg bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 px-3 py-2", children: [_jsx(Text, { className: "flex-1 truncate text-xs text-zinc-600 dark:text-zinc-400 font-mono", children: heroAssetUrl }), _jsx(Button, { type: "button", variant: "outline", onClick: () => onCopy(heroAssetUrl), children: copiedUrl === heroAssetUrl ? "Copied!" : ACTIONS.MEDIA["copy-url"].label })] })), _jsx(MediaUploadList, { label: "Gallery assets", value: galleryAssets, onChange: setGalleryAssets, onUpload: onUpload, helperText: "Attach multiple images or videos for batch checks.", onAbort: handleAbort, onStagedUrlsChange: setGalleryStagedUrls, maxItems: 12 }), galleryAssets.length > 0 && (_jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 font-medium", children: "Gallery URLs" }), galleryAssets.map((asset, i) => (_jsxs(Div, { className: "flex items-center gap-2 rounded bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 px-2 py-1", children: [_jsx(Text, { className: "flex-1 truncate text-xs text-zinc-600 dark:text-zinc-400 font-mono", children: asset.url }), _jsx(Button, { type: "button", variant: "ghost", onClick: () => onCopy(asset.url), children: copiedUrl === asset.url ? "Copied!" : "Copy" })] }, i)))] })), _jsxs(Div, { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { type: "button", variant: "outline", onClick: onClearPreviews, disabled: isUploadPending || isCleanupPending, children: ACTIONS.MEDIA["clear-previews"].label }), _jsx(Button, { type: "button", variant: "outline", onClick: onDiscardStaged, disabled: isUploadPending || isCleanupPending || stagedUrls.length === 0, children: isCleanupPending ? "Discarding…" : ACTIONS.MEDIA["discard-staged"].label }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: [stagedUrls.length, " staged upload(s)"] })] })] }));
|
|
69
70
|
}
|
|
70
71
|
export function AdminMediaView({ labels = {}, children, ...rest }) {
|
|
71
72
|
const hasChildren = React.Children.count(children) > 0;
|
|
@@ -6,10 +6,11 @@ import { Alert, Button, ConfirmDeleteModal, RowActionMenu, StackedViewShell, Tex
|
|
|
6
6
|
import { apiClient } from "../../../http";
|
|
7
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
8
|
import { AdminNavEditorView } from "./AdminNavEditorView";
|
|
9
|
+
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
|
|
9
10
|
function NavItemRow({ item, idx, total, reorderPending, onMoveUp, onMoveDown, onVisibilityChange, onEdit, onDelete, }) {
|
|
10
11
|
return (_jsxs("div", { className: "flex items-center gap-3 px-4 py-3 bg-white dark:bg-zinc-900", children: [_jsxs("div", { className: "flex flex-col gap-0.5 shrink-0", children: [_jsx("button", { type: "button", onClick: () => onMoveUp(idx), disabled: idx === 0 || reorderPending, className: "text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 disabled:opacity-30 leading-none text-xs", "aria-label": "Move up", children: "\u25B2" }), _jsx("button", { type: "button", onClick: () => onMoveDown(idx), disabled: idx >= total - 1 || reorderPending, className: "text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 disabled:opacity-30 leading-none text-xs", "aria-label": "Move down", children: "\u25BC" })] }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs(Text, { className: "text-sm font-medium text-zinc-800 dark:text-zinc-100 truncate", children: [item.parentId ? _jsx("span", { className: "text-zinc-400 mr-1", children: "\u21B3" }) : null, item.label] }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 truncate", children: item.href })] }), _jsx(Toggle, { checked: item.isVisible ?? true, onChange: (val) => onVisibilityChange(item.id, val), label: "" }), _jsx(RowActionMenu, { actions: [
|
|
11
|
-
{ label:
|
|
12
|
-
{ label:
|
|
12
|
+
{ label: ROW_ACTION_META[ROW_ACTION_ID.EDIT].label, onClick: () => onEdit(item) },
|
|
13
|
+
{ label: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label, destructive: true, onClick: () => onDelete(item) },
|
|
13
14
|
] })] }));
|
|
14
15
|
}
|
|
15
16
|
// --- Component ---------------------------------------------------------------
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ListingViewShellProps } from "../../../ui";
|
|
2
2
|
export interface AdminNewsletterViewProps extends ListingViewShellProps {
|
|
3
|
+
onBulkUnsubscribe?: (ids: string[]) => Promise<void>;
|
|
3
4
|
}
|
|
4
|
-
export declare function AdminNewsletterView({ children, ...props }: AdminNewsletterViewProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }: AdminNewsletterViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -5,8 +5,9 @@ import { X } from "lucide-react";
|
|
|
5
5
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { Button, ConfirmDeleteModal, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, useToast, } from "../../../ui";
|
|
8
|
+
import { BulkActionBar, Button, ConfirmDeleteModal, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, useToast, } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
11
|
import { ADMIN_NEWSLETTER_STATUS_TABS } from "../constants/filter-tabs";
|
|
11
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
12
13
|
import { DataTable } from "./DataTable";
|
|
@@ -24,7 +25,7 @@ function NewsletterFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount,
|
|
|
24
25
|
return null;
|
|
25
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 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})` : ""] }) })] })] }));
|
|
26
27
|
}
|
|
27
|
-
export function AdminNewsletterView({ children, ...props }) {
|
|
28
|
+
export function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }) {
|
|
28
29
|
const hasChildren = React.Children.count(children) > 0;
|
|
29
30
|
const [view, setView] = useState("table");
|
|
30
31
|
const queryClient = useQueryClient();
|
|
@@ -123,14 +124,22 @@ export function AdminNewsletterView({ children, ...props }) {
|
|
|
123
124
|
const currentPage = table.getNumber("page", 1);
|
|
124
125
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
125
126
|
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
127
|
+
const bulkActions = [
|
|
128
|
+
...(onBulkUnsubscribe ? [{
|
|
129
|
+
id: "bulk-unsubscribe",
|
|
130
|
+
label: ACTIONS.ADMIN["unsubscribe-newsletter"].label,
|
|
131
|
+
variant: "danger",
|
|
132
|
+
onClick: async () => { await onBulkUnsubscribe(selection.selectedIds); selection.clearSelection(); },
|
|
133
|
+
}] : []),
|
|
134
|
+
];
|
|
126
135
|
if (hasChildren) {
|
|
127
136
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
128
137
|
}
|
|
129
|
-
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: "
|
|
138
|
+
return (_jsxs(_Fragment, { children: [selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _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: ACTIONS.ADMIN["export-csv"].label }) }), 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) => {
|
|
130
139
|
const nr = row;
|
|
131
140
|
return (_jsx(RowActionMenu, { actions: [
|
|
132
141
|
{
|
|
133
|
-
label: "
|
|
142
|
+
label: ACTIONS.ADMIN["unsubscribe-newsletter"].label,
|
|
134
143
|
destructive: true,
|
|
135
144
|
disabled: nr.status === "unsubscribed",
|
|
136
145
|
onClick: () => { setSelectedRow(nr); setUnsubscribeOpen(true); },
|
|
@@ -7,6 +7,8 @@ import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
8
|
import { BulkActionBar, ConfirmDeleteModal, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text, useToast } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
|
+
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
|
|
10
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
11
13
|
import { DataTable } from "./DataTable";
|
|
12
14
|
import { apiClient } from "../../../http";
|
|
@@ -117,13 +119,13 @@ export function AdminNotificationsView({ children, ...props }) {
|
|
|
117
119
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
118
120
|
}
|
|
119
121
|
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 }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
120
|
-
{ id:
|
|
121
|
-
{ id:
|
|
122
|
+
{ id: ROW_ACTION_ID.MARK_READ, label: ACTIONS.ADMIN["mark-read"].label, variant: "primary", onClick: () => { selection.clearSelection(); } },
|
|
123
|
+
{ id: ROW_ACTION_ID.DELETE, label: ACTIONS.ADMIN["delete-notification"].label, variant: "secondary", onClick: () => { selection.clearSelection(); } },
|
|
122
124
|
] }), 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) => {
|
|
123
125
|
const nr = row;
|
|
124
126
|
return (_jsx(RowActionMenu, { actions: [
|
|
125
|
-
{ label: "
|
|
126
|
-
{ label:
|
|
127
|
+
{ label: ACTIONS.ADMIN["resend-notification"].label, onClick: () => resendMutation.mutate(nr.id) },
|
|
128
|
+
{ label: ROW_ACTION_META[ROW_ACTION_ID.DELETE].label, destructive: ROW_ACTION_META[ROW_ACTION_ID.DELETE].destructive, onClick: () => setDeleteTarget(nr) },
|
|
127
129
|
] }));
|
|
128
130
|
} })] }), _jsx(NotificationsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }), _jsx(ConfirmDeleteModal, { isOpen: Boolean(deleteTarget), onClose: () => setDeleteTarget(null), onConfirm: () => { if (deleteTarget)
|
|
129
131
|
deleteMutation.mutate(deleteTarget.id); }, isDeleting: deleteMutation.isPending, title: "Delete notification?", message: "This notification will be permanently removed.", confirmText: "Delete", variant: "danger" })] }));
|
|
@@ -9,6 +9,7 @@ import { apiClient } from "../../../http";
|
|
|
9
9
|
import { QuickEditMenu } from "./QuickEditMenu";
|
|
10
10
|
import { AdminViewCards } from "./AdminViewCards";
|
|
11
11
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
12
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
12
13
|
import { ADMIN_ORDER_STATUS_TABS } from "../constants/filter-tabs";
|
|
13
14
|
import { toRecordArray, toRelativeDate, toRupees, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
14
15
|
import { DataTable } from "./DataTable";
|
|
@@ -102,11 +103,11 @@ export function AdminOrdersView({ children, ...props }) {
|
|
|
102
103
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
103
104
|
}
|
|
104
105
|
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: [
|
|
105
|
-
{ id: "mark-shipped", label: "
|
|
106
|
+
{ id: "mark-shipped", label: ACTIONS.ADMIN["mark-shipped"].label, variant: "secondary", onClick: () => { for (const id of selection.selectedIds)
|
|
106
107
|
void handleQuickStatus(id, "SHIPPED"); selection.clearSelection(); } },
|
|
107
|
-
{ id: "mark-delivered", label: "
|
|
108
|
+
{ id: "mark-delivered", label: ACTIONS.ADMIN["mark-delivered"].label, variant: "primary", onClick: () => { for (const id of selection.selectedIds)
|
|
108
109
|
void handleQuickStatus(id, "DELIVERED"); selection.clearSelection(); } },
|
|
109
|
-
{ id: "mark-cancelled", label: "
|
|
110
|
+
{ id: "mark-cancelled", label: ACTIONS.ADMIN["cancel-order"].label, variant: "danger", onClick: () => { for (const id of selection.selectedIds)
|
|
110
111
|
void handleQuickStatus(id, "CANCELLED"); selection.clearSelection(); } },
|
|
111
112
|
] }), 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: [
|
|
112
113
|
{
|
|
@@ -130,8 +130,8 @@ export function AdminPayoutsView({ children, ...props }) {
|
|
|
130
130
|
if (hasChildren) {
|
|
131
131
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
132
132
|
}
|
|
133
|
-
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: "
|
|
134
|
-
{ id: "mark-paid", label: "
|
|
133
|
+
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: ACTIONS.ADMIN["export-csv"].label }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
134
|
+
{ id: "mark-paid", label: ACTIONS.ADMIN["mark-paid"].label, variant: "primary", onClick: () => { selection.clearSelection(); } },
|
|
135
135
|
] }), 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) => {
|
|
136
136
|
const pr = row;
|
|
137
137
|
return (_jsx(RowActionMenu, { actions: [
|
|
@@ -6,6 +6,7 @@ import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
7
|
import { Alert, Badge, BulkActionBar, Button, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Text } from "../../../ui";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
9
10
|
import { ADMIN_PRODUCT_STATUS_TABS } from "../constants/filter-tabs";
|
|
10
11
|
import { ROUTES } from "../../../constants";
|
|
11
12
|
import { toRecordArray, toRelativeDate, toRupees, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
@@ -139,6 +140,6 @@ export function AdminPrizeDrawsView({ children, ...props }) {
|
|
|
139
140
|
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) => {
|
|
140
141
|
table.set("sort", v);
|
|
141
142
|
}, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
142
|
-
{ id: "delete", label: "
|
|
143
|
+
{ id: "delete", label: ACTIONS.ADMIN["delete-prize-draw"].label, variant: "secondary", onClick: () => { selection.clearSelection(); } },
|
|
143
144
|
] }), 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(PrizeDrawsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }));
|
|
144
145
|
}
|
|
@@ -182,11 +182,11 @@ export function AdminProductsView({ children, actionHref, getRowHref, ...props }
|
|
|
182
182
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(Heading, { level: 1, className: "sr-only", children: "Products" }), _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, toggles: [
|
|
183
183
|
{ label: "Show sold", active: showSold, onChange: (next) => table.set("showSold", next ? "true" : "") },
|
|
184
184
|
], 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: [
|
|
185
|
-
{ id: "feature", label: "
|
|
185
|
+
{ id: "feature", label: ACTIONS.ADMIN["toggle-featured"].label, variant: "secondary", onClick: () => { for (const id of selection.selectedIds)
|
|
186
186
|
void handleToggle(id, "featured", !rows.find(r => r.id === id)?.featured); selection.clearSelection(); } },
|
|
187
|
-
{ id: "promote", label: "
|
|
187
|
+
{ id: "promote", label: ACTIONS.ADMIN["toggle-promoted"].label, variant: "secondary", onClick: () => { for (const id of selection.selectedIds)
|
|
188
188
|
void handleToggle(id, "isPromoted", !rows.find(r => r.id === id)?.isPromoted); selection.clearSelection(); } },
|
|
189
|
-
{ id: "sale", label: "
|
|
189
|
+
{ id: "sale", label: ACTIONS.ADMIN["toggle-on-sale"].label, variant: "secondary", onClick: () => { for (const id of selection.selectedIds)
|
|
190
190
|
void handleToggle(id, "isOnSale", !rows.find(r => r.id === id)?.isOnSale); selection.clearSelection(); } },
|
|
191
191
|
] }), 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: [
|
|
192
192
|
{
|
|
@@ -8,6 +8,7 @@ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
|
8
8
|
import { BulkActionBar, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, Modal, RowActionMenu, Button, useToast } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
|
+
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
|
|
11
12
|
import { ADMIN_REVIEW_STATUS_TABS, ADMIN_REVIEW_RATING_TABS } from "../constants/filter-tabs";
|
|
12
13
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
13
14
|
import { DataTable } from "./DataTable";
|
|
@@ -159,15 +160,15 @@ export function AdminReviewsView({ renderDetailView, children, ...props }) {
|
|
|
159
160
|
{ label: ACTIONS.ADMIN["approve-review"].label, onClick: () => patchMutation.mutate({ id: rr.id, payload: { status: "approved" } }) },
|
|
160
161
|
{ label: ACTIONS.ADMIN["reject-review"].label, destructive: true, onClick: () => patchMutation.mutate({ id: rr.id, payload: { status: "rejected" } }) },
|
|
161
162
|
{
|
|
162
|
-
label: rr.isFeatured ? "
|
|
163
|
+
label: rr.isFeatured ? ACTIONS.ADMIN["unfeature-review"].label : ACTIONS.ADMIN["feature-review"].label,
|
|
163
164
|
onClick: () => patchMutation.mutate({ id: rr.id, payload: { featured: !rr.isFeatured } }),
|
|
164
165
|
},
|
|
165
166
|
{
|
|
166
|
-
label:
|
|
167
|
+
label: ROW_ACTION_META[ROW_ACTION_ID.REPLY].label,
|
|
167
168
|
onClick: () => { setReplyTarget(rr); setReplyText(""); setReplyOpen(true); },
|
|
168
169
|
},
|
|
169
170
|
{
|
|
170
|
-
label:
|
|
171
|
+
label: ROW_ACTION_META[ROW_ACTION_ID.VIEW].label,
|
|
171
172
|
onClick: () => handleViewReview(rr),
|
|
172
173
|
},
|
|
173
174
|
] }));
|
|
@@ -6,6 +6,7 @@ import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
7
|
import { FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text } from "../../../ui";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
9
10
|
import { ADMIN_SCAMMER_STATUS_TABS } from "../constants/filter-tabs";
|
|
10
11
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
11
12
|
import { DataTable } from "./DataTable";
|
|
@@ -135,7 +136,7 @@ export function AdminScammersView({ children, ...props }) {
|
|
|
135
136
|
table.set("sort", v);
|
|
136
137
|
}, 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: [
|
|
137
138
|
{
|
|
138
|
-
label: "
|
|
139
|
+
label: ACTIONS.ADMIN["review-scammer"].label,
|
|
139
140
|
onClick: () => {
|
|
140
141
|
setSelectedRow(row);
|
|
141
142
|
setDrawerOpen(true);
|
|
@@ -7,6 +7,8 @@ import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
8
|
import { BulkActionBar, ConfirmDeleteModal, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text, useToast } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
|
+
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
|
|
10
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
11
13
|
import { DataTable } from "./DataTable";
|
|
12
14
|
import { apiClient } from "../../../http";
|
|
@@ -117,11 +119,11 @@ export function AdminSessionsView({ children, ...props }) {
|
|
|
117
119
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
118
120
|
}
|
|
119
121
|
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 }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
120
|
-
{ id:
|
|
122
|
+
{ id: ROW_ACTION_ID.REVOKE, label: ACTIONS.ADMIN["revoke-session"].label, variant: "secondary", onClick: () => { selection.clearSelection(); } },
|
|
121
123
|
] }), 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: [
|
|
122
124
|
{
|
|
123
|
-
label:
|
|
124
|
-
destructive:
|
|
125
|
+
label: ROW_ACTION_META[ROW_ACTION_ID.REVOKE].label,
|
|
126
|
+
destructive: ROW_ACTION_META[ROW_ACTION_ID.REVOKE].destructive,
|
|
125
127
|
onClick: () => setRevokeTarget(row),
|
|
126
128
|
},
|
|
127
129
|
] })) })] }), _jsx(SessionsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }), _jsx(ConfirmDeleteModal, { isOpen: Boolean(revokeTarget), onClose: () => setRevokeTarget(null), onConfirm: () => { if (revokeTarget)
|
|
@@ -112,7 +112,7 @@ export function AdminStoresView({ children, ...props }) {
|
|
|
112
112
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
113
113
|
}
|
|
114
114
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search stores, slugs, or owner 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 }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
115
|
-
{ id: "manage", label: "
|
|
115
|
+
{ id: "manage", label: ACTIONS.ADMIN["manage-store"].label, variant: "primary", onClick: () => { const id = selection.selectedIds[0]; if (id)
|
|
116
116
|
openEditPanel(id); selection.clearSelection(); } },
|
|
117
117
|
] }), 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 stores found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), renderRowActions: (row) => {
|
|
118
118
|
const sr = row;
|
|
@@ -120,7 +120,7 @@ export function AdminStoresView({ children, ...props }) {
|
|
|
120
120
|
const isVerified = Boolean(sr._raw?.isVerified);
|
|
121
121
|
return (_jsx(RowActionMenu, { actions: [
|
|
122
122
|
{
|
|
123
|
-
label: "
|
|
123
|
+
label: ACTIONS.ADMIN["manage-store"].label,
|
|
124
124
|
onClick: () => openEditPanel(sr.id),
|
|
125
125
|
},
|
|
126
126
|
{
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
interface AdminSublistingCategoriesViewProps {
|
|
2
|
+
onBulkDelete?: (ids: string[]) => Promise<void>;
|
|
3
|
+
}
|
|
4
|
+
export declare function AdminSublistingCategoriesView({ onBulkDelete }: AdminSublistingCategoriesViewProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export {};
|
|
@@ -3,8 +3,9 @@ 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
5
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
|
-
import { ListingToolbar, Pagination } from "../../../ui";
|
|
6
|
+
import { BulkActionBar, ListingToolbar, Pagination } from "../../../ui";
|
|
7
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
8
9
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
9
10
|
import { DataTable } from "./DataTable";
|
|
10
11
|
const PAGE_SIZE = 25;
|
|
@@ -15,7 +16,7 @@ const SORT_OPTIONS = [
|
|
|
15
16
|
{ value: "-productCount", label: "Most listings" },
|
|
16
17
|
{ value: "-createdAt", label: "Newest" },
|
|
17
18
|
];
|
|
18
|
-
export function AdminSublistingCategoriesView() {
|
|
19
|
+
export function AdminSublistingCategoriesView({ onBulkDelete }) {
|
|
19
20
|
const [view, setView] = useState("table");
|
|
20
21
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
21
22
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
@@ -51,7 +52,15 @@ export function AdminSublistingCategoriesView() {
|
|
|
51
52
|
const currentPage = table.getNumber("page", 1);
|
|
52
53
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
53
54
|
const selection = useBulkSelection({ items: rows ?? [], keyExtractor: (r) => r.id });
|
|
55
|
+
const bulkActions = [
|
|
56
|
+
...(onBulkDelete ? [{
|
|
57
|
+
id: "bulk-delete",
|
|
58
|
+
label: ACTIONS.ADMIN["delete-sublisting-category"].label,
|
|
59
|
+
variant: "danger",
|
|
60
|
+
onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
|
|
61
|
+
}] : []),
|
|
62
|
+
];
|
|
54
63
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { searchValue: searchInput, searchPlaceholder: "Search sub-listing categories\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
|
|
55
64
|
table.set("sort", v);
|
|
56
|
-
}, 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 sub-listing categories found" })] })] }));
|
|
65
|
+
}, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), 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 sub-listing categories found" })] })] }));
|
|
57
66
|
}
|
|
@@ -6,6 +6,7 @@ import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
7
|
import { FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text } from "../../../ui";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
|
+
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
|
|
9
10
|
import { ADMIN_SUPPORT_TICKET_STATUS_TABS, ADMIN_SUPPORT_TICKET_PRIORITY_TABS, } from "../constants/filter-tabs";
|
|
10
11
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
11
12
|
import { DataTable } from "./DataTable";
|
|
@@ -144,7 +145,7 @@ export function AdminSupportTicketsView({ children, ...props }) {
|
|
|
144
145
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by subject", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
|
|
145
146
|
table.set("sort", v);
|
|
146
147
|
}, 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 support tickets found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
147
|
-
{ label:
|
|
148
|
+
{ label: ROW_ACTION_META[ROW_ACTION_ID.VIEW].label, onClick: () => { setSelectedRow(row); setDrawerOpen(true); } },
|
|
148
149
|
] })), columns: buildTicketColumns(setSelectedRow, setDrawerOpen) })] }), _jsx(SupportTicketsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }), _jsx(AdminSupportTicketDetailView, { open: drawerOpen, onClose: () => setDrawerOpen(false), ticketId: selectedRow?.id, subject: selectedRow?.primary, userDisplayName: toStringValue(selectedRow?._raw?.userDisplayName, ""), category: toStringValue(selectedRow?._raw?.category, "general"), currentStatus: selectedRow?.status, currentPriority: toStringValue(selectedRow?._raw?.priority, "normal"), description: toStringValue(selectedRow?._raw?.description, ""), messages: Array.isArray(selectedRow?._raw?.messages)
|
|
149
150
|
? selectedRow._raw.messages
|
|
150
151
|
: [], internalNotes: toStringValue(selectedRow?._raw?.internalNotes, "") || undefined, orderId: toStringValue(selectedRow?._raw?.orderId, "") || undefined })] }));
|