@mohasinac/appkit 2.7.50 → 2.7.52

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 (143) hide show
  1. package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.d.ts +1 -0
  2. package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.js +65 -0
  3. package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.d.ts +1 -0
  4. package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.js +163 -0
  5. package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.d.ts +1 -0
  6. package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.js +115 -0
  7. package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.d.ts +1 -0
  8. package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.js +96 -0
  9. package/dist/client.d.ts +29 -6
  10. package/dist/client.js +16 -4
  11. package/dist/constants/api-endpoints.d.ts +42 -0
  12. package/dist/constants/api-endpoints.js +14 -0
  13. package/dist/features/_guide-cls.d.ts +13 -0
  14. package/dist/features/_guide-cls.js +14 -0
  15. package/dist/features/about/components/HelpPageView.js +29 -26
  16. package/dist/features/account/components/BuyerAccountGuideView.d.ts +1 -0
  17. package/dist/features/account/components/BuyerAccountGuideView.js +39 -0
  18. package/dist/features/account/components/BuyerAuctionsGuideView.d.ts +1 -0
  19. package/dist/features/account/components/BuyerAuctionsGuideView.js +38 -0
  20. package/dist/features/account/components/BuyerOrdersGuideView.d.ts +1 -0
  21. package/dist/features/account/components/BuyerOrdersGuideView.js +48 -0
  22. package/dist/features/account/components/BuyerShoppingGuideView.d.ts +1 -0
  23. package/dist/features/account/components/BuyerShoppingGuideView.js +38 -0
  24. package/dist/features/admin/components/AdminAnalyticsGuideView.d.ts +1 -0
  25. package/dist/features/admin/components/AdminAnalyticsGuideView.js +24 -0
  26. package/dist/features/admin/components/AdminBlogEditorView.js +100 -77
  27. package/dist/features/admin/components/AdminBundleEditorView.js +20 -14
  28. package/dist/features/admin/components/AdminBundlesView.d.ts +1 -3
  29. package/dist/features/admin/components/AdminBundlesView.js +173 -52
  30. package/dist/features/admin/components/AdminCatalogGuideView.d.ts +1 -0
  31. package/dist/features/admin/components/AdminCatalogGuideView.js +28 -0
  32. package/dist/features/admin/components/AdminContentGuideView.d.ts +1 -0
  33. package/dist/features/admin/components/AdminContentGuideView.js +36 -0
  34. package/dist/features/admin/components/AdminCouponEditorView.js +1 -1
  35. package/dist/features/admin/components/AdminGuideHubView.d.ts +5 -0
  36. package/dist/features/admin/components/AdminGuideHubView.js +79 -0
  37. package/dist/features/admin/components/AdminOrdersGuideView.d.ts +1 -0
  38. package/dist/features/admin/components/AdminOrdersGuideView.js +44 -0
  39. package/dist/features/admin/components/AdminProductsView.js +8 -3
  40. package/dist/features/admin/components/AdminSiteConfigGuideView.d.ts +1 -0
  41. package/dist/features/admin/components/AdminSiteConfigGuideView.js +21 -0
  42. package/dist/features/admin/components/AdminStoresGuideView.d.ts +1 -0
  43. package/dist/features/admin/components/AdminStoresGuideView.js +32 -0
  44. package/dist/features/admin/components/AdminTeamGuideView.d.ts +1 -0
  45. package/dist/features/admin/components/AdminTeamGuideView.js +33 -0
  46. package/dist/features/admin/components/AdminTrustGuideView.d.ts +1 -0
  47. package/dist/features/admin/components/AdminTrustGuideView.js +40 -0
  48. package/dist/features/admin/components/AdminUsersGuideView.d.ts +1 -0
  49. package/dist/features/admin/components/AdminUsersGuideView.js +38 -0
  50. package/dist/features/categories/components/CategoryBundlesListing.js +3 -33
  51. package/dist/features/categories/components/index.d.ts +0 -2
  52. package/dist/features/categories/components/index.js +0 -1
  53. package/dist/features/events/components/AdminEventEditorView.js +223 -269
  54. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +9 -0
  55. package/dist/features/grouped/repository/grouped-listings.repository.js +12 -0
  56. package/dist/features/media/upload/MediaUploadField.js +1 -1
  57. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +3 -1
  58. package/dist/features/products/components/AuctionsIndexListing.js +3 -1
  59. package/dist/features/products/components/MarketplaceBundleCard.d.ts +21 -0
  60. package/dist/features/products/components/MarketplaceBundleCard.js +56 -0
  61. package/dist/features/products/components/ProductsIndexListing.js +3 -1
  62. package/dist/features/products/components/index.d.ts +2 -4
  63. package/dist/features/products/components/index.js +1 -2
  64. package/dist/features/promotions/schemas/firestore.js +1 -1
  65. package/dist/features/reviews/components/index.d.ts +0 -2
  66. package/dist/features/reviews/components/index.js +0 -1
  67. package/dist/features/seller/components/BrandInlineSelect.js +23 -3
  68. package/dist/features/seller/components/CategoryInlineSelect.js +20 -3
  69. package/dist/features/seller/components/SellerAnalyticsAlertsView.d.ts +6 -0
  70. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +124 -0
  71. package/dist/features/seller/components/SellerAnalyticsView.d.ts +2 -0
  72. package/dist/features/seller/components/SellerAnalyticsView.js +17 -9
  73. package/dist/features/seller/components/SellerBundlesView.d.ts +5 -0
  74. package/dist/features/seller/components/SellerBundlesView.js +104 -0
  75. package/dist/features/seller/components/SellerClassifiedView.d.ts +5 -0
  76. package/dist/features/seller/components/SellerClassifiedView.js +113 -0
  77. package/dist/features/seller/components/SellerDashboardView.js +19 -7
  78. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +5 -0
  79. package/dist/features/seller/components/SellerDigitalCodesView.js +115 -0
  80. package/dist/features/seller/components/SellerGoogleReviewsView.d.ts +14 -0
  81. package/dist/features/seller/components/SellerGoogleReviewsView.js +95 -0
  82. package/dist/features/seller/components/SellerGroupedListingsView.d.ts +6 -0
  83. package/dist/features/seller/components/SellerGroupedListingsView.js +112 -0
  84. package/dist/features/seller/components/SellerLiveView.d.ts +5 -0
  85. package/dist/features/seller/components/SellerLiveView.js +113 -0
  86. package/dist/features/seller/components/SellerOrdersView.js +18 -2
  87. package/dist/features/seller/components/SellerPayoutMethodsView.d.ts +7 -0
  88. package/dist/features/seller/components/SellerPayoutMethodsView.js +120 -0
  89. package/dist/features/seller/components/SellerPayoutSettingsView.js +50 -12
  90. package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
  91. package/dist/features/seller/components/SellerProductShell.js +9 -7
  92. package/dist/features/seller/components/SellerProductsFilterDrawer.js +3 -1
  93. package/dist/features/seller/components/SellerProductsView.js +48 -54
  94. package/dist/features/seller/components/SellerShippingConfigsView.d.ts +7 -0
  95. package/dist/features/seller/components/SellerShippingConfigsView.js +149 -0
  96. package/dist/features/seller/components/SellerShippingView.js +37 -10
  97. package/dist/features/seller/components/SellerStoreCategoriesView.d.ts +9 -0
  98. package/dist/features/seller/components/SellerStoreCategoriesView.js +122 -0
  99. package/dist/features/seller/components/SellerStorefrontView.d.ts +0 -7
  100. package/dist/features/seller/components/SellerStorefrontView.js +26 -29
  101. package/dist/features/seller/components/SellerTemplatesView.d.ts +10 -0
  102. package/dist/features/seller/components/SellerTemplatesView.js +265 -0
  103. package/dist/features/seller/components/index.d.ts +26 -0
  104. package/dist/features/seller/components/index.js +13 -0
  105. package/dist/features/stores/api/[storeSlug]/reviews/route.js +49 -7
  106. package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +3 -0
  107. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +101 -0
  108. package/dist/features/stores/components/StoreFinanceGuideView.d.ts +3 -0
  109. package/dist/features/stores/components/StoreFinanceGuideView.js +79 -0
  110. package/dist/features/stores/components/StoreGuideHubView.d.ts +5 -0
  111. package/dist/features/stores/components/StoreGuideHubView.js +89 -0
  112. package/dist/features/stores/components/StoreListingsGuideView.d.ts +3 -0
  113. package/dist/features/stores/components/StoreListingsGuideView.js +151 -0
  114. package/dist/features/stores/components/StoreOrdersGuideView.d.ts +3 -0
  115. package/dist/features/stores/components/StoreOrdersGuideView.js +122 -0
  116. package/dist/features/stores/components/StoreReviewsListing.js +100 -23
  117. package/dist/features/stores/components/StoreSettingsGuideView.d.ts +3 -0
  118. package/dist/features/stores/components/StoreSettingsGuideView.js +56 -0
  119. package/dist/features/stores/components/index.d.ts +12 -1
  120. package/dist/features/stores/components/index.js +6 -1
  121. package/dist/features/stores/hooks/useStores.d.ts +5 -0
  122. package/dist/features/stores/hooks/useStores.js +10 -0
  123. package/dist/features/stores/manifest.js +1 -1
  124. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +13 -2
  125. package/dist/index.d.ts +54 -9
  126. package/dist/index.js +58 -15
  127. package/dist/next/routing/route-map.d.ts +55 -0
  128. package/dist/next/routing/route-map.js +25 -0
  129. package/dist/providers/db-firebase/__tests__/filter-aliases.test.d.ts +1 -0
  130. package/dist/providers/db-firebase/__tests__/filter-aliases.test.js +93 -0
  131. package/dist/providers/db-firebase/sieve.d.ts +49 -0
  132. package/dist/providers/db-firebase/sieve.js +61 -8
  133. package/dist/repositories/index.d.ts +1 -0
  134. package/dist/repositories/index.js +2 -0
  135. package/dist/tailwind-utilities.css +1 -1
  136. package/dist/ui/components/ListingToolbar.d.ts +8 -1
  137. package/dist/ui/components/ListingToolbar.js +4 -2
  138. package/dist/ui/components/__tests__/BulkActionBar.test.d.ts +1 -0
  139. package/dist/ui/components/__tests__/BulkActionBar.test.js +96 -0
  140. package/dist/ui/components/__tests__/ListingToolbar.test.d.ts +1 -0
  141. package/dist/ui/components/__tests__/ListingToolbar.test.js +125 -0
  142. package/dist/ui/index.d.ts +1 -1
  143. package/package.json +8 -2
