@mohasinac/appkit 4.30.5 → 4.30.7

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 (36) hide show
  1. package/dist/features/admin/components/AdminAddressesView.js +1 -1
  2. package/dist/features/admin/components/AdminAllEventEntriesView.js +1 -1
  3. package/dist/features/admin/components/AdminBidsView.js +1 -1
  4. package/dist/features/admin/components/AdminBlogView.js +1 -1
  5. package/dist/features/admin/components/AdminBrandsView.js +1 -1
  6. package/dist/features/admin/components/AdminCarouselView.js +3 -1
  7. package/dist/features/admin/components/AdminCartsView.js +1 -1
  8. package/dist/features/admin/components/AdminCategoriesView.js +1 -1
  9. package/dist/features/admin/components/AdminContactView.js +1 -1
  10. package/dist/features/admin/components/AdminCouponsView.js +1 -1
  11. package/dist/features/admin/components/AdminFeaturesView.js +1 -1
  12. package/dist/features/admin/components/AdminGroupedListingsView.js +1 -1
  13. package/dist/features/admin/components/AdminHistoryView.js +1 -1
  14. package/dist/features/admin/components/AdminNewsletterView.js +1 -1
  15. package/dist/features/admin/components/AdminOffersView.js +1 -1
  16. package/dist/features/admin/components/AdminOrdersView.js +1 -1
  17. package/dist/features/admin/components/AdminPaymentMethodsView.js +1 -1
  18. package/dist/features/admin/components/AdminReturnRequestsView.js +1 -1
  19. package/dist/features/admin/components/AdminSessionsView.js +1 -1
  20. package/dist/features/admin/components/AdminStoreAddressesView.js +1 -1
  21. package/dist/features/admin/components/AdminSublistingCategoriesView.js +1 -1
  22. package/dist/features/admin/components/AdminUsersView.js +11 -1
  23. package/dist/features/admin/components/AdminWishlistsView.js +1 -1
  24. package/dist/features/admin/components/DataListingView.d.ts +30 -1
  25. package/dist/features/admin/components/DataListingView.js +7 -1
  26. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
  27. package/dist/features/events/components/AdminEventsView.js +1 -1
  28. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  29. package/dist/features/seller/components/SellerGroupedListingsView.js +1 -1
  30. package/dist/features/seller/components/SellerOffersView.js +1 -1
  31. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  32. package/dist/features/seller/components/SellerPayoutsView.js +1 -1
  33. package/dist/features/seller/components/SellerStoreCategoriesView.js +1 -1
  34. package/dist/features/shipments/components/AdminShipmentsView.js +1 -1
  35. package/dist/features/tester/components/AdminTesterFeedbackListView.js +1 -1
  36. package/package.json +1 -1
@@ -51,7 +51,7 @@ function buildAddressConfig(onAction) {
51
51
  return {
52
52
  portal: "admin",
53
53
  title: "Address Management",
54
- searchPlaceholder: "Search by city or owner",
54
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
55
55
  emptyLabel: "No addresses found",
56
56
  // URL-synced via filterKeys (2026-08-21). The ban-status chips used to
57
57
  // live outside this config in local useState, so the selection was not in
@@ -32,7 +32,7 @@ export function AdminAllEventEntriesView({ children, ...props }) {
32
32
  const config = {
33
33
  portal: "admin",
34
34
  title: "Event Entries",
35
- searchPlaceholder: "Search by user name or event ID",
35
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
36
36
  emptyLabel: "No entries found",
37
37
  filterKeys: ["status"],
38
38
  defaultSort: sortBy("submittedAt", "DESC"),
@@ -41,7 +41,7 @@ export function AdminBidsView({ children, ...props }) {
41
41
  const config = {
42
42
  portal: "admin",
43
43
  title: "Bids",
44
- searchPlaceholder: "Search bids, products, or bidder IDs",
44
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
45
45
  emptyLabel: "No bids found",
46
46
  filterKeys: ["status"],
47
47
  defaultSort: sortBy("bidDate", "DESC"),
@@ -21,7 +21,7 @@ export function AdminBlogView({ children, ...props }) {
21
21
  const config = {
22
22
  portal: "admin",
23
23
  title: "Blog Posts",
24
- searchPlaceholder: "Search articles, authors, or tags",
24
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
25
25
  emptyLabel: "No blog posts found",
26
26
  filterKeys: ["status", "isFeatured"],
27
27
  defaultSort: sortBy("publishedAt", "DESC"),
@@ -33,7 +33,7 @@ const COLUMNS = [
33
33
  const ADMIN_BRANDS_CONFIG = {
34
34
  portal: "admin",
35
35
  title: "Brands",
36
- searchPlaceholder: "Search brands by name or slug",
36
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
37
37
  emptyLabel: "No brands found",
38
38
  filterKeys: ["isActive"],
39
39
  defaultSort: sortBy("order", "ASC"),
@@ -145,5 +145,7 @@ export function AdminCarouselView({ children, onBulkDelete, ...props }) {
145
145
  ] })),
146
146
  },
147
147
  ];
148
- return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search slide titles", onSearchChange: setSearchInput, onSearchCommit: commitSearch, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", 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, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), _jsx(DataTable, { rows: displayRows, columns: columns, isLoading: isLoading, emptyLabel: "No carousel slides \u2014 use 'New slide' to add one", rowHrefTemplate: "/admin/carousel/{id}/edit" })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Row, { wrap: true, gap: "sm", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx(Button, { variant: "ghost", type: "button", onClick: () => setPendingFilters((p) => ({ ...p, active: opt.value })), className: `rounded-full px-[var(--appkit-space-3)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium border transition-colors min-h-0 h-auto ${(pendingFilters.active || "") === opt.value ? "bg-primary text-white border-primary" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-surface-hover"}`, children: opt.label }, opt.label))) })] }) }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Carousel Slide", message: "Delete this carousel slide? This cannot be undone.", onConfirm: () => { deleteMutation.mutate(deleteTargetId); setDeleteTargetId(null); }, onClose: () => setDeleteTargetId(null), isDeleting: deleteMutation.isPending }))] }));
148
+ return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput,
149
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
150
+ onSearchChange: setSearchInput, onSearchCommit: commitSearch, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", 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, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), _jsx(DataTable, { rows: displayRows, columns: columns, isLoading: isLoading, emptyLabel: "No carousel slides \u2014 use 'New slide' to add one", rowHrefTemplate: "/admin/carousel/{id}/edit" })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Row, { wrap: true, gap: "sm", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx(Button, { variant: "ghost", type: "button", onClick: () => setPendingFilters((p) => ({ ...p, active: opt.value })), className: `rounded-full px-[var(--appkit-space-3)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium border transition-colors min-h-0 h-auto ${(pendingFilters.active || "") === opt.value ? "bg-primary text-white border-primary" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-surface-hover"}`, children: opt.label }, opt.label))) })] }) }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Carousel Slide", message: "Delete this carousel slide? This cannot be undone.", onConfirm: () => { deleteMutation.mutate(deleteTargetId); setDeleteTargetId(null); }, onClose: () => setDeleteTargetId(null), isDeleting: deleteMutation.isPending }))] }));
149
151
  }
@@ -32,7 +32,7 @@ function toCartItemEntries(raw) {
32
32
  const ADMIN_CARTS_CONFIG = {
33
33
  portal: "admin",
34
34
  title: "Carts",
35
- searchPlaceholder: "Search by user ID or session",
35
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
36
36
  emptyLabel: "No carts found",
37
37
  filterKeys: ["ownership"],
38
38
  defaultSort: sortBy("updatedAt", "DESC"),
@@ -32,7 +32,7 @@ const COLUMNS = [
32
32
  const ADMIN_CATEGORIES_CONFIG = {
33
33
  portal: "admin",
34
34
  title: "Categories",
35
- searchPlaceholder: "Search categories, slugs, or parent category",
35
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
36
36
  emptyLabel: "No categories found",
37
37
  filterKeys: ["isActive", "isFeatured"],
38
38
  defaultSort: sortBy("name", "ASC"),
@@ -49,7 +49,7 @@ export function AdminContactView({ children, onBulkMarkRead, onBulkArchive, onBu
49
49
  const config = {
50
50
  portal: "admin",
51
51
  title: "Contact Submissions",
52
- searchPlaceholder: "Search by subject, name, or email",
52
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
53
53
  emptyLabel: "No contact submissions found",
54
54
  filterKeys: ["status"],
55
55
  defaultSort: sortBy("createdAt", "DESC"),
@@ -52,7 +52,7 @@ export function AdminCouponsView({ children, onBulkArchive, onBulkDelete, ...pro
52
52
  const config = {
53
53
  portal: "admin",
54
54
  title: "Coupons",
55
- searchPlaceholder: "Search codes, campaigns, or seller scopes",
55
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
56
56
  emptyLabel: "No coupons found",
57
57
  filterKeys: ["type", "showExpired"],
58
58
  defaultSort: sortBy("createdAt", "DESC"),
@@ -41,7 +41,7 @@ export function AdminFeaturesView({ children, ...props }) {
41
41
  const config = {
42
42
  portal: "admin",
43
43
  title: "Features",
44
- searchPlaceholder: "Search features by label",
44
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
45
45
  emptyLabel: "No features found",
46
46
  filterKeys: [],
47
47
  defaultSort: sortBy("displayOrder", "ASC"),
@@ -52,7 +52,7 @@ export function AdminGroupedListingsView({ children, ...props }) {
52
52
  const config = React.useMemo(() => ({
53
53
  portal: "admin",
54
54
  title: "Grouped Listings",
55
- searchPlaceholder: "Search grouped listings by title or seller",
55
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
56
56
  emptyLabel: "No grouped listings",
57
57
  filterKeys: [],
58
58
  defaultSort: sortBy("createdAt", "DESC"),
@@ -15,7 +15,7 @@ import { DataListingView } from "./DataListingView";
15
15
  const ADMIN_HISTORY_CONFIG = {
16
16
  portal: "admin",
17
17
  title: "History",
18
- searchPlaceholder: "Search by user ID",
18
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
19
19
  emptyLabel: "No user history records found",
20
20
  filterKeys: [],
21
21
  defaultSort: sortBy("updatedAt", "DESC"),
@@ -87,7 +87,7 @@ export function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }) {
87
87
  const config = {
88
88
  portal: "admin",
89
89
  title: "Newsletter",
90
- searchPlaceholder: "Search by email or source",
90
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
91
91
  emptyLabel: "No subscribers found",
92
92
  filterKeys: ["status"],
93
93
  defaultSort: sortBy("subscribedAt", "DESC"),
@@ -103,7 +103,7 @@ export function AdminOffersView({ children, ...props }) {
103
103
  const config = {
104
104
  portal: "admin",
105
105
  title: "Offers",
106
- searchPlaceholder: "Search offers by product, buyer, or store",
106
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
107
107
  emptyLabel: "No offers found",
108
108
  filterKeys: ["status"],
109
109
  defaultSort: sortBy("createdAt", "DESC"),
@@ -85,7 +85,7 @@ export function AdminOrdersView({ children, ...props }) {
85
85
  const config = {
86
86
  portal: "admin",
87
87
  title: "Orders",
88
- searchPlaceholder: "Search orders, buyers, or tracking IDs",
88
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
89
89
  emptyLabel: "No orders found",
90
90
  filterKeys: ["status", "paymentReview"],
91
91
  defaultSort: sortBy("createdAt", "DESC"),
@@ -52,7 +52,7 @@ onOpen) {
52
52
  return {
53
53
  portal: "admin",
54
54
  title: "Payment Methods",
55
- searchPlaceholder: "Search by type or owner",
55
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
56
56
  emptyLabel: "No payment methods found",
57
57
  // URL-synced via filterKeys (2026-08-21) — see the matching note in
58
58
  // AdminAddressesView. banStatus is a query param, not a Sieve clause,
@@ -46,7 +46,7 @@ export function AdminReturnRequestsView(_props) {
46
46
  const config = {
47
47
  portal: "admin",
48
48
  title: "Return Requests",
49
- searchPlaceholder: "Search by order ID or buyer",
49
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
50
50
  emptyLabel: "No return requests",
51
51
  filterKeys: [],
52
52
  defaultSort: sortBy("createdAt", "DESC"),
@@ -41,7 +41,7 @@ export function AdminSessionsView({ children, ...props }) {
41
41
  const config = {
42
42
  portal: "admin",
43
43
  title: "Sessions",
44
- searchPlaceholder: "Search by user or device",
44
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
45
45
  emptyLabel: "No sessions found",
46
46
  filterKeys: ["isActive"],
47
47
  defaultSort: sortBy("lastActivity", "DESC"),
@@ -9,7 +9,7 @@ import { RecordDetailModal } from "../../../ui";
9
9
  const ADMIN_STORE_ADDRESSES_CONFIG = {
10
10
  portal: "admin",
11
11
  title: "Store Addresses",
12
- searchPlaceholder: "Search by label, city, or store ID",
12
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
13
13
  emptyLabel: "No store addresses found",
14
14
  filterKeys: [],
15
15
  defaultSort: sortBy("storeId", "ASC"),
@@ -11,7 +11,7 @@ export function AdminSublistingCategoriesView({ onBulkDelete, }) {
11
11
  const config = {
12
12
  portal: "admin",
13
13
  title: "Sub-listing Categories",
14
- searchPlaceholder: "Search sub-listing categories…",
14
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
15
15
  emptyLabel: "No sub-listing categories found",
16
16
  filterKeys: [],
17
17
  defaultSort: sortBy("name", "ASC"),
@@ -99,7 +99,17 @@ export function AdminUsersView({ children, ...props }) {
99
99
  const config = {
100
100
  portal: "admin",
101
101
  title: "Users",
102
- searchPlaceholder: "Search users, email, or seller handles",
102
+ // Exact match, and the copy has to say so. `/api/admin/users` branches on
103
+ // whether the term contains "@": an email resolves through
104
+ // `emailIndex == piiBlindIndex(q)` and a name through `displayName == q`.
105
+ // BOTH are equality. Email cannot be partial-matched at all — it is
106
+ // encrypted, and ciphertext has no usable prefix (D1) — and "seller
107
+ // handles" were never matched by anything.
108
+ search: {
109
+ placeholder: "Full name or email (exact match)",
110
+ mode: "exact",
111
+ fields: ["displayName", "email"],
112
+ },
103
113
  emptyLabel: "No users found",
104
114
  filterKeys: ["status", "role"],
105
115
  defaultSort: sortBy("createdAt", "DESC"),
@@ -11,7 +11,7 @@ import { DataListingView } from "./DataListingView";
11
11
  const ADMIN_WISHLISTS_CONFIG = {
12
12
  portal: "admin",
13
13
  title: "Wishlists",
14
- searchPlaceholder: "Search by user ID",
14
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
15
15
  emptyLabel: "No user wishlists found",
16
16
  filterKeys: [],
17
17
  defaultSort: sortBy("updatedAt", "DESC"),
@@ -78,7 +78,36 @@ export interface ListingViewConfig<TResponse, TRow extends {
78
78
  portal: "admin" | "seller" | "user";
79
79
  title: string;
80
80
  subtitle?: string;
81
- searchPlaceholder: string;
81
+ /**
82
+ * Search box copy. OPTIONAL — declaring it is what makes the box exist.
83
+ *
84
+ * It was REQUIRED, and `DataListingView` passed `onSearchChange`
85
+ * unconditionally, so a developer could not build a listing view without
86
+ * rendering a search box. `ListingToolbar` has always gated the box on
87
+ * `onSearchChange` being supplied; nothing ever declined to supply it.
88
+ *
89
+ * That is the structural cause of the dead boxes: ~39 views render a search
90
+ * input whose endpoint never reads `q`. The box accepts typing, the request
91
+ * carries the term, and the results do not change — no error anywhere.
92
+ *
93
+ * Omit it when the endpoint cannot search. Do not pass a placeholder
94
+ * describing a capability the endpoint lacks.
95
+ */
96
+ searchPlaceholder?: string;
97
+ /**
98
+ * Explicit search capability. Prefer this over a bare `searchPlaceholder`
99
+ * when the match semantics matter to the user — PII-backed fields are
100
+ * exact-match by construction (encryption and partial-match search are
101
+ * mutually exclusive), and a placeholder promising "search by email" over a
102
+ * blind index is a promise the backend cannot keep.
103
+ */
104
+ search?: {
105
+ placeholder: string;
106
+ /** `exact` for a field resolved through an HMAC blind index. */
107
+ mode?: "partial" | "exact";
108
+ /** Fields the endpoint actually matches on. */
109
+ fields?: readonly string[];
110
+ };
82
111
  emptyLabel?: string;
83
112
  sortOptions: ListingSortOption[];
84
113
  columns?: AdminTableColumn<TRow>[];
@@ -78,7 +78,13 @@ export function DataListingView({ config, }) {
78
78
  window.location.href = href;
79
79
  }
80
80
  : undefined;
81
- return (_jsxs(Div, { className: config.className ?? "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: config.searchPlaceholder, onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || config.defaultSort, sortOptions: config.sortOptions, onSortChange: (v) => table.set("sort", v), showTableView: !config.hideTableView, toggles: config.toggles, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: config.primaryAction || config.toolbarExtra ? (_jsxs(Row, { align: "center", gap: "sm", children: [config.toolbarExtra, config.primaryAction && (_jsxs(Button, { gap: "sm", size: "sm", onClick: () => config.primaryAction.onClick({
81
+ // A box exists only when the config asks for one.
82
+ const searchEnabled = Boolean(config.search ?? config.searchPlaceholder);
83
+ return (_jsxs(Div, { className: config.className ?? "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: config.search?.placeholder ?? config.searchPlaceholder,
84
+ // Passing this is what renders the box — ListingToolbar gates on it.
85
+ // Undefined when the config declares no search, so a view whose
86
+ // endpoint cannot search no longer shows an input that does nothing.
87
+ onSearchChange: searchEnabled ? setSearchInput : undefined, onSearchCommit: searchEnabled ? commitSearch : undefined, sortValue: table.get("sort") || config.defaultSort, sortOptions: config.sortOptions, onSortChange: (v) => table.set("sort", v), showTableView: !config.hideTableView, toggles: config.toggles, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: config.primaryAction || config.toolbarExtra ? (_jsxs(Row, { align: "center", gap: "sm", children: [config.toolbarExtra, config.primaryAction && (_jsxs(Button, { gap: "sm", size: "sm", onClick: () => config.primaryAction.onClick({
82
88
  openCreatePanel: panel.openCreatePanel,
83
89
  }), children: [config.primaryAction.icon ?? _jsx(Plus, { className: "h-4 w-4" }), config.primaryAction.label] }))] })) : undefined }), config.renderAboveContent?.(), bulkActionItems && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActionItems })), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p), pageSize: pageSize, onPageSizeChange: setPageSize, paginationConfig: { showPageSizeSelector: true, pageSizeOptions: [10, 25, 50, 100] } }) }) })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), view === "table" && !config.hideTableView && !isMobile ? (_jsx(DataTable, { columns: config.columns, rows: rows, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel: config.emptyLabel ?? `No ${config.title.toLowerCase()} found`, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next
84
90
  ? selection.setSelectedIds(rows.map((r) => r.id))
@@ -41,7 +41,7 @@ export function AdminCatalogueApprovalsView() {
41
41
  const config = {
42
42
  portal: "admin",
43
43
  title: "Catalogue Approvals",
44
- searchPlaceholder: "Search by title",
44
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
45
45
  emptyLabel: "No pending catalogue requests",
46
46
  filterKeys: [],
47
47
  defaultSort: sortBy("createdAt", "DESC"),
@@ -33,7 +33,7 @@ export function AdminEventsView({ children, ...props }) {
33
33
  const config = {
34
34
  portal: "admin",
35
35
  title: "Events",
36
- searchPlaceholder: "Search events by title or type",
36
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
37
37
  emptyLabel: "No events found",
38
38
  filterKeys: ["status", "type"],
39
39
  defaultSort: sortBy("startsAt", "DESC"),
@@ -49,7 +49,7 @@ export function SellerCouponsView({ onCreateClick, onEditClick, onToggle, onDele
49
49
  const config = {
50
50
  portal: "seller",
51
51
  title: "Coupons",
52
- searchPlaceholder: "Search by coupon code",
52
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
53
53
  emptyLabel: "No coupons found — create your first coupon",
54
54
  filterKeys: ["isActive"],
55
55
  defaultSort: sortBy("createdAt", "DESC"),
@@ -54,7 +54,7 @@ export function SellerGroupedListingsView({ onCreateClick, onEditClick, onDelete
54
54
  const config = {
55
55
  portal: "seller",
56
56
  title: "Grouped Listings",
57
- searchPlaceholder: "Search grouped listings",
57
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
58
58
  emptyLabel: "No grouped listings yet",
59
59
  filterKeys: [],
60
60
  defaultSort: sortBy("createdAt", "DESC"),
@@ -22,7 +22,7 @@ export function SellerOffersView({ children, onAcceptOffer, onRejectOffer, onCou
22
22
  const config = {
23
23
  portal: "seller",
24
24
  title: "Offers",
25
- searchPlaceholder: "Search offers by product or buyer name",
25
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
26
26
  emptyLabel: "No offers received",
27
27
  filterKeys: ["status"],
28
28
  defaultSort: sortBy("createdAt", "DESC"),
@@ -294,7 +294,7 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
294
294
  const config = {
295
295
  portal: "seller",
296
296
  title: "Orders",
297
- searchPlaceholder: "Search by order ID or buyer name",
297
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
298
298
  emptyLabel: "No orders yet",
299
299
  filterKeys: ["status"],
300
300
  defaultSort: DEFAULT_SORT,
@@ -95,7 +95,7 @@ export function SellerPayoutsView({ children, ...props }) {
95
95
  const config = {
96
96
  portal: "seller",
97
97
  title: "Payouts",
98
- searchPlaceholder: "Search payouts by payout # or amount",
98
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
99
99
  emptyLabel: "No payouts found",
100
100
  filterKeys: ["status", "showAllPayouts"],
101
101
  defaultSort: sortBy("createdAt", "DESC"),
@@ -64,7 +64,7 @@ export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete
64
64
  const config = {
65
65
  portal: "seller",
66
66
  title: "Store Categories",
67
- searchPlaceholder: "Search categories by label...",
67
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
68
68
  emptyLabel: "No categories yet — add your first storefront category",
69
69
  filterKeys: [],
70
70
  defaultSort: DEFAULT_SORT,
@@ -28,7 +28,7 @@ export function AdminShipmentsView({ children, ...props }) {
28
28
  const config = {
29
29
  portal: "admin",
30
30
  title: "Procurement Shipments",
31
- searchPlaceholder: "Search by shipment number or supplier",
31
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
32
32
  emptyLabel: "No shipments found",
33
33
  filterKeys: ["status"],
34
34
  defaultSort: sortBy("createdAt", "DESC"),
@@ -60,7 +60,7 @@ export function AdminTesterFeedbackListView(_props) {
60
60
  const config = {
61
61
  portal: "admin",
62
62
  title: "All Submissions",
63
- searchPlaceholder: "Search tester, comment, or case",
63
+ // Search intentionally absent: this endpoint does not read `q`, so the box accepted typing and changed nothing. Restore it when the collection gains searchTxt — audit-listing-search-capability tracks it.
64
64
  emptyLabel: "No feedback submitted yet",
65
65
  filterKeys: ["answer", "status"],
66
66
  defaultSort: sortBy(TESTER_CHECKLIST_RESPONSE_FIELDS.CREATED_AT, "DESC"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.30.5",
3
+ "version": "4.30.7",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"