@mohasinac/appkit 2.7.58 → 2.7.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/_internal/server/features/scams/og.js +1 -1
  2. package/dist/_internal/shared/actions/bulk-helpers.d.ts +3 -0
  3. package/dist/_internal/shared/actions/bulk-helpers.js +19 -0
  4. package/dist/_internal/shared/config/schema.d.ts +4 -4
  5. package/dist/client.d.ts +3 -0
  6. package/dist/client.js +2 -0
  7. package/dist/configs/next.js +11 -0
  8. package/dist/features/account/components/AddressesIndexListing.js +3 -4
  9. package/dist/features/account/components/UserSidebar.js +14 -10
  10. package/dist/features/admin/components/AdminAllEventEntriesView.js +3 -9
  11. package/dist/features/admin/components/AdminBidsView.js +2 -8
  12. package/dist/features/admin/components/AdminBlogView.js +4 -9
  13. package/dist/features/admin/components/AdminBrandEditorView.js +14 -17
  14. package/dist/features/admin/components/AdminBrandsView.js +4 -9
  15. package/dist/features/admin/components/AdminBundlesView.js +39 -58
  16. package/dist/features/admin/components/AdminCarouselView.js +7 -20
  17. package/dist/features/admin/components/AdminCartsView.js +3 -4
  18. package/dist/features/admin/components/AdminCategoriesView.js +4 -9
  19. package/dist/features/admin/components/AdminCategoryEditorView.js +1 -5
  20. package/dist/features/admin/components/AdminContactView.js +6 -24
  21. package/dist/features/admin/components/AdminCouponEditorView.js +14 -17
  22. package/dist/features/admin/components/AdminCouponsView.js +8 -16
  23. package/dist/features/admin/components/AdminFaqEditorView.js +14 -17
  24. package/dist/features/admin/components/AdminFaqsView.js +18 -69
  25. package/dist/features/admin/components/AdminFeatureEditorView.js +7 -9
  26. package/dist/features/admin/components/AdminNewsletterView.js +4 -14
  27. package/dist/features/admin/components/AdminNotificationsView.js +2 -8
  28. package/dist/features/admin/components/AdminOrdersView.js +2 -8
  29. package/dist/features/admin/components/AdminPayoutsView.js +2 -8
  30. package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -9
  31. package/dist/features/admin/components/AdminProductEditorView.js +1 -5
  32. package/dist/features/admin/components/AdminProductsView.js +4 -9
  33. package/dist/features/admin/components/AdminReviewsView.js +2 -8
  34. package/dist/features/admin/components/AdminScammersView.js +2 -8
  35. package/dist/features/admin/components/AdminSessionsView.js +2 -8
  36. package/dist/features/admin/components/AdminStoresView.js +2 -8
  37. package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -6
  38. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +17 -20
  39. package/dist/features/admin/components/AdminSupportTicketsView.js +2 -8
  40. package/dist/features/admin/components/AdminTeamView.js +5 -14
  41. package/dist/features/admin/components/AdminUsersView.js +2 -8
  42. package/dist/features/admin/hooks/useAdminListing.d.ts +58 -0
  43. package/dist/features/admin/hooks/useAdminListing.js +71 -0
  44. package/dist/features/auth/schemas/firestore.d.ts +1 -0
  45. package/dist/features/blog/components/BlogIndexListing.js +3 -6
  46. package/dist/features/categories/components/CategoriesIndexListing.js +3 -6
  47. package/dist/features/events/components/AdminEventsView.js +4 -4
  48. package/dist/features/events/components/EventsIndexListing.js +3 -4
  49. package/dist/features/homepage/components/AnnouncementBar.d.ts +4 -1
  50. package/dist/features/homepage/components/AnnouncementBar.js +51 -4
  51. package/dist/features/homepage/components/MarketplaceHomepageView.d.ts +3 -1
  52. package/dist/features/homepage/components/MarketplaceHomepageView.js +2 -2
  53. package/dist/features/homepage/components/index.d.ts +1 -1
  54. package/dist/features/homepage/components/index.js +1 -1
  55. package/dist/features/homepage/lib/section-renderer.d.ts +2 -2
  56. package/dist/features/homepage/lib/section-renderer.js +2 -2
  57. package/dist/features/orders/repository/orders.repository.d.ts +0 -1
  58. package/dist/features/orders/repository/orders.repository.js +0 -1
  59. package/dist/features/orders/server.d.ts +1 -1
  60. package/dist/features/orders/server.js +1 -1
  61. package/dist/features/products/components/GroupSettingsPanel.js +64 -48
  62. package/dist/features/products/repository/products.repository.d.ts +0 -2
  63. package/dist/features/products/repository/products.repository.js +0 -2
  64. package/dist/features/products/server.d.ts +1 -1
  65. package/dist/features/products/server.js +1 -1
  66. package/dist/features/promotions/components/CouponCard.js +5 -5
  67. package/dist/features/promotions/components/CouponsIndexListing.js +3 -2
  68. package/dist/features/reviews/components/ReviewsIndexListing.js +3 -4
  69. package/dist/features/reviews/repository/reviews.repository.d.ts +0 -1
  70. package/dist/features/reviews/repository/reviews.repository.js +0 -1
  71. package/dist/features/reviews/server.d.ts +1 -1
  72. package/dist/features/reviews/server.js +1 -1
  73. package/dist/features/seller/components/SellerAddressesView.js +10 -20
  74. package/dist/features/seller/components/SellerAuctionsView.js +13 -22
  75. package/dist/features/seller/components/SellerBidsView.js +5 -5
  76. package/dist/features/seller/components/SellerBundlesView.js +11 -19
  77. package/dist/features/seller/components/SellerClassifiedView.js +11 -19
  78. package/dist/features/seller/components/SellerCouponsView.js +14 -20
  79. package/dist/features/seller/components/SellerDigitalCodesView.js +11 -19
  80. package/dist/features/seller/components/SellerGroupedListingsView.js +6 -10
  81. package/dist/features/seller/components/SellerLiveView.js +11 -19
  82. package/dist/features/seller/components/SellerOffersView.js +3 -4
  83. package/dist/features/seller/components/SellerOrdersView.js +8 -22
  84. package/dist/features/seller/components/SellerPayoutMethodsView.js +11 -23
  85. package/dist/features/seller/components/SellerPayoutsView.js +3 -4
  86. package/dist/features/seller/components/SellerPreOrdersView.js +13 -22
  87. package/dist/features/seller/components/SellerPrizeDrawsView.js +13 -22
  88. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +1 -1
  89. package/dist/features/seller/components/SellerProductsFilterDrawer.js +3 -6
  90. package/dist/features/seller/components/SellerProductsView.js +9 -22
  91. package/dist/features/seller/components/SellerShippingConfigsView.js +11 -23
  92. package/dist/features/seller/components/SellerStoreCategoriesView.js +14 -33
  93. package/dist/features/seller/components/SellerTemplatesView.js +25 -41
  94. package/dist/features/stores/components/StoreReviewsListing.js +3 -4
  95. package/dist/features/stores/components/StoresIndexListing.js +3 -4
  96. package/dist/index.d.ts +2 -6
  97. package/dist/index.js +3 -12
  98. package/dist/react/contexts/SessionContext.d.ts +2 -0
  99. package/dist/react/contexts/SessionContext.js +2 -0
  100. package/dist/react/hooks/useEntityDelete.d.ts +15 -0
  101. package/dist/react/hooks/useEntityDelete.js +39 -0
  102. package/dist/react/index.d.ts +2 -0
  103. package/dist/react/index.js +2 -0
  104. package/dist/repositories/index.d.ts +3 -3
  105. package/dist/repositories/index.js +3 -3
  106. package/dist/styles.css +1 -1
  107. package/dist/tailwind-utilities.css +1 -1
  108. package/dist/ui/components/ListingFilterDrawer.d.ts +10 -0
  109. package/dist/ui/components/ListingFilterDrawer.js +8 -0
  110. package/dist/ui/index.d.ts +2 -0
  111. package/dist/ui/index.js +1 -0
  112. package/package.json +1 -1
@@ -1,14 +1,15 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React, { useState, useCallback } from "react";
4
- import { X, Eye, Printer, MapPin, Truck } from "lucide-react";
4
+ import { Eye, Printer, MapPin, Truck } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
7
  import { useActionDispatch } from "../../../react/hooks/use-action-dispatch";
8
- import { BulkActionBar, Badge, Button, Div, FilterChipGroup, Heading, Input, ListingToolbar, Pagination, ListingViewShell, Select, SideDrawer, Stack, Text, useToast } from "../../../ui";
8
+ import { BulkActionBar, Badge, Button, Div, FilterChipGroup, Heading, Input, ListingFilterDrawer, ListingToolbar, Pagination, ListingViewShell, Select, SideDrawer, Stack, Text, useToast } from "../../../ui";
9
9
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
10
10
  import { SELLER_ORDER_STATUS_TABS } from "../../admin/constants/filter-tabs";
11
11
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
12
+ import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
12
13
  import { PhysicalLocationModal } from "./PhysicalLocationModal";
13
14
  import { ROUTES } from "../../../constants";
14
15
  import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
@@ -292,28 +293,13 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
292
293
  }
293
294
  }, [selection, showToast]);
294
295
  const bulkActions = [
295
- {
296
- id: ACTIONS.STORE["print-packing-slips"].id,
297
- label: ACTIONS.STORE["print-packing-slips"].label,
298
- icon: _jsx(Printer, { className: "w-4 h-4" }),
299
- onClick: handlePrintPackingSlips,
300
- },
301
- {
302
- id: ACTIONS.STORE["set-location"].id,
303
- label: ACTIONS.STORE["set-location"].label,
304
- icon: _jsx(MapPin, { className: "w-4 h-4" }),
305
- onClick: () => setSetLocationOpen(true),
306
- },
307
- {
308
- id: ACTIONS.STORE["request-payout"].id,
309
- label: ACTIONS.STORE["request-payout"].label,
310
- onClick: () => void requestPayoutForSelection(),
311
- variant: "primary",
312
- },
296
+ buildBulkAction(ACTIONS.STORE["print-packing-slips"], handlePrintPackingSlips, { icon: _jsx(Printer, { className: "w-4 h-4" }) }),
297
+ buildBulkAction(ACTIONS.STORE["set-location"], () => setSetLocationOpen(true), { icon: _jsx(MapPin, { className: "w-4 h-4" }) }),
298
+ buildBulkAction(ACTIONS.STORE["request-payout"], () => void requestPayoutForSelection(), { variant: "primary" }),
313
299
  ];
314
300
  if (hasChildren) {
315
301
  return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
316
302
  }
317
303
  useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
318
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by order ID or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedIds.length > 0 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+88px)] z-20 px-3 sm:px-4 py-2 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700", children: _jsx(BulkActionBar, { selectedCount: selection.selectedIds.length, onClearSelection: selection.clearSelection, actions: bulkActions }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-error-surface dark:border-red-900/60 px-4 py-3 text-sm text-error", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No orders yet", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: () => selection.toggleAll(), renderRowActions: renderRowActions })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 dark:text-zinc-400 hover:text-rose-500 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), selectedOrderId && (_jsx(OrderDetailDrawer, { orderId: selectedOrderId, apiBase: orderDetailApiBase, onClose: () => setSelectedOrderId(null) })), setLocationOpen && (_jsx(PhysicalLocationModal, { count: selection.selectedIds.length, onSave: handleSetLocation, onClose: () => setSetLocationOpen(false) }))] }));
304
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by order ID or buyer name", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedIds.length > 0 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+88px)] z-20 px-3 sm:px-4 py-2 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700", children: _jsx(BulkActionBar, { selectedCount: selection.selectedIds.length, onClearSelection: selection.clearSelection, actions: bulkActions }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl border border-red-200 bg-error-surface dark:border-red-900/60 px-4 py-3 text-sm text-error", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No orders yet", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: () => selection.toggleAll(), renderRowActions: renderRowActions })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), selectedOrderId && (_jsx(OrderDetailDrawer, { orderId: selectedOrderId, apiBase: orderDetailApiBase, onClose: () => setSelectedOrderId(null) })), setLocationOpen && (_jsx(PhysicalLocationModal, { count: selection.selectedIds.length, onSave: handleSetLocation, onClose: () => setSetLocationOpen(false) }))] }));
319
305
  }
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback } from "react";
4
+ import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
4
5
  import { Plus } from "lucide-react";
5
6
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
7
  import { Button, ConfirmDeleteModal, Div, ListingToolbar, Text, useToast, } from "../../../ui";
@@ -32,7 +33,13 @@ export function SellerPayoutMethodsView({ onCreateClick, onEditClick, onDelete,
32
33
  const table = useUrlTable({ defaults: { sort: DEFAULT_SORT } });
33
34
  const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
34
35
  const [deleteTargetId, setDeleteTargetId] = useState(null);
35
- const [deletingId, setDeletingId] = useState(null);
36
+ const { deletingId, handleDelete: performDelete } = useEntityDelete({
37
+ endpoint: SELLER_ENDPOINTS.PAYOUT_METHOD_BY_ID,
38
+ deleteFn: onDelete,
39
+ successMessage: "Payout method deleted.",
40
+ onSuccess: () => { refetch?.(); },
41
+ fetchOptions: { credentials: "include" },
42
+ });
36
43
  const [settingDefaultId, setSettingDefaultId] = useState(null);
37
44
  const { showToast } = useToast();
38
45
  const commitSearch = useCallback(() => {
@@ -61,28 +68,9 @@ export function SellerPayoutMethodsView({ onCreateClick, onEditClick, onDelete,
61
68
  })),
62
69
  });
63
70
  const handleDelete = useCallback(async (id) => {
64
- setDeletingId(id);
65
- try {
66
- if (onDelete) {
67
- await onDelete(id);
68
- }
69
- else {
70
- await fetch(SELLER_ENDPOINTS.PAYOUT_METHOD_BY_ID(id), {
71
- method: "DELETE",
72
- credentials: "include",
73
- });
74
- }
75
- refetch?.();
76
- showToast("Payout method deleted.", "success");
77
- }
78
- catch (err) {
79
- showToast(err instanceof Error ? err.message : "Failed to delete payout method.", "error");
80
- }
81
- finally {
82
- setDeletingId(null);
83
- setDeleteTargetId(null);
84
- }
85
- }, [onDelete, refetch, showToast]);
71
+ await performDelete(id);
72
+ setDeleteTargetId(null);
73
+ }, [performDelete]);
86
74
  const handleSetDefault = useCallback(async (id) => {
87
75
  setSettingDefaultId(id);
88
76
  try {
@@ -1,11 +1,10 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React, { useState, useCallback } from "react";
4
- import { X } from "lucide-react";
5
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
5
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
6
  import { AdminViewCards } from "../../admin/components/AdminViewCards";
8
- import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, RowActionMenu } from "../../../ui";
7
+ import { FilterChipGroup, ListingFilterDrawer, ListingToolbar, Pagination, ListingViewShell, RowActionMenu } from "../../../ui";
9
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
10
9
  import { ADMIN_PAYOUT_STATUS_TABS } from "../../admin/constants/filter-tabs";
11
10
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
@@ -83,5 +82,5 @@ export function SellerPayoutsView({ children, onViewClick, onExportClick, ...pro
83
82
  return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search payouts by payout # or amount", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No payouts found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
84
83
  ...(onViewClick ? [{ label: ACTIONS.STORE["view-payout"].label, onClick: () => onViewClick(row.id) }] : []),
85
84
  ...(onExportClick ? [{ label: ACTIONS.STORE["export-payout"].label, onClick: () => onExportClick(row.id) }] : []),
86
- ] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No payouts found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
85
+ ] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No payouts found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) })] }));
87
86
  }
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React, { useState, useCallback } from "react";
4
- import { X } from "lucide-react";
4
+ import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { Alert, Badge, Button, ConfirmDeleteModal, Div, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Row, RowActionMenu, Text, useToast } from "../../../ui";
7
+ import { Alert, Badge, ConfirmDeleteModal, Div, FilterChipGroup, ListingFilterDrawer, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text } from "../../../ui";
8
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
9
  import { SELLER_PRE_ORDER_STATUS_TABS } from "../../admin/constants/filter-tabs";
10
10
  import { ROUTES } from "../../../constants";
@@ -75,8 +75,12 @@ export function SellerPreOrdersView({ children, onDelete, ...props }) {
75
75
  const [view, setView] = useState("table");
76
76
  const dispatch = useActionDispatch();
77
77
  const [deleteTargetId, setDeleteTargetId] = useState(null);
78
- const [deletingId, setDeletingId] = useState(null);
79
- const { showToast } = useToast();
78
+ const { deletingId, handleDelete: performDelete } = useEntityDelete({
79
+ endpoint: (id) => `/api/store/products/${id}`,
80
+ deleteFn: onDelete,
81
+ successMessage: "Pre-order deleted.",
82
+ fetchOptions: { credentials: "include" },
83
+ });
80
84
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
81
85
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
82
86
  const [filterOpen, setFilterOpen] = useState(false);
@@ -139,22 +143,9 @@ export function SellerPreOrdersView({ children, onDelete, ...props }) {
139
143
  const totalPages = Math.ceil(total / PAGE_SIZE);
140
144
  const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
141
145
  const handleDelete = useCallback(async (id) => {
142
- setDeletingId(id);
143
- try {
144
- if (onDelete)
145
- await onDelete(id);
146
- else
147
- await fetch(`/api/store/products/${id}`, { method: "DELETE", credentials: "include" });
148
- showToast("Pre-order deleted.", "success");
149
- }
150
- catch (err) {
151
- showToast(err instanceof Error ? err.message : "Failed to delete pre-order.", "error");
152
- }
153
- finally {
154
- setDeletingId(null);
155
- setDeleteTargetId(null);
156
- }
157
- }, [onDelete, showToast]);
146
+ await performDelete(id);
147
+ setDeleteTargetId(null);
148
+ }, [performDelete]);
158
149
  if (hasChildren) {
159
150
  return (_jsx(ListingViewShell, { portal: "seller", ...props, children: children }));
160
151
  }
@@ -169,5 +160,5 @@ export function SellerPreOrdersView({ children, onDelete, ...props }) {
169
160
  onClick: () => setDeleteTargetId(row.id),
170
161
  disabled: deletingId === row.id,
171
162
  }] : []),
172
- ] })) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx(Div, { role: "presentation", className: "fixed inset-0 z-40 bg-black/40", 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(Row, { justify: "between", className: "border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx(Text, { className: "text-base font-semibold text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs(Row, { className: "gap-2", children: [activeFilterCount > 0 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: clearFilters, className: "text-xs text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-error)]", children: "Clear all" })), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setFilterOpen(false), "aria-label": "Close filters", 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, { variant: "primary", onClick: applyFilters, className: "w-full rounded-lg py-2.5 active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Pre-Order", message: "Are you sure you want to delete this pre-order listing? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
163
+ ] })) })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Pre-Order", message: "Are you sure you want to delete this pre-order listing? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
173
164
  }
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React, { useState, useCallback } from "react";
4
- import { X } from "lucide-react";
4
+ import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { Alert, Badge, Button, ConfirmDeleteModal, Div, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Row, RowActionMenu, Text, useToast } from "../../../ui";
7
+ import { Alert, Badge, ConfirmDeleteModal, Div, FilterChipGroup, ListingFilterDrawer, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text } from "../../../ui";
8
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
9
  import { SELLER_PRIZE_DRAW_STATUS_TABS } from "../../admin/constants/filter-tabs";
10
10
  import { ROUTES } from "../../../constants";
@@ -72,8 +72,12 @@ export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
72
72
  const [view, setView] = useState("table");
73
73
  const dispatch = useActionDispatch();
74
74
  const [deleteTargetId, setDeleteTargetId] = useState(null);
75
- const [deletingId, setDeletingId] = useState(null);
76
- const { showToast } = useToast();
75
+ const { deletingId, handleDelete: performDelete } = useEntityDelete({
76
+ endpoint: (id) => `/api/store/products/${id}`,
77
+ deleteFn: onDelete,
78
+ successMessage: "Prize draw deleted.",
79
+ fetchOptions: { credentials: "include" },
80
+ });
77
81
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
78
82
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
79
83
  const [filterOpen, setFilterOpen] = useState(false);
@@ -136,22 +140,9 @@ export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
136
140
  const totalPages = Math.ceil(total / PAGE_SIZE);
137
141
  const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
138
142
  const handleDelete = useCallback(async (id) => {
139
- setDeletingId(id);
140
- try {
141
- if (onDelete)
142
- await onDelete(id);
143
- else
144
- await fetch(`/api/store/products/${id}`, { method: "DELETE", credentials: "include" });
145
- showToast("Prize draw deleted.", "success");
146
- }
147
- catch (err) {
148
- showToast(err instanceof Error ? err.message : "Failed to delete prize draw.", "error");
149
- }
150
- finally {
151
- setDeletingId(null);
152
- setDeleteTargetId(null);
153
- }
154
- }, [onDelete, showToast]);
143
+ await performDelete(id);
144
+ setDeleteTargetId(null);
145
+ }, [performDelete]);
155
146
  if (hasChildren) {
156
147
  return (_jsx(ListingViewShell, { portal: "seller", ...props, children: children }));
157
148
  }
@@ -166,5 +157,5 @@ export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
166
157
  onClick: () => setDeleteTargetId(row.id),
167
158
  disabled: deletingId === row.id,
168
159
  }] : []),
169
- ] })) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx(Div, { role: "presentation", className: "fixed inset-0 z-40 bg-black/40", 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(Row, { justify: "between", className: "border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx(Text, { className: "text-base font-semibold text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs(Row, { className: "gap-2", children: [activeFilterCount > 0 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: clearFilters, className: "text-xs text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-error)]", children: "Clear all" })), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setFilterOpen(false), "aria-label": "Close filters", 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, { variant: "primary", onClick: applyFilters, className: "w-full rounded-lg py-2.5 active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Prize Draw", message: "Are you sure you want to delete this prize draw listing? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
160
+ ] })) })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Prize Draw", message: "Are you sure you want to delete this prize draw listing? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
170
161
  }
@@ -11,4 +11,4 @@ export interface SellerProductsFilterDrawerProps {
11
11
  onApply: () => void;
12
12
  onClose: () => void;
13
13
  }
14
- export declare function SellerProductsFilterDrawer({ isOpen, pendingFilters, statusOptions, activeFilterCount, onChange, onClear, onApply, onClose, }: SellerProductsFilterDrawerProps): import("react/jsx-runtime").JSX.Element | null;
14
+ export declare function SellerProductsFilterDrawer({ isOpen, pendingFilters, statusOptions, activeFilterCount, onChange, onClear, onApply, onClose, }: SellerProductsFilterDrawerProps): import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,6 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { X } from "lucide-react";
4
- import { Button, Div, FilterChipGroup, Row, Text } from "../../../ui";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Div, FilterChipGroup, ListingFilterDrawer, Row, Text } from "../../../ui";
5
4
  import { INPUT_CLS, FILTER_LABEL_CLS } from "./seller-products-styles";
6
5
  import { CategoryInlineSelect } from "./CategoryInlineSelect";
7
6
  import { BrandInlineSelect } from "./BrandInlineSelect";
@@ -15,8 +14,6 @@ const CONDITION_OPTIONS = [
15
14
  { value: "for-parts", label: "For parts" },
16
15
  ];
17
16
  export function SellerProductsFilterDrawer({ isOpen, pendingFilters, statusOptions, activeFilterCount, onChange, onClear, onApply, onClose, }) {
18
- if (!isOpen)
19
- return null;
20
17
  const patch = (k, v) => onChange({ ...pendingFilters, [k]: v });
21
- return (_jsxs(_Fragment, { children: [_jsx(Div, { role: "presentation", className: "fixed inset-0 z-40 bg-black/40", onClick: onClose }), _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(Row, { justify: "between", className: "border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx(Text, { className: "text-base font-semibold text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs(Row, { className: "gap-2", children: [activeFilterCount > 0 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: onClear, className: "text-xs text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-error)]", children: "Clear all" })), _jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, "aria-label": "Close filters", 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: statusOptions, value: pendingFilters.status ?? "", onChange: (id) => patch("status", id) }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Category" }), _jsx(CategoryInlineSelect, { value: pendingFilters.category ?? "", onChange: (v) => patch("category", v), placeholder: "Search categories\u2026" })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Brand" }), _jsx(BrandInlineSelect, { value: pendingFilters.brand ?? "", onChange: (v) => patch("brand", v), placeholder: "Search brands\u2026", allowCreate: false })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Condition" }), _jsx("select", { value: pendingFilters.condition ?? "", onChange: (e) => patch("condition", e.target.value), className: INPUT_CLS, children: CONDITION_OPTIONS.map((o) => (_jsx("option", { value: o.value, children: o.label }, o.value))) })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Price (\u20B9 Rupees)" }), _jsxs(Row, { className: "gap-2", children: [_jsx("input", { type: "number", min: 0, value: pendingFilters.minPrice ?? "", onChange: (e) => patch("minPrice", e.target.value), placeholder: "min", className: HALF_INPUT_CLS }), _jsx("input", { type: "number", min: 0, value: pendingFilters.maxPrice ?? "", onChange: (e) => patch("maxPrice", e.target.value), placeholder: "max", className: HALF_INPUT_CLS })] })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Tags (comma-separated)" }), _jsx("input", { type: "text", value: pendingFilters.tags ?? "", onChange: (e) => patch("tags", e.target.value), placeholder: "pokemon, vintage, holo", className: INPUT_CLS })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Badges (feature slugs)" }), _jsx("input", { type: "text", value: pendingFilters.badges ?? "", onChange: (e) => patch("badges", e.target.value), placeholder: "feature-free-shipping, feature-verified", className: INPUT_CLS })] })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)] px-4 py-3.5", children: _jsxs(Button, { variant: "primary", onClick: onApply, className: "w-full rounded-lg py-2.5 active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
18
+ return (_jsxs(ListingFilterDrawer, { open: isOpen, onClose: onClose, onApply: onApply, onClear: onClear, activeCount: activeFilterCount, children: [_jsx(FilterChipGroup, { label: "Status", tabs: statusOptions, value: pendingFilters.status ?? "", onChange: (id) => patch("status", id) }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Category" }), _jsx(CategoryInlineSelect, { value: pendingFilters.category ?? "", onChange: (v) => patch("category", v), placeholder: "Search categories\u2026" })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Brand" }), _jsx(BrandInlineSelect, { value: pendingFilters.brand ?? "", onChange: (v) => patch("brand", v), placeholder: "Search brands\u2026", allowCreate: false })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Condition" }), _jsx("select", { value: pendingFilters.condition ?? "", onChange: (e) => patch("condition", e.target.value), className: INPUT_CLS, children: CONDITION_OPTIONS.map((o) => (_jsx("option", { value: o.value, children: o.label }, o.value))) })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Price (\u20B9 Rupees)" }), _jsxs(Row, { className: "gap-2", children: [_jsx("input", { type: "number", min: 0, value: pendingFilters.minPrice ?? "", onChange: (e) => patch("minPrice", e.target.value), placeholder: "min", className: HALF_INPUT_CLS }), _jsx("input", { type: "number", min: 0, value: pendingFilters.maxPrice ?? "", onChange: (e) => patch("maxPrice", e.target.value), placeholder: "max", className: HALF_INPUT_CLS })] })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Tags (comma-separated)" }), _jsx("input", { type: "text", value: pendingFilters.tags ?? "", onChange: (e) => patch("tags", e.target.value), placeholder: "pokemon, vintage, holo", className: INPUT_CLS })] }), _jsxs(Div, { className: "space-y-1.5", children: [_jsx(Text, { className: FILTER_LABEL_CLS, children: "Badges (feature slugs)" }), _jsx("input", { type: "text", value: pendingFilters.badges ?? "", onChange: (e) => patch("badges", e.target.value), placeholder: "feature-free-shipping, feature-verified", className: INPUT_CLS })] })] }));
22
19
  }
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import React, { useState, useCallback } from "react";
4
+ import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
4
5
  import { useActionDispatch } from "../../../react/hooks/use-action-dispatch";
5
6
  import { Eye, EyeOff, Pencil, Trash2, Printer, MapPin } from "lucide-react";
6
7
  import { PhysicalLocationModal } from "./PhysicalLocationModal";
@@ -14,6 +15,7 @@ import { normalizeListingType } from "../../products/utils/listing-type";
14
15
  import { toRecordArray, toRelativeDate, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
15
16
  import { DataTable } from "../../admin/components/DataTable";
16
17
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
18
+ import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
17
19
  import { SellerProductsCards } from "./SellerProductsCards";
18
20
  import { SellerProductsFilterDrawer } from "./SellerProductsFilterDrawer";
19
21
  import { KIND_BADGE_VARIANT } from "./seller-products-styles";
@@ -111,7 +113,10 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, children, .
111
113
  const [filterOpen, setFilterOpen] = useState(false);
112
114
  // listingKind is URL-driven so it survives navigation and back/forward
113
115
  const listingKind = (table.get("listingType") || "all");
114
- const [deletingId, setDeletingId] = useState(null);
116
+ const { deletingId, handleDelete: performDelete } = useEntityDelete({
117
+ deleteFn: onDeleteProduct,
118
+ onSuccess: (id) => { setDeletedIds((prev) => new Set([...prev, id])); },
119
+ });
115
120
  const [deletedIds, setDeletedIds] = useState(new Set());
116
121
  const [publishingId, setPublishingId] = useState(null);
117
122
  const [statusOverrides, setStatusOverrides] = useState(new Map());
@@ -217,15 +222,7 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, children, .
217
222
  const handleDelete = async (row) => {
218
223
  if (!onDeleteProduct)
219
224
  return;
220
- setDeletingId(row.id);
221
- try {
222
- await onDeleteProduct(row.id);
223
- // Optimistically remove from list — avoids a full refetch
224
- setDeletedIds((prev) => new Set([...prev, row.id]));
225
- }
226
- finally {
227
- setDeletingId(null);
228
- }
225
+ await performDelete(row.id);
229
226
  };
230
227
  // S-STORE-2-C — Duplicate verb. Server-side endpoint is /api/store/products/[id]/duplicate.
231
228
  const handleDuplicate = async (row) => {
@@ -283,18 +280,8 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, children, .
283
280
  }
284
281
  }, [selection.selectedIds, showToast]);
285
282
  const bulkActions = [
286
- {
287
- id: ACTIONS.STORE["print-labels"].id,
288
- label: ACTIONS.STORE["print-labels"].label,
289
- icon: _jsx(Printer, { className: "w-4 h-4" }),
290
- onClick: handleBulkPrintLabels,
291
- },
292
- {
293
- id: ACTIONS.STORE["set-location"].id,
294
- label: ACTIONS.STORE["set-location"].label,
295
- icon: _jsx(MapPin, { className: "w-4 h-4" }),
296
- onClick: () => setSetLocationOpen(true),
297
- },
283
+ buildBulkAction(ACTIONS.STORE["print-labels"], handleBulkPrintLabels, { icon: _jsx(Printer, { className: "w-4 h-4" }) }),
284
+ buildBulkAction(ACTIONS.STORE["set-location"], () => setSetLocationOpen(true), { icon: _jsx(MapPin, { className: "w-4 h-4" }) }),
298
285
  ];
299
286
  useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
300
287
  return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search products by name\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, toggles: [
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback } from "react";
4
+ import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
4
5
  import { Plus } from "lucide-react";
5
6
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
7
  import { Button, ConfirmDeleteModal, DataTable, Div, ListingToolbar, RowActionMenu, Text, useToast, } from "../../../ui";
@@ -44,7 +45,13 @@ export function SellerShippingConfigsView({ onCreateClick, onEditClick, onDelete
44
45
  const table = useUrlTable({ defaults: { sort: DEFAULT_SORT } });
45
46
  const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
46
47
  const [deleteTargetId, setDeleteTargetId] = useState(null);
47
- const [deletingId, setDeletingId] = useState(null);
48
+ const { deletingId, handleDelete: performDelete } = useEntityDelete({
49
+ endpoint: SELLER_ENDPOINTS.SHIPPING_CONFIG_BY_ID,
50
+ deleteFn: onDelete,
51
+ successMessage: "Shipping config deleted.",
52
+ onSuccess: () => { refetch?.(); },
53
+ fetchOptions: { credentials: "include" },
54
+ });
48
55
  const [settingDefaultId, setSettingDefaultId] = useState(null);
49
56
  const { showToast } = useToast();
50
57
  const commitSearch = useCallback(() => {
@@ -73,28 +80,9 @@ export function SellerShippingConfigsView({ onCreateClick, onEditClick, onDelete
73
80
  })),
74
81
  });
75
82
  const handleDelete = useCallback(async (id) => {
76
- setDeletingId(id);
77
- try {
78
- if (onDelete) {
79
- await onDelete(id);
80
- }
81
- else {
82
- await fetch(SELLER_ENDPOINTS.SHIPPING_CONFIG_BY_ID(id), {
83
- method: "DELETE",
84
- credentials: "include",
85
- });
86
- }
87
- refetch?.();
88
- showToast("Shipping config deleted.", "success");
89
- }
90
- catch (err) {
91
- showToast(err instanceof Error ? err.message : "Failed to delete config.", "error");
92
- }
93
- finally {
94
- setDeletingId(null);
95
- setDeleteTargetId(null);
96
- }
97
- }, [onDelete, refetch, showToast]);
83
+ await performDelete(id);
84
+ setDeleteTargetId(null);
85
+ }, [performDelete]);
98
86
  const handleSetDefault = useCallback(async (id) => {
99
87
  setSettingDefaultId(id);
100
88
  try {
@@ -1,13 +1,15 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback } from "react";
4
+ import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
4
5
  import { Plus } from "lucide-react";
5
6
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
7
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { BulkActionBar, Button, ConfirmDeleteModal, DataTable, Div, ListingToolbar, Pagination, RowActionMenu, Text, useToast, } from "../../../ui";
8
+ import { BulkActionBar, Button, ConfirmDeleteModal, DataTable, Div, ListingToolbar, Pagination, RowActionMenu, Text, } from "../../../ui";
8
9
  import { useBottomActions } from "../../layout";
9
10
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
10
11
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
12
+ import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
11
13
  import { ROUTES } from "../../../next";
12
14
  import { toRecordArray, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
13
15
  import { TABLE_KEYS } from "../../../constants/table-keys";
@@ -42,8 +44,11 @@ export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete
42
44
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
43
45
  const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
44
46
  const [deleteTargetId, setDeleteTargetId] = useState(null);
45
- const [deletingId, setDeletingId] = useState(null);
46
- const { showToast } = useToast();
47
+ const { deletingId, handleDelete: performDelete } = useEntityDelete({
48
+ deleteFn: onDelete,
49
+ successMessage: "Category deleted.",
50
+ onSuccess: () => { refetch?.(); },
51
+ });
47
52
  const commitSearch = useCallback(() => { table.set(TABLE_KEYS.QUERY, searchInput.trim()); }, [searchInput, table]);
48
53
  const resetAll = useCallback(() => {
49
54
  table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
@@ -73,37 +78,13 @@ export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete
73
78
  const handleDelete = useCallback(async (id) => {
74
79
  if (!onDelete)
75
80
  return;
76
- setDeletingId(id);
77
- try {
78
- await onDelete(id);
79
- refetch?.();
80
- showToast("Category deleted.", "success");
81
- }
82
- catch (err) {
83
- showToast(err instanceof Error ? err.message : "Failed to delete category.", "error");
84
- }
85
- finally {
86
- setDeletingId(null);
87
- setDeleteTargetId(null);
88
- }
89
- }, [onDelete, refetch, showToast]);
81
+ await performDelete(id);
82
+ setDeleteTargetId(null);
83
+ }, [onDelete, performDelete]);
90
84
  const bulkActions = [
91
- ...(onBulkDelete ? [{
92
- id: "bulk-delete",
93
- label: ACTIONS.STORE["delete-listing"].label,
94
- variant: "danger",
95
- onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); refetch?.(); },
96
- }] : []),
97
- ...(onBulkActivate ? [{
98
- id: "bulk-activate",
99
- label: ACTIONS.ADMIN["activate-bundle"].label,
100
- onClick: async () => { await onBulkActivate(selection.selectedIds); selection.clearSelection(); refetch?.(); },
101
- }] : []),
102
- ...(onBulkDeactivate ? [{
103
- id: "bulk-deactivate",
104
- label: ACTIONS.ADMIN["deactivate-bundle"].label,
105
- onClick: async () => { await onBulkDeactivate(selection.selectedIds); selection.clearSelection(); refetch?.(); },
106
- }] : []),
85
+ ...(onBulkDelete ? [buildBulkAction(ACTIONS.STORE["delete-listing"], async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); refetch?.(); })] : []),
86
+ ...(onBulkActivate ? [buildBulkAction(ACTIONS.ADMIN["activate-bundle"], async () => { await onBulkActivate(selection.selectedIds); selection.clearSelection(); refetch?.(); })] : []),
87
+ ...(onBulkDeactivate ? [buildBulkAction(ACTIONS.ADMIN["deactivate-bundle"], async () => { await onBulkDeactivate(selection.selectedIds); selection.clearSelection(); refetch?.(); })] : []),
107
88
  ];
108
89
  const handleNavigateNew = () => {
109
90
  if (onCreateClick) {