@@ -3,6 +3,11 @@ export interface ListingToolbarSortOption {
3
3
  value: string;
4
4
  label: string;
5
5
  }
6
+ export interface ListingToolbarToggle {
7
+ label: string;
8
+ active: boolean;
9
+ onChange: (next: boolean) => void;
10
+ }
6
11
  export interface ListingToolbarLabels {
7
12
  search?: string;
8
13
  filters?: string;
@@ -48,10 +53,12 @@ export interface ListingToolbarProps {
48
53
  bulkTotalCount?: number;
49
54
  onBulkSelectAll?: () => void;
50
55
  onBulkClear?: () => void;
56
+ /** Pill-style filter toggles rendered between view controls and extra */
57
+ toggles?: ListingToolbarToggle[];
51
58
  /** Any extra action buttons placed after the view toggle */
52
59
  extra?: React.ReactNode;
53
60
  /** Overridable text labels for i18n */
54
61
  labels?: ListingToolbarLabels;
55
62
  className?: string;
56
63
  }
57
- export declare function ListingToolbar({ filterCount, onFiltersClick, searchValue, searchPlaceholder, onSearchChange, onSearchCommit, onSearchKeyDown, sortValue, sortOptions, onSortChange, view, onViewChange, hideViewToggle, showTableView, onResetAll, hasActiveState, bulkMode, bulkSelectedCount, bulkTotalCount, onBulkSelectAll, onBulkClear, extra, labels, className, }: ListingToolbarProps): import("react/jsx-runtime").JSX.Element;
64
+ export declare function ListingToolbar({ filterCount, onFiltersClick, searchValue, searchPlaceholder, onSearchChange, onSearchCommit, onSearchKeyDown, sortValue, sortOptions, onSortChange, view, onViewChange, hideViewToggle, showTableView, onResetAll, hasActiveState, bulkMode, bulkSelectedCount, bulkTotalCount, onBulkSelectAll, onBulkClear, toggles, extra, labels, className, }: ListingToolbarProps): import("react/jsx-runtime").JSX.Element;
@@ -18,7 +18,7 @@ const DEFAULT_LABELS = {
18
18
  const VIEW_BTN_BASE = "p-1.5 sm:p-2 transition-colors";
19
19
  const VIEW_BTN_ACTIVE = "bg-[var(--appkit-color-primary,theme(colors.violet.600))] text-white";
20
20
  const VIEW_BTN_INACTIVE = "text-zinc-500 hover:bg-zinc-50 dark:hover:bg-slate-800 dark:text-zinc-400";
21
- export function ListingToolbar({ filterCount = 0, onFiltersClick, searchValue = "", searchPlaceholder = "Search…", onSearchChange, onSearchCommit, onSearchKeyDown, sortValue, sortOptions, onSortChange, view = "grid", onViewChange, hideViewToggle = false, showTableView = false, onResetAll, hasActiveState = false, bulkMode = false, bulkSelectedCount = 0, bulkTotalCount = 0, onBulkSelectAll, onBulkClear, extra, labels, className = "", }) {
21
+ export function ListingToolbar({ filterCount = 0, onFiltersClick, searchValue = "", searchPlaceholder = "Search…", onSearchChange, onSearchCommit, onSearchKeyDown, sortValue, sortOptions, onSortChange, view = "grid", onViewChange, hideViewToggle = false, showTableView = false, onResetAll, hasActiveState = false, bulkMode = false, bulkSelectedCount = 0, bulkTotalCount = 0, onBulkSelectAll, onBulkClear, toggles, extra, labels, className = "", }) {
22
22
  const l = { ...DEFAULT_LABELS, ...labels };
23
23
  const handleKeyDown = (e) => {
24
24
  if (onSearchKeyDown) {
@@ -31,5 +31,7 @@ export function ListingToolbar({ filterCount = 0, onFiltersClick, searchValue =
31
31
  const allSelected = bulkTotalCount > 0 && bulkSelectedCount === bulkTotalCount;
32
32
  return (_jsx("div", { "data-testid": "listing-toolbar", className: `sticky top-[var(--header-height,0px)] z-20 border-b border-zinc-200 dark:border-slate-700 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm py-2 px-3 sm:py-2.5 sm:px-4 ${className}`, children: _jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-2.5", children: [bulkMode ? (_jsxs("div", { className: "flex flex-1 items-center gap-2", children: [_jsxs("button", { type: "button", onClick: onBulkSelectAll, className: "flex items-center gap-1.5 rounded-lg border border-zinc-300 dark:border-slate-600 px-3 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [allSelected
33
33
  ? _jsx(CheckSquare, { className: "h-4 w-4 text-[var(--appkit-color-primary,theme(colors.violet.600))]" })
34
- : _jsx(Square, { className: "h-4 w-4" }), allSelected ? l.deselectAll : l.selectAll(bulkTotalCount)] }), _jsx("span", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: l.selected(bulkSelectedCount) }), _jsx("button", { type: "button", onClick: onBulkClear, className: "text-xs text-zinc-400 hover:text-rose-500 dark:text-zinc-500 transition-colors", children: l.clearSelection })] })) : onSearchChange ? (_jsxs("div", { className: "flex flex-1 items-center overflow-hidden rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-900 min-w-0", children: [_jsx("input", { type: "text", value: searchValue, onChange: (e) => onSearchChange(e.target.value), onKeyDown: handleKeyDown, placeholder: searchPlaceholder, className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none" }), _jsx("button", { type: "button", onClick: onSearchCommit, className: "flex shrink-0 items-center justify-center px-2.5 py-2 text-zinc-400 hover:text-[var(--appkit-color-primary,theme(colors.violet.600))] transition-colors", "aria-label": l.search, children: _jsx(Search, { className: "h-4 w-4" }) })] })) : null, _jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2 shrink-0", children: [onFiltersClick && (_jsxs("button", { type: "button", onClick: onFiltersClick, className: "relative flex shrink-0 items-center gap-1.5 rounded-lg border border-zinc-300 dark:border-slate-600 px-2.5 py-1.5 sm:px-3.5 sm:py-2 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsx("span", { className: "hidden sm:inline", children: l.filters }), filterCount > 0 && (_jsx("span", { className: "absolute -top-1.5 -right-1.5 flex h-4 w-4 items-center justify-center rounded-full bg-[var(--appkit-color-primary,theme(colors.violet.600))] text-[10px] font-bold text-white", children: filterCount }))] })), sortOptions && sortValue !== undefined && onSortChange && (_jsxs("div", { className: "flex items-center gap-1.5 text-sm text-zinc-500 dark:text-zinc-400", children: [_jsx("span", { className: "hidden md:inline whitespace-nowrap text-xs", children: l.sort }), _jsx(SortDropdown, { value: sortValue, onChange: onSortChange, options: sortOptions })] })), !hideViewToggle && onViewChange && (_jsxs("div", { className: "flex items-center rounded-lg border border-zinc-300 dark:border-slate-600 overflow-hidden", children: [_jsx("button", { type: "button", onClick: () => onViewChange("grid"), "aria-label": l.gridView, className: `${VIEW_BTN_BASE} ${view === "grid" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(LayoutGrid, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => onViewChange("list"), "aria-label": l.listView, className: `${VIEW_BTN_BASE} ${view === "list" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(List, { className: "h-4 w-4" }) }), showTableView && (_jsx("button", { type: "button", onClick: () => onViewChange("table"), "aria-label": l.tableView, className: `${VIEW_BTN_BASE} ${view === "table" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(Table2, { className: "h-4 w-4" }) }))] })), onResetAll && hasActiveState && (_jsx("button", { type: "button", onClick: onResetAll, "aria-label": l.resetAll, title: l.resetAll, className: "flex shrink-0 items-center justify-center rounded-lg border border-zinc-300 dark:border-slate-600 p-1.5 sm:p-2 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:bg-slate-800 hover:text-rose-500 dark:hover:text-rose-400 transition-colors", children: _jsx(RotateCcw, { className: "h-4 w-4" }) })), extra] })] }) }));
34
+ : _jsx(Square, { className: "h-4 w-4" }), allSelected ? l.deselectAll : l.selectAll(bulkTotalCount)] }), _jsx("span", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: l.selected(bulkSelectedCount) }), _jsx("button", { type: "button", onClick: onBulkClear, className: "text-xs text-zinc-400 hover:text-rose-500 dark:text-zinc-500 transition-colors", children: l.clearSelection })] })) : onSearchChange ? (_jsxs("div", { className: "flex flex-1 items-center overflow-hidden rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-900 min-w-0", children: [_jsx("input", { type: "text", value: searchValue, onChange: (e) => onSearchChange(e.target.value), onKeyDown: handleKeyDown, placeholder: searchPlaceholder, className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none" }), _jsx("button", { type: "button", onClick: onSearchCommit, className: "flex shrink-0 items-center justify-center px-2.5 py-2 text-zinc-400 hover:text-[var(--appkit-color-primary,theme(colors.violet.600))] transition-colors", "aria-label": l.search, children: _jsx(Search, { className: "h-4 w-4" }) })] })) : null, _jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2 shrink-0", children: [onFiltersClick && (_jsxs("button", { type: "button", onClick: onFiltersClick, className: "relative flex shrink-0 items-center gap-1.5 rounded-lg border border-zinc-300 dark:border-slate-600 px-2.5 py-1.5 sm:px-3.5 sm:py-2 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsx("span", { className: "hidden sm:inline", children: l.filters }), filterCount > 0 && (_jsx("span", { className: "absolute -top-1.5 -right-1.5 flex h-4 w-4 items-center justify-center rounded-full bg-[var(--appkit-color-primary,theme(colors.violet.600))] text-[10px] font-bold text-white", children: filterCount }))] })), sortOptions && sortValue !== undefined && onSortChange && (_jsxs("div", { className: "flex items-center gap-1.5 text-sm text-zinc-500 dark:text-zinc-400", children: [_jsx("span", { className: "hidden md:inline whitespace-nowrap text-xs", children: l.sort }), _jsx(SortDropdown, { value: sortValue, onChange: onSortChange, options: sortOptions })] })), !hideViewToggle && onViewChange && (_jsxs("div", { className: "flex items-center rounded-lg border border-zinc-300 dark:border-slate-600 overflow-hidden", children: [_jsx("button", { type: "button", onClick: () => onViewChange("grid"), "aria-label": l.gridView, className: `${VIEW_BTN_BASE} ${view === "grid" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(LayoutGrid, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => onViewChange("list"), "aria-label": l.listView, className: `${VIEW_BTN_BASE} ${view === "list" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(List, { className: "h-4 w-4" }) }), showTableView && (_jsx("button", { type: "button", onClick: () => onViewChange("table"), "aria-label": l.tableView, className: `${VIEW_BTN_BASE} ${view === "table" ? VIEW_BTN_ACTIVE : VIEW_BTN_INACTIVE}`, children: _jsx(Table2, { className: "h-4 w-4" }) }))] })), onResetAll && hasActiveState && (_jsx("button", { type: "button", onClick: onResetAll, "aria-label": l.resetAll, title: l.resetAll, className: "flex shrink-0 items-center justify-center rounded-lg border border-zinc-300 dark:border-slate-600 p-1.5 sm:p-2 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:bg-slate-800 hover:text-rose-500 dark:hover:text-rose-400 transition-colors", children: _jsx(RotateCcw, { className: "h-4 w-4" }) })), toggles && toggles.length > 0 && (_jsx("div", { className: "flex items-center gap-1", children: toggles.map((t) => (_jsx("button", { type: "button", role: "switch", "aria-checked": t.active, onClick: () => t.onChange(!t.active), className: `rounded-full border px-2.5 py-1 text-xs font-medium transition-colors whitespace-nowrap ${t.active
35
+ ? "border-transparent bg-[var(--appkit-color-primary,theme(colors.violet.600))] text-white"
36
+ : "border-zinc-300 dark:border-slate-600 text-zinc-600 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: t.label }, t.label))) })), extra] })] }) }));
35
37
  }
@@ -0,0 +1,96 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen, fireEvent } from "@testing-library/react";
3
+ import { describe, it, expect, vi } from "vitest";
4
+ import { BulkActionBar } from "../BulkActionBar";
5
+ const actions = [
6
+ { id: "publish", label: "Publish", onClick: vi.fn() },
7
+ { id: "delete", label: "Delete", variant: "danger", onClick: vi.fn() },
8
+ ];
9
+ describe("BulkActionBar", () => {
10
+ it("renders nothing when selectedCount is 0", () => {
11
+ render(_jsx(BulkActionBar, { selectedCount: 0, actions: actions }));
12
+ expect(screen.queryByTestId("bulk-action-bar")).toBeNull();
13
+ });
14
+ it("renders the bar when selectedCount > 0", () => {
15
+ render(_jsx(BulkActionBar, { selectedCount: 3, actions: actions }));
16
+ expect(screen.getByTestId("bulk-action-bar")).toBeTruthy();
17
+ });
18
+ it("shows the selected count in the count pill", () => {
19
+ render(_jsx(BulkActionBar, { selectedCount: 5, actions: actions }));
20
+ expect(screen.getByTestId("bulk-selected-count").textContent).toContain("5");
21
+ });
22
+ it("calls onClearSelection when the count pill button is clicked", () => {
23
+ const onClear = vi.fn();
24
+ render(_jsx(BulkActionBar, { selectedCount: 2, actions: actions, onClearSelection: onClear }));
25
+ fireEvent.click(screen.getByRole("button", { name: /clear selection/i }));
26
+ expect(onClear).toHaveBeenCalledOnce();
27
+ });
28
+ it("renders no action buttons when actions array is empty", () => {
29
+ render(_jsx(BulkActionBar, { selectedCount: 1, actions: [] }));
30
+ expect(screen.queryByRole("button", { name: /apply/i })).toBeNull();
31
+ });
32
+ it("renders the picker trigger and apply button when actions are provided", () => {
33
+ render(_jsx(BulkActionBar, { selectedCount: 1, actions: actions }));
34
+ // Apply button must exist
35
+ expect(screen.getByRole("button", { name: /apply/i })).toBeTruthy();
36
+ // Picker trigger has aria-haspopup="listbox"
37
+ expect(screen.getByRole("button", { expanded: false })).toBeTruthy();
38
+ });
39
+ it("opens the action picker on trigger click", () => {
40
+ render(_jsx(BulkActionBar, { selectedCount: 1, actions: actions }));
41
+ const trigger = screen.getByRole("button", { expanded: false });
42
+ fireEvent.click(trigger);
43
+ // Trigger now has aria-expanded="true"
44
+ expect(screen.getByRole("button", { expanded: true })).toBeTruthy();
45
+ });
46
+ it("selects an action when an option is clicked and closes the picker", () => {
47
+ render(_jsx(BulkActionBar, { selectedCount: 1, actions: actions }));
48
+ // Open picker
49
+ const trigger = screen.getByRole("button", { expanded: false });
50
+ fireEvent.click(trigger);
51
+ // Trigger is expanded
52
+ expect(trigger.getAttribute("aria-expanded")).toBe("true");
53
+ // Click Delete option
54
+ fireEvent.click(screen.getByRole("option", { name: /delete/i }));
55
+ // Picker should close — aria-expanded returns to false
56
+ expect(trigger.getAttribute("aria-expanded")).toBe("false");
57
+ });
58
+ it("calls the selected action's onClick when Apply is clicked", () => {
59
+ const publishFn = vi.fn();
60
+ const actionsWithFns = [
61
+ { id: "publish", label: "Publish", onClick: publishFn },
62
+ ];
63
+ render(_jsx(BulkActionBar, { selectedCount: 1, actions: actionsWithFns }));
64
+ fireEvent.click(screen.getByRole("button", { name: /apply/i }));
65
+ expect(publishFn).toHaveBeenCalledOnce();
66
+ });
67
+ it("executes the newly selected action when a different option is chosen then Apply clicked", () => {
68
+ const publishFn = vi.fn();
69
+ const deleteFn = vi.fn();
70
+ const actionsWithFns = [
71
+ { id: "publish", label: "Publish", onClick: publishFn },
72
+ { id: "delete", label: "Delete", variant: "danger", onClick: deleteFn },
73
+ ];
74
+ render(_jsx(BulkActionBar, { selectedCount: 1, actions: actionsWithFns }));
75
+ // Select Delete via picker
76
+ fireEvent.click(screen.getByRole("button", { expanded: false }));
77
+ fireEvent.click(screen.getByRole("option", { name: /delete/i }));
78
+ // Apply
79
+ fireEvent.click(screen.getByRole("button", { name: /apply/i }));
80
+ expect(deleteFn).toHaveBeenCalledOnce();
81
+ expect(publishFn).not.toHaveBeenCalled();
82
+ });
83
+ it("disables the apply button when the selected action is disabled", () => {
84
+ const disabledActions = [
85
+ { id: "archive", label: "Archive", onClick: vi.fn(), disabled: true },
86
+ ];
87
+ render(_jsx(BulkActionBar, { selectedCount: 1, actions: disabledActions }));
88
+ const applyBtn = screen.getByRole("button", { name: /apply/i });
89
+ expect(applyBtn.disabled).toBe(true);
90
+ });
91
+ it("uses custom labels", () => {
92
+ render(_jsx(BulkActionBar, { selectedCount: 2, actions: actions, labels: { apply: "Run", selected: "chosen" } }));
93
+ expect(screen.getByRole("button", { name: /run/i })).toBeTruthy();
94
+ expect(screen.getByTestId("bulk-selected-count").textContent).toContain("chosen");
95
+ });
96
+ });
@@ -0,0 +1,125 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen, fireEvent } from "@testing-library/react";
3
+ import { describe, it, expect, vi } from "vitest";
4
+ import { ListingToolbar } from "../ListingToolbar";
5
+ describe("ListingToolbar", () => {
6
+ it("renders the toolbar container", () => {
7
+ render(_jsx(ListingToolbar, {}));
8
+ expect(screen.getByTestId("listing-toolbar")).toBeTruthy();
9
+ });
10
+ it("renders search input when onSearchChange is provided", () => {
11
+ render(_jsx(ListingToolbar, { onSearchChange: vi.fn(), searchValue: "", searchPlaceholder: "Find\u2026" }));
12
+ expect(screen.getByPlaceholderText("Find…")).toBeTruthy();
13
+ });
14
+ it("does not render search input when onSearchChange is absent", () => {
15
+ render(_jsx(ListingToolbar, {}));
16
+ expect(screen.queryByRole("textbox")).toBeNull();
17
+ });
18
+ it("calls onSearchChange when typing in the search input", () => {
19
+ const onChange = vi.fn();
20
+ render(_jsx(ListingToolbar, { onSearchChange: onChange, searchValue: "" }));
21
+ fireEvent.change(screen.getByRole("textbox"), { target: { value: "pokemon" } });
22
+ expect(onChange).toHaveBeenCalledWith("pokemon");
23
+ });
24
+ it("calls onSearchCommit when Enter is pressed in the search input", () => {
25
+ const onCommit = vi.fn();
26
+ render(_jsx(ListingToolbar, { onSearchChange: vi.fn(), onSearchCommit: onCommit, searchValue: "" }));
27
+ fireEvent.keyDown(screen.getByRole("textbox"), { key: "Enter" });
28
+ expect(onCommit).toHaveBeenCalledOnce();
29
+ });
30
+ it("calls onSearchCommit when the search button is clicked", () => {
31
+ const onCommit = vi.fn();
32
+ render(_jsx(ListingToolbar, { onSearchChange: vi.fn(), onSearchCommit: onCommit, searchValue: "" }));
33
+ fireEvent.click(screen.getByRole("button", { name: /search/i }));
34
+ expect(onCommit).toHaveBeenCalledOnce();
35
+ });
36
+ it("renders filters button when onFiltersClick is provided", () => {
37
+ const onClick = vi.fn();
38
+ render(_jsx(ListingToolbar, { onFiltersClick: onClick }));
39
+ const btn = screen.getByRole("button", { name: /filters/i });
40
+ expect(btn).toBeTruthy();
41
+ fireEvent.click(btn);
42
+ expect(onClick).toHaveBeenCalledOnce();
43
+ });
44
+ it("does not render filters button when onFiltersClick is absent", () => {
45
+ render(_jsx(ListingToolbar, {}));
46
+ expect(screen.queryByRole("button", { name: /filters/i })).toBeNull();
47
+ });
48
+ it("shows filter count badge when filterCount > 0", () => {
49
+ render(_jsx(ListingToolbar, { onFiltersClick: vi.fn(), filterCount: 3 }));
50
+ expect(screen.getByText("3")).toBeTruthy();
51
+ });
52
+ it("renders grid and list view buttons when onViewChange is provided", () => {
53
+ render(_jsx(ListingToolbar, { onViewChange: vi.fn(), view: "grid" }));
54
+ expect(screen.getByRole("button", { name: /grid view/i })).toBeTruthy();
55
+ expect(screen.getByRole("button", { name: /list view/i })).toBeTruthy();
56
+ });
57
+ it("does not render view buttons when hideViewToggle is true", () => {
58
+ render(_jsx(ListingToolbar, { onViewChange: vi.fn(), hideViewToggle: true, view: "grid" }));
59
+ expect(screen.queryByRole("button", { name: /grid view/i })).toBeNull();
60
+ });
61
+ it("calls onViewChange with 'list' when list button is clicked", () => {
62
+ const onViewChange = vi.fn();
63
+ render(_jsx(ListingToolbar, { onViewChange: onViewChange, view: "grid" }));
64
+ fireEvent.click(screen.getByRole("button", { name: /list view/i }));
65
+ expect(onViewChange).toHaveBeenCalledWith("list");
66
+ });
67
+ it("renders table view button only when showTableView is true", () => {
68
+ const { rerender } = render(_jsx(ListingToolbar, { onViewChange: vi.fn(), view: "grid" }));
69
+ expect(screen.queryByRole("button", { name: /table view/i })).toBeNull();
70
+ rerender(_jsx(ListingToolbar, { onViewChange: vi.fn(), view: "grid", showTableView: true }));
71
+ expect(screen.getByRole("button", { name: /table view/i })).toBeTruthy();
72
+ });
73
+ it("shows reset-all button only when onResetAll + hasActiveState are both truthy", () => {
74
+ const onReset = vi.fn();
75
+ const { rerender } = render(_jsx(ListingToolbar, { onResetAll: onReset, hasActiveState: false }));
76
+ expect(screen.queryByRole("button", { name: /reset all/i })).toBeNull();
77
+ rerender(_jsx(ListingToolbar, { onResetAll: onReset, hasActiveState: true }));
78
+ expect(screen.getByRole("button", { name: /reset all/i })).toBeTruthy();
79
+ fireEvent.click(screen.getByRole("button", { name: /reset all/i }));
80
+ expect(onReset).toHaveBeenCalledOnce();
81
+ });
82
+ it("renders bulk-mode controls when bulkMode is true", () => {
83
+ render(_jsx(ListingToolbar, { bulkMode: true, bulkSelectedCount: 2, bulkTotalCount: 10, onBulkSelectAll: vi.fn(), onBulkClear: vi.fn() }));
84
+ expect(screen.getByText(/select all/i)).toBeTruthy();
85
+ expect(screen.getByText(/2 selected/i)).toBeTruthy();
86
+ expect(screen.getByText(/clear/i)).toBeTruthy();
87
+ });
88
+ it("calls onBulkSelectAll when Select All is clicked", () => {
89
+ const onSelectAll = vi.fn();
90
+ render(_jsx(ListingToolbar, { bulkMode: true, bulkSelectedCount: 0, bulkTotalCount: 5, onBulkSelectAll: onSelectAll }));
91
+ fireEvent.click(screen.getByText(/select all/i));
92
+ expect(onSelectAll).toHaveBeenCalledOnce();
93
+ });
94
+ it("calls onBulkClear when Clear button is clicked", () => {
95
+ const onClear = vi.fn();
96
+ render(_jsx(ListingToolbar, { bulkMode: true, bulkSelectedCount: 2, bulkTotalCount: 5, onBulkClear: onClear }));
97
+ fireEvent.click(screen.getByText(/clear/i));
98
+ expect(onClear).toHaveBeenCalledOnce();
99
+ });
100
+ it("shows 'Deselect All' label when all items are selected", () => {
101
+ render(_jsx(ListingToolbar, { bulkMode: true, bulkSelectedCount: 5, bulkTotalCount: 5, onBulkSelectAll: vi.fn() }));
102
+ expect(screen.getByText(/deselect all/i)).toBeTruthy();
103
+ });
104
+ it("renders pill toggle buttons from the toggles prop", () => {
105
+ const onToggle = vi.fn();
106
+ const toggles = [
107
+ { label: "In Stock", active: false, onChange: onToggle },
108
+ { label: "Featured", active: true, onChange: vi.fn() },
109
+ ];
110
+ render(_jsx(ListingToolbar, { toggles: toggles }));
111
+ expect(screen.getByRole("switch", { name: "In Stock" })).toBeTruthy();
112
+ expect(screen.getByRole("switch", { name: "Featured" })).toBeTruthy();
113
+ });
114
+ it("calls toggle onChange with flipped value when a pill toggle is clicked", () => {
115
+ const onChange = vi.fn();
116
+ const toggles = [{ label: "In Stock", active: false, onChange }];
117
+ render(_jsx(ListingToolbar, { toggles: toggles }));
118
+ fireEvent.click(screen.getByRole("switch", { name: "In Stock" }));
119
+ expect(onChange).toHaveBeenCalledWith(true);
120
+ });
121
+ it("uses custom labels from the labels prop", () => {
122
+ render(_jsx(ListingToolbar, { onFiltersClick: vi.fn(), labels: { filters: "Refine" } }));
123
+ expect(screen.getByRole("button", { name: /refine/i })).toBeTruthy();
124
+ });
125
+ });
@@ -158,7 +158,7 @@ export type { ActiveFilter, ActiveFilterChipsProps, } from "./components/ActiveF
158
158
  export { ActiveFilterChips } from "./components/ActiveFilterChips";
159
159
  export type { SortOption, SortDropdownProps } from "./components/SortDropdown";
160
160
  export { SortDropdown } from "./components/SortDropdown";
161
- export type { ListingToolbarProps, ListingToolbarSortOption, ListingToolbarLabels } from "./components/ListingToolbar";
161
+ export type { ListingToolbarProps, ListingToolbarSortOption, ListingToolbarLabels, ListingToolbarToggle } from "./components/ListingToolbar";
162
162
  export { ListingToolbar } from "./components/ListingToolbar";
163
163
  export type { TablePaginationProps, TablePaginationLabels, } from "./components/TablePagination";
164
164
  export { TablePagination } from "./components/TablePagination";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "2.7.50",
3
+ "version": "2.7.52",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -149,6 +149,8 @@
149
149
  "audit": "node scripts/audit-violations.mjs",
150
150
  "check:types": "tsc --noEmit",
151
151
  "check:audits": "node scripts/audit-violations.mjs && node scripts/verify-entries.mjs && node scripts/verify-css-build.mjs && node scripts/audit-use-client.mjs && node scripts/audit-double-navigation.mjs && node scripts/audit-repository-fields.mjs && node scripts/audit-query-provider.mjs && node scripts/audit-export-paths.mjs",
152
+ "test": "vitest run --passWithNoTests",
153
+ "test:watch": "vitest",
152
154
  "check": "npm run check:types && npm run check:audits"
153
155
  },
154
156
  "dependencies": {
@@ -190,6 +192,8 @@
190
192
  },
191
193
  "devDependencies": {
192
194
  "@tanstack/react-query": "^5.0.0",
195
+ "@testing-library/jest-dom": "^6.9.1",
196
+ "@testing-library/react": "^16.3.2",
193
197
  "@upstash/ratelimit": "^2.0.8",
194
198
  "@upstash/redis": "^1.37.0",
195
199
  "concurrently": "^9.2.1",
@@ -197,6 +201,7 @@
197
201
  "firebase": "^12.8.0",
198
202
  "firebase-admin": "^10.3.0",
199
203
  "firebase-functions": "^6.6.0",
204
+ "jsdom": "^29.1.1",
200
205
  "lucide-react": "^1.7.0",
201
206
  "next": "^15.0.0",
202
207
  "next-intl": "^4.9.1",
@@ -206,6 +211,7 @@
206
211
  "resend": "^6.10.0",
207
212
  "server-only": "^0.0.1",
208
213
  "tailwindcss": "^3.4.19",
209
- "typescript": "^5.9.3"
214
+ "typescript": "^5.9.3",
215
+ "vitest": "^4.1.7"
210
216
  }
211
217
  }