@vendure/dashboard 3.5.0-minor-202510031341 → 3.5.0-minor-202510161257

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 (220) hide show
  1. package/dist/plugin/dashboard.plugin.js +1 -1
  2. package/dist/plugin/default-page.html +1 -1
  3. package/dist/vite/utils/ast-utils.spec.js +3 -3
  4. package/dist/vite/utils/tsconfig-utils.js +2 -1
  5. package/dist/vite/vite-plugin-hmr.d.ts +8 -0
  6. package/dist/vite/vite-plugin-hmr.js +34 -0
  7. package/dist/vite/vite-plugin-theme.js +6 -6
  8. package/dist/vite/vite-plugin-transform-index.js +6 -1
  9. package/dist/vite/vite-plugin-vendure-dashboard.d.ts +31 -4
  10. package/dist/vite/vite-plugin-vendure-dashboard.js +89 -34
  11. package/package.json +18 -5
  12. package/src/app/app-providers.tsx +4 -1
  13. package/src/app/common/map-faceted-filter-fields.ts +21 -0
  14. package/src/app/main.tsx +3 -1
  15. package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +2 -2
  16. package/src/app/routes/_authenticated/_administrators/administrators.tsx +13 -3
  17. package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +6 -13
  18. package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +1 -1
  19. package/src/app/routes/_authenticated/_assets/assets.tsx +17 -1
  20. package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
  21. package/src/app/routes/_authenticated/_collections/collections.tsx +5 -0
  22. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +0 -1
  23. package/src/app/routes/_authenticated/_customers/customers.tsx +9 -5
  24. package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +0 -6
  25. package/src/app/routes/_authenticated/_facets/components/facet-value-bulk-actions.tsx +16 -0
  26. package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +43 -12
  27. package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +14 -5
  28. package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +4 -8
  29. package/src/app/routes/_authenticated/_global-settings/utils/global-languages.ts +268 -0
  30. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +117 -92
  31. package/src/app/routes/_authenticated/_orders/components/order-address.tsx +15 -15
  32. package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +5 -5
  33. package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +2 -1
  34. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +26 -27
  35. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +5 -3
  36. package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +6 -9
  37. package/src/app/routes/_authenticated/_orders/orders.graphql.ts +17 -1
  38. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +48 -281
  39. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +59 -40
  40. package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +73 -0
  41. package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +312 -0
  42. package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +2 -2
  43. package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +4 -0
  44. package/src/app/routes/_authenticated/_product-variants/components/add-currency-dropdown.tsx +49 -0
  45. package/src/app/routes/_authenticated/_product-variants/components/add-stock-location-dropdown.tsx +56 -0
  46. package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +12 -0
  47. package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +178 -50
  48. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +0 -6
  49. package/src/app/routes/_authenticated/_products/components/product-variants-table.tsx +0 -11
  50. package/src/app/routes/_authenticated/_products/products.tsx +6 -2
  51. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +4 -8
  52. package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +0 -10
  53. package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +2 -2
  54. package/src/app/routes/_authenticated/_promotions/promotions.tsx +12 -0
  55. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +3 -10
  56. package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +2 -2
  57. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +2 -2
  58. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +4 -0
  59. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +4 -10
  60. package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +2 -2
  61. package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +2 -2
  62. package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +9 -0
  63. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +1 -0
  64. package/src/app/routes/_authenticated/_zones/zones.graphql.ts +2 -2
  65. package/src/app/routes/login.tsx +2 -2
  66. package/src/i18n/locales/ar.po +420 -289
  67. package/src/i18n/locales/cs.po +420 -289
  68. package/src/i18n/locales/de.po +420 -289
  69. package/src/i18n/locales/en.po +420 -289
  70. package/src/i18n/locales/es.po +420 -289
  71. package/src/i18n/locales/fa.po +420 -289
  72. package/src/i18n/locales/fr.po +468 -337
  73. package/src/i18n/locales/he.po +420 -289
  74. package/src/i18n/locales/hr.po +420 -289
  75. package/src/i18n/locales/it.po +420 -289
  76. package/src/i18n/locales/ja.po +420 -289
  77. package/src/i18n/locales/nb.po +420 -289
  78. package/src/i18n/locales/ne.po +420 -289
  79. package/src/i18n/locales/pl.po +420 -289
  80. package/src/i18n/locales/pt_BR.po +420 -289
  81. package/src/i18n/locales/pt_PT.po +420 -289
  82. package/src/i18n/locales/ru.po +420 -289
  83. package/src/i18n/locales/sv.po +420 -289
  84. package/src/i18n/locales/tr.po +420 -289
  85. package/src/i18n/locales/uk.po +420 -289
  86. package/src/i18n/locales/zh_Hans.po +420 -289
  87. package/src/i18n/locales/zh_Hant.po +420 -289
  88. package/src/lib/components/data-input/affixed-input.stories.tsx +93 -0
  89. package/src/lib/components/data-input/affixed-input.tsx +5 -2
  90. package/src/lib/components/data-input/boolean-input.stories.tsx +102 -0
  91. package/src/lib/components/data-input/checkbox-input.stories.tsx +61 -0
  92. package/src/lib/components/data-input/customer-group-input.tsx +0 -1
  93. package/src/lib/components/data-input/datetime-input.stories.tsx +62 -0
  94. package/src/lib/components/data-input/datetime-input.tsx +27 -13
  95. package/src/lib/components/data-input/default-relation-input.tsx +18 -12
  96. package/src/lib/components/data-input/money-input.stories.tsx +88 -0
  97. package/src/lib/components/data-input/money-input.tsx +7 -11
  98. package/src/lib/components/data-input/number-input.stories.tsx +103 -0
  99. package/src/lib/components/data-input/number-input.tsx +16 -5
  100. package/src/lib/components/data-input/password-input.stories.tsx +65 -0
  101. package/src/lib/components/data-input/rich-text-input.stories.tsx +92 -0
  102. package/src/lib/components/data-input/slug-input.stories.tsx +232 -0
  103. package/src/lib/components/data-input/slug-input.tsx +9 -10
  104. package/src/lib/components/data-input/text-input.stories.tsx +52 -0
  105. package/src/lib/components/data-input/textarea-input.stories.tsx +55 -0
  106. package/src/lib/components/data-table/add-filter-menu.tsx +6 -1
  107. package/src/lib/components/data-table/column-header-wrapper.tsx +106 -0
  108. package/src/lib/components/data-table/data-table-bulk-action-item.tsx +11 -9
  109. package/src/lib/components/data-table/data-table-bulk-actions.tsx +4 -4
  110. package/src/lib/components/data-table/data-table-column-header.tsx +17 -14
  111. package/src/lib/components/data-table/data-table-faceted-filter.tsx +33 -11
  112. package/src/lib/components/data-table/data-table-filter-badge-editable.tsx +35 -0
  113. package/src/lib/components/data-table/data-table-filter-badge.tsx +28 -14
  114. package/src/lib/components/data-table/data-table-filter-dialog.tsx +28 -8
  115. package/src/lib/components/data-table/data-table-pagination.tsx +23 -7
  116. package/src/lib/components/data-table/data-table.stories.tsx +249 -0
  117. package/src/lib/components/data-table/data-table.tsx +39 -11
  118. package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +79 -34
  119. package/src/lib/components/data-table/use-generated-columns.tsx +55 -27
  120. package/src/lib/components/layout/generated-breadcrumbs.tsx +4 -12
  121. package/src/lib/components/layout/nav-user.tsx +19 -13
  122. package/src/lib/components/login/login-form.tsx +39 -123
  123. package/src/lib/components/shared/alerts.tsx +29 -17
  124. package/src/lib/components/shared/asset/asset-bulk-actions.tsx +3 -3
  125. package/src/lib/components/shared/asset/asset-gallery.stories.tsx +76 -0
  126. package/src/lib/components/shared/asset/asset-gallery.tsx +147 -113
  127. package/src/lib/components/shared/asset/asset-picker-dialog.stories.tsx +58 -0
  128. package/src/lib/components/shared/configurable-operation-input.tsx +1 -1
  129. package/src/lib/components/shared/customer-group-selector.tsx +5 -2
  130. package/src/lib/components/shared/detail-page-button.stories.tsx +52 -0
  131. package/src/lib/components/shared/facet-value-selector.stories.tsx +48 -0
  132. package/src/lib/components/shared/facet-value-selector.tsx +130 -34
  133. package/src/lib/components/shared/paginated-list-data-table.stories.tsx +212 -0
  134. package/src/lib/components/shared/paginated-list-data-table.tsx +12 -12
  135. package/src/lib/components/shared/permission-guard.stories.tsx +46 -0
  136. package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +2 -0
  137. package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +8 -4
  138. package/src/lib/components/shared/rich-text-editor/rich-text-editor.tsx +1 -0
  139. package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +40 -0
  140. package/src/lib/components/shared/vendure-image.stories.tsx +167 -0
  141. package/src/lib/components/shared/vendure-image.tsx +6 -7
  142. package/src/lib/components/ui/accordion.stories.tsx +33 -0
  143. package/src/lib/components/ui/alert-dialog.stories.tsx +48 -0
  144. package/src/lib/components/ui/alert.stories.tsx +35 -0
  145. package/src/lib/components/ui/aspect-ratio.stories.tsx +28 -0
  146. package/src/lib/components/ui/badge.stories.tsx +28 -0
  147. package/src/lib/components/ui/breadcrumb.stories.tsx +41 -0
  148. package/src/lib/components/ui/button.stories.tsx +38 -0
  149. package/src/lib/components/ui/calendar.stories.tsx +22 -0
  150. package/src/lib/components/ui/card.stories.tsx +28 -0
  151. package/src/lib/components/ui/carousel.stories.tsx +34 -0
  152. package/src/lib/components/ui/checkbox.stories.tsx +31 -0
  153. package/src/lib/components/ui/collapsible.stories.tsx +39 -0
  154. package/src/lib/components/ui/command.stories.tsx +44 -0
  155. package/src/lib/components/ui/context-menu.stories.tsx +38 -0
  156. package/src/lib/components/ui/dialog.stories.tsx +52 -0
  157. package/src/lib/components/ui/drawer.stories.tsx +50 -0
  158. package/src/lib/components/ui/dropdown-menu.stories.tsx +41 -0
  159. package/src/lib/components/ui/hover-card.stories.tsx +38 -0
  160. package/src/lib/components/ui/input-group.tsx +148 -0
  161. package/src/lib/components/ui/input-otp.stories.tsx +30 -0
  162. package/src/lib/components/ui/input.stories.tsx +38 -0
  163. package/src/lib/components/ui/label.stories.tsx +24 -0
  164. package/src/lib/components/ui/menubar.stories.tsx +53 -0
  165. package/src/lib/components/ui/navigation-menu.stories.tsx +54 -0
  166. package/src/lib/components/ui/pagination.stories.tsx +51 -0
  167. package/src/lib/components/ui/password-input.stories.tsx +32 -0
  168. package/src/lib/components/ui/password-input.tsx +33 -0
  169. package/src/lib/components/ui/popover.stories.tsx +33 -0
  170. package/src/lib/components/ui/progress.stories.tsx +27 -0
  171. package/src/lib/components/ui/radio-group.stories.tsx +34 -0
  172. package/src/lib/components/ui/resizable.stories.tsx +32 -0
  173. package/src/lib/components/ui/scroll-area.stories.tsx +31 -0
  174. package/src/lib/components/ui/select.stories.tsx +36 -0
  175. package/src/lib/components/ui/separator.stories.tsx +35 -0
  176. package/src/lib/components/ui/sheet.stories.tsx +50 -0
  177. package/src/lib/components/ui/sidebar-context.ts +16 -0
  178. package/src/lib/components/ui/sidebar.tsx +2 -13
  179. package/src/lib/components/ui/skeleton.stories.tsx +26 -0
  180. package/src/lib/components/ui/slider.stories.tsx +37 -0
  181. package/src/lib/components/ui/switch.stories.tsx +31 -0
  182. package/src/lib/components/ui/table.stories.tsx +52 -0
  183. package/src/lib/components/ui/tabs.stories.tsx +29 -0
  184. package/src/lib/components/ui/textarea.stories.tsx +32 -0
  185. package/src/lib/components/ui/toggle-group.stories.tsx +31 -0
  186. package/src/lib/components/ui/toggle.stories.tsx +39 -0
  187. package/src/lib/components/ui/tooltip.stories.tsx +30 -0
  188. package/src/lib/components/ui/tooltip.tsx +2 -2
  189. package/src/lib/framework/alert/alert-extensions.tsx +0 -11
  190. package/src/lib/framework/alert/alert-item.tsx +14 -19
  191. package/src/lib/framework/alert/alerts-indicator.tsx +14 -15
  192. package/src/lib/framework/alert/search-index-buffer-alert/search-index-buffer-alert.ts +41 -0
  193. package/src/lib/framework/component-registry/component-registry.tsx +3 -14
  194. package/src/lib/framework/dashboard-widget/base-widget.tsx +18 -9
  195. package/src/lib/framework/dashboard-widget/latest-orders-widget/index.tsx +0 -2
  196. package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +12 -11
  197. package/src/lib/framework/defaults.ts +9 -13
  198. package/src/lib/framework/extension-api/input-component-extensions.tsx +6 -1
  199. package/src/lib/framework/extension-api/logic/alerts.ts +3 -2
  200. package/src/lib/framework/extension-api/types/alerts.ts +12 -6
  201. package/src/lib/framework/extension-api/types/data-table.ts +5 -2
  202. package/src/lib/framework/extension-api/types/layout.ts +41 -1
  203. package/src/lib/framework/extension-api/types/login.ts +0 -21
  204. package/src/lib/framework/form-engine/value-transformers.ts +8 -1
  205. package/src/lib/framework/layout-engine/custom-form-page.stories.tsx +344 -0
  206. package/src/lib/framework/layout-engine/page-layout.tsx +69 -57
  207. package/src/lib/framework/layout-engine/page.stories.tsx +275 -0
  208. package/src/lib/framework/nav-menu/nav-menu-extensions.ts +32 -19
  209. package/src/lib/framework/page/detail-page.stories.tsx +151 -0
  210. package/src/lib/framework/page/detail-page.tsx +12 -15
  211. package/src/lib/framework/page/list-page.stories.tsx +217 -0
  212. package/src/lib/framework/page/list-page.tsx +8 -1
  213. package/src/lib/graphql/api.ts +18 -1
  214. package/src/lib/graphql/graphql-env.d.ts +1 -1
  215. package/src/lib/hooks/use-alerts.ts +84 -0
  216. package/src/lib/hooks/use-floating-bulk-actions.ts +2 -3
  217. package/src/lib/index.ts +12 -5
  218. package/src/lib/providers/alerts-provider.tsx +60 -0
  219. package/src/lib/providers/channel-provider.tsx +1 -0
  220. package/src/lib/providers/theme-provider.tsx +6 -3
@@ -16,8 +16,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
16
16
  import { api } from '@/vdb/graphql/api.js';
17
17
  import { assetFragment, AssetFragment } from '@/vdb/graphql/fragments.js';
18
18
  import { graphql } from '@/vdb/graphql/graphql.js';
19
- import { Trans } from '@lingui/react/macro';
20
19
  import { formatFileSize } from '@/vdb/lib/utils.js';
20
+ import { Trans } from '@lingui/react/macro';
21
21
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
22
22
  import { useDebounce } from '@uidotdev/usehooks';
23
23
  import { Loader2, Search, Upload, X } from 'lucide-react';
@@ -131,6 +131,11 @@ export interface AssetGalleryProps {
131
131
  * Whether the gallery should display bulk actions.
132
132
  */
133
133
  displayBulkActions?: boolean;
134
+ /**
135
+ * @description
136
+ * The function to call when the page size changes.
137
+ */
138
+ onPageSizeChange?: (pageSize: number) => void;
134
139
  }
135
140
 
136
141
  /**
@@ -140,12 +145,12 @@ export interface AssetGalleryProps {
140
145
  * @example
141
146
  * ```tsx
142
147
  * <AssetGallery
143
- onSelect={handleAssetSelect}
144
- multiSelect="manual"
145
- initialSelectedAssets={initialSelectedAssets}
146
- fixedHeight={false}
147
- displayBulkActions={false}
148
- />
148
+ * onSelect={handleAssetSelect}
149
+ * multiSelect="manual"
150
+ * initialSelectedAssets={initialSelectedAssets}
151
+ * fixedHeight={false}
152
+ * displayBulkActions={false}
153
+ * />
149
154
  * ```
150
155
  *
151
156
  * @docsCategory components
@@ -153,18 +158,19 @@ export interface AssetGalleryProps {
153
158
  * @docsWeight 0
154
159
  */
155
160
  export function AssetGallery({
156
- onSelect,
157
- selectable = true,
158
- multiSelect = undefined,
159
- initialSelectedAssets = [],
160
- pageSize = 24,
161
- fixedHeight = false,
162
- showHeader = true,
163
- className = '',
164
- onFilesDropped,
165
- bulkActions,
166
- displayBulkActions = true,
167
- }: AssetGalleryProps) {
161
+ onSelect,
162
+ selectable = true,
163
+ multiSelect = undefined,
164
+ initialSelectedAssets = [],
165
+ pageSize = 24,
166
+ fixedHeight = false,
167
+ showHeader = true,
168
+ className = '',
169
+ onFilesDropped,
170
+ bulkActions,
171
+ displayBulkActions = true,
172
+ onPageSizeChange,
173
+ }: AssetGalleryProps) {
168
174
  // State
169
175
  const [page, setPage] = useState(1);
170
176
  const [search, setSearch] = useState('');
@@ -380,8 +386,7 @@ export function AssetGallery({
380
386
  <input {...getInputProps()} />
381
387
 
382
388
  {isDragActive && (
383
- <div
384
- className="absolute inset-0 bg-background/80 backdrop-blur-sm z-10 flex flex-col items-center justify-center rounded-md">
389
+ <div className="absolute inset-0 bg-background/80 backdrop-blur-sm z-10 flex flex-col items-center justify-center rounded-md">
385
390
  <Upload className="h-12 w-12 text-primary mb-2" />
386
391
  <p className="text-center font-medium">Drop files here to upload</p>
387
392
  </div>
@@ -389,7 +394,7 @@ export function AssetGallery({
389
394
 
390
395
  <div
391
396
  data-asset-gallery
392
- className="grid grid-cols-1 xs:grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-3 p-1"
397
+ className="grid grid-cols-2 xs:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-3 p-1"
393
398
  >
394
399
  {isLoading ? (
395
400
  <div className="col-span-full flex justify-center py-12">
@@ -470,119 +475,148 @@ export function AssetGallery({
470
475
  </div>
471
476
  </div>
472
477
 
473
- {totalPages > 1 && (
474
- <Pagination className="mt-4 flex-shrink-0">
475
- <PaginationContent>
476
- <PaginationItem>
477
- <PaginationPrevious
478
- href="#"
479
- size="default"
480
- onClick={e => {
481
- e.preventDefault();
482
- goToPage(page - 1);
483
- }}
484
- className={page === 1 ? 'pointer-events-none opacity-50' : ''}
485
- />
486
- </PaginationItem>
478
+ <div className="flex flex-col md:flex-row items-center md:justify-between gap-4 mt-4 flex-shrink-0">
479
+ <div className="mt-2 text-xs text-muted-foreground flex-shrink-0">
480
+ {totalItems} {totalItems === 1 ? 'asset' : 'assets'} found
481
+ {selected.length > 0 && `, ${selected.length} selected`}
482
+ </div>
483
+ <div className="flex-1"></div>
484
+ {/* Items per page selector */}
485
+ {onPageSizeChange && (
486
+ <div className="flex items-center gap-2">
487
+ <span className="text-sm text-muted-foreground">Items per page</span>
488
+ <Select
489
+ value={pageSize.toString()}
490
+ onValueChange={value => {
491
+ const newPageSize = parseInt(value, 10);
492
+ onPageSizeChange(newPageSize);
493
+ setPage(1); // Reset to first page when changing page size
494
+ }}
495
+ >
496
+ <SelectTrigger className="h-8 w-[70px]">
497
+ <SelectValue />
498
+ </SelectTrigger>
499
+ <SelectContent side="top">
500
+ {[12, 24, 48, 96].map(size => (
501
+ <SelectItem key={size} value={`${size}`}>
502
+ {size}
503
+ </SelectItem>
504
+ ))}
505
+ </SelectContent>
506
+ </Select>
507
+ </div>
508
+ )}
487
509
 
488
- {/* First page */}
489
- {page > 2 && (
510
+ {/* Pagination */}
511
+ {totalPages > 1 && (
512
+ <Pagination className="w-auto">
513
+ <PaginationContent>
490
514
  <PaginationItem>
491
- <PaginationLink
515
+ <PaginationPrevious
492
516
  href="#"
517
+ size="default"
493
518
  onClick={e => {
494
519
  e.preventDefault();
495
- goToPage(1);
520
+ goToPage(page - 1);
496
521
  }}
497
- >
498
- 1
499
- </PaginationLink>
522
+ className={page === 1 ? 'pointer-events-none opacity-50' : ''}
523
+ />
500
524
  </PaginationItem>
501
- )}
502
525
 
503
- {/* Ellipsis if needed */}
504
- {page > 3 && (
505
- <PaginationItem>
506
- <PaginationEllipsis />
507
- </PaginationItem>
508
- )}
526
+ {/* First page */}
527
+ {page > 2 && (
528
+ <PaginationItem>
529
+ <PaginationLink
530
+ href="#"
531
+ onClick={e => {
532
+ e.preventDefault();
533
+ goToPage(1);
534
+ }}
535
+ >
536
+ 1
537
+ </PaginationLink>
538
+ </PaginationItem>
539
+ )}
509
540
 
510
- {/* Previous page */}
511
- {page > 1 && (
512
- <PaginationItem>
513
- <PaginationLink
514
- href="#"
515
- onClick={e => {
516
- e.preventDefault();
517
- goToPage(page - 1);
518
- }}
519
- >
520
- {page - 1}
521
- </PaginationLink>
522
- </PaginationItem>
523
- )}
541
+ {/* Ellipsis if needed */}
542
+ {page > 3 && (
543
+ <PaginationItem>
544
+ <PaginationEllipsis />
545
+ </PaginationItem>
546
+ )}
524
547
 
525
- {/* Current page */}
526
- <PaginationItem>
527
- <PaginationLink href="#" isActive>
528
- {page}
529
- </PaginationLink>
530
- </PaginationItem>
548
+ {/* Previous page */}
549
+ {page > 1 && (
550
+ <PaginationItem>
551
+ <PaginationLink
552
+ href="#"
553
+ onClick={e => {
554
+ e.preventDefault();
555
+ goToPage(page - 1);
556
+ }}
557
+ >
558
+ {page - 1}
559
+ </PaginationLink>
560
+ </PaginationItem>
561
+ )}
531
562
 
532
- {/* Next page */}
533
- {page < totalPages && (
563
+ {/* Current page */}
534
564
  <PaginationItem>
535
- <PaginationLink
536
- href="#"
537
- onClick={e => {
538
- e.preventDefault();
539
- goToPage(page + 1);
540
- }}
541
- >
542
- {page + 1}
565
+ <PaginationLink href="#" isActive>
566
+ {page}
543
567
  </PaginationLink>
544
568
  </PaginationItem>
545
- )}
546
569
 
547
- {/* Ellipsis if needed */}
548
- {page < totalPages - 2 && (
549
- <PaginationItem>
550
- <PaginationEllipsis />
551
- </PaginationItem>
552
- )}
570
+ {/* Next page */}
571
+ {page < totalPages && (
572
+ <PaginationItem>
573
+ <PaginationLink
574
+ href="#"
575
+ onClick={e => {
576
+ e.preventDefault();
577
+ goToPage(page + 1);
578
+ }}
579
+ >
580
+ {page + 1}
581
+ </PaginationLink>
582
+ </PaginationItem>
583
+ )}
584
+
585
+ {/* Ellipsis if needed */}
586
+ {page < totalPages - 2 && (
587
+ <PaginationItem>
588
+ <PaginationEllipsis />
589
+ </PaginationItem>
590
+ )}
591
+
592
+ {/* Last page */}
593
+ {page < totalPages - 1 && (
594
+ <PaginationItem>
595
+ <PaginationLink
596
+ href="#"
597
+ onClick={e => {
598
+ e.preventDefault();
599
+ goToPage(totalPages);
600
+ }}
601
+ >
602
+ {totalPages}
603
+ </PaginationLink>
604
+ </PaginationItem>
605
+ )}
553
606
 
554
- {/* Last page */}
555
- {page < totalPages - 1 && (
556
607
  <PaginationItem>
557
- <PaginationLink
608
+ <PaginationNext
558
609
  href="#"
559
610
  onClick={e => {
560
611
  e.preventDefault();
561
- goToPage(totalPages);
612
+ goToPage(page + 1);
562
613
  }}
563
- >
564
- {totalPages}
565
- </PaginationLink>
614
+ className={page === totalPages ? 'pointer-events-none opacity-50' : ''}
615
+ />
566
616
  </PaginationItem>
567
- )}
568
-
569
- <PaginationItem>
570
- <PaginationNext
571
- href="#"
572
- onClick={e => {
573
- e.preventDefault();
574
- goToPage(page + 1);
575
- }}
576
- className={page === totalPages ? 'pointer-events-none opacity-50' : ''}
577
- />
578
- </PaginationItem>
579
- </PaginationContent>
580
- </Pagination>
581
- )}
582
-
583
- <div className="mt-2 text-xs text-muted-foreground flex-shrink-0">
584
- {totalItems} {totalItems === 1 ? 'asset' : 'assets'} found
585
- {selected.length > 0 && `, ${selected.length} selected`}
617
+ </PaginationContent>
618
+ </Pagination>
619
+ )}
586
620
  </div>
587
621
  </div>
588
622
  );
@@ -0,0 +1,58 @@
1
+ import { Button } from '@/vdb/components/ui/button.js';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+ import { RouterContextProvider } from '@tanstack/react-router';
4
+ import { useState } from 'react';
5
+ import { createDemoRoute } from '../../../../../.storybook/providers.js';
6
+ import { withDescription } from '../../../../../.storybook/with-description.js';
7
+ import { AssetPickerDialog } from './asset-picker-dialog.js';
8
+
9
+ const meta = {
10
+ title: 'Components/AssetPickerDialog',
11
+ component: AssetPickerDialog,
12
+ ...withDescription(import.meta.url, './asset-picker-dialog.js'),
13
+ parameters: {
14
+ layout: 'centered',
15
+ },
16
+ tags: ['autodocs'],
17
+ argTypes: {
18
+ multiSelect: {
19
+ control: 'boolean',
20
+ description: 'Whether multiple assets can be selected',
21
+ },
22
+ title: {
23
+ control: 'text',
24
+ description: 'The title of the dialog',
25
+ },
26
+ },
27
+ } satisfies Meta<typeof AssetPickerDialog>;
28
+
29
+ export default meta;
30
+ type Story = StoryObj<typeof meta>;
31
+
32
+ export const Playground: Story = {
33
+ args: {
34
+ multiSelect: false,
35
+ title: 'Select Assets',
36
+ },
37
+ render: args => {
38
+ const [open, setOpen] = useState(false);
39
+ const { route, router } = createDemoRoute();
40
+
41
+ return (
42
+ <RouterContextProvider router={router}>
43
+ <div>
44
+ <Button onClick={() => setOpen(true)}>Open Asset Picker</Button>
45
+ <AssetPickerDialog
46
+ {...args}
47
+ open={open}
48
+ onClose={() => setOpen(false)}
49
+ onSelect={assets => {
50
+ console.log('Selected assets:', assets);
51
+ setOpen(false);
52
+ }}
53
+ />
54
+ </div>
55
+ </RouterContextProvider>
56
+ );
57
+ },
58
+ };
@@ -144,7 +144,7 @@ export function interpolateDescription(
144
144
  (substring: string, argName: string) => {
145
145
  const normalizedArgName = argName.toLowerCase();
146
146
  const value = values.find(v => v.name === normalizedArgName)?.value;
147
- if (value == null) {
147
+ if (value == null || value === '') {
148
148
  return '_';
149
149
  }
150
150
  let formatted = value;
@@ -1,5 +1,5 @@
1
1
  import { Button } from '@/vdb/components/ui/button.js';
2
- import { Command, CommandItem, CommandList } from '@/vdb/components/ui/command.js';
2
+ import { Command, CommandEmpty, CommandItem, CommandList } from '@/vdb/components/ui/command.js';
3
3
  import { Popover, PopoverContent, PopoverTrigger } from '@/vdb/components/ui/popover.js';
4
4
  import { api } from '@/vdb/graphql/api.js';
5
5
  import { graphql } from '@/vdb/graphql/graphql.js';
@@ -27,7 +27,7 @@ export interface CustomerGroupSelectorProps {
27
27
  export function CustomerGroupSelector(props: CustomerGroupSelectorProps) {
28
28
  const [open, setOpen] = useState(false);
29
29
 
30
- const { data: groups } = useQuery({
30
+ const { data: groups, isLoading } = useQuery({
31
31
  queryKey: ['customerGroups'],
32
32
  queryFn: () =>
33
33
  api.query(customerGroupsDocument, {
@@ -54,6 +54,9 @@ export function CustomerGroupSelector(props: CustomerGroupSelectorProps) {
54
54
  {group.name}
55
55
  </CommandItem>
56
56
  ))}
57
+ <CommandEmpty>
58
+ {isLoading ? <Trans>Loading...</Trans> : <Trans>No results</Trans>}
59
+ </CommandEmpty>
57
60
  </CommandList>
58
61
  </Command>
59
62
  </PopoverContent>
@@ -0,0 +1,52 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { RouterContextProvider } from '@tanstack/react-router';
3
+ import { createDemoRoute } from '../../../../.storybook/providers.js';
4
+ import { withDescription } from '../../../../.storybook/with-description.js';
5
+ import { DetailPageButton } from './detail-page-button.js';
6
+
7
+ const meta = {
8
+ title: 'Components/DetailPageButton',
9
+ component: DetailPageButton,
10
+ ...withDescription(import.meta.url, './detail-page-button.js'),
11
+ parameters: {
12
+ layout: 'centered',
13
+ },
14
+ tags: ['autodocs'],
15
+ argTypes: {
16
+ label: {
17
+ control: 'text',
18
+ description: 'The button label text',
19
+ },
20
+ disabled: {
21
+ control: 'boolean',
22
+ description: 'Whether the button is disabled',
23
+ },
24
+ id: {
25
+ control: 'text',
26
+ description: 'ID for relative navigation (e.g., "123")',
27
+ },
28
+ href: {
29
+ control: 'text',
30
+ description: 'Custom href for absolute navigation',
31
+ },
32
+ },
33
+ } satisfies Meta<typeof DetailPageButton>;
34
+
35
+ export default meta;
36
+ type Story = StoryObj<typeof meta>;
37
+
38
+ export const Playground: Story = {
39
+ args: {
40
+ label: 'View Details',
41
+ id: '123',
42
+ disabled: false,
43
+ },
44
+ render: args => {
45
+ const { route, router } = createDemoRoute();
46
+ return (
47
+ <RouterContextProvider router={router}>
48
+ <DetailPageButton {...args} />
49
+ </RouterContextProvider>
50
+ );
51
+ },
52
+ };
@@ -0,0 +1,48 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { withDescription } from '../../../../.storybook/with-description.js';
3
+ import { FacetValueSelector } from './facet-value-selector.js';
4
+
5
+ const meta = {
6
+ title: 'Components/FacetValueSelector',
7
+ component: FacetValueSelector,
8
+ ...withDescription(import.meta.url, './facet-value-selector.js'),
9
+ parameters: {
10
+ layout: 'centered',
11
+ },
12
+ tags: ['autodocs'],
13
+ argTypes: {
14
+ disabled: {
15
+ control: 'boolean',
16
+ description: 'Whether the selector is disabled',
17
+ },
18
+ placeholder: {
19
+ control: 'text',
20
+ description: 'Placeholder text for the search input',
21
+ },
22
+ pageSize: {
23
+ control: 'number',
24
+ description: 'Number of facet values to display per page',
25
+ },
26
+ },
27
+ } satisfies Meta<typeof FacetValueSelector>;
28
+
29
+ export default meta;
30
+ type Story = StoryObj<typeof meta>;
31
+
32
+ export const Playground: Story = {
33
+ args: {
34
+ disabled: false,
35
+ placeholder: 'Search facet values...',
36
+ pageSize: 10,
37
+ },
38
+ render: args => {
39
+ return (
40
+ <FacetValueSelector
41
+ {...args}
42
+ onValueSelect={value => {
43
+ console.log('Selected facet value:', value);
44
+ }}
45
+ />
46
+ );
47
+ },
48
+ };