@vendure/dashboard 3.4.3-master-202509260228 → 3.5.0-minor-202510012036

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 (295) hide show
  1. package/README.md +4 -0
  2. package/dist/plugin/api/api-extensions.js +11 -14
  3. package/dist/plugin/api/metrics.resolver.d.ts +2 -2
  4. package/dist/plugin/api/metrics.resolver.js +2 -2
  5. package/dist/plugin/config/metrics-strategies.d.ts +9 -9
  6. package/dist/plugin/config/metrics-strategies.js +6 -6
  7. package/dist/plugin/constants.d.ts +2 -0
  8. package/dist/plugin/constants.js +3 -1
  9. package/dist/plugin/dashboard.plugin.js +13 -0
  10. package/dist/plugin/service/metrics.service.d.ts +3 -3
  11. package/dist/plugin/service/metrics.service.js +37 -53
  12. package/dist/plugin/types.d.ts +9 -12
  13. package/dist/plugin/types.js +7 -11
  14. package/dist/vite/vite-plugin-config.js +13 -9
  15. package/dist/vite/vite-plugin-translations.d.ts +22 -0
  16. package/dist/vite/vite-plugin-translations.js +66 -0
  17. package/dist/vite/vite-plugin-vendure-dashboard.js +10 -8
  18. package/lingui.config.js +25 -2
  19. package/package.json +159 -156
  20. package/src/app/app-providers.tsx +0 -4
  21. package/src/app/common/delete-bulk-action.tsx +6 -5
  22. package/src/app/common/duplicate-bulk-action.tsx +4 -5
  23. package/src/app/common/duplicate-entity-dialog.tsx +1 -1
  24. package/src/app/common/set-document-direction.ts +7 -0
  25. package/src/app/main.tsx +50 -17
  26. package/src/app/routes/_authenticated/_administrators/administrators.tsx +8 -6
  27. package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +17 -6
  28. package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +2 -2
  29. package/src/app/routes/_authenticated/_assets/assets.tsx +1 -1
  30. package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +4 -4
  31. package/src/app/routes/_authenticated/_assets/components/asset-bulk-actions.tsx +8 -6
  32. package/src/app/routes/_authenticated/_assets/components/asset-tag-filter.tsx +1 -1
  33. package/src/app/routes/_authenticated/_assets/components/asset-tags-editor.tsx +1 -1
  34. package/src/app/routes/_authenticated/_assets/components/manage-tags-dialog.tsx +3 -8
  35. package/src/app/routes/_authenticated/_channels/channels.tsx +3 -6
  36. package/src/app/routes/_authenticated/_channels/channels_.$id.tsx +5 -5
  37. package/src/app/routes/_authenticated/_collections/collections.tsx +10 -6
  38. package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +16 -5
  39. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +1 -1
  40. package/src/app/routes/_authenticated/_collections/components/collection-contents-sheet.tsx +1 -1
  41. package/src/app/routes/_authenticated/_collections/components/move-collections-dialog.tsx +6 -6
  42. package/src/app/routes/_authenticated/_countries/countries.graphql.ts +2 -0
  43. package/src/app/routes/_authenticated/_countries/countries.tsx +2 -3
  44. package/src/app/routes/_authenticated/_countries/countries_.$id.tsx +4 -4
  45. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-members-sheet.tsx +1 -1
  46. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-members-table.tsx +4 -4
  47. package/src/app/routes/_authenticated/_customer-groups/customer-groups.tsx +2 -4
  48. package/src/app/routes/_authenticated/_customer-groups/customer-groups_.$id.tsx +13 -6
  49. package/src/app/routes/_authenticated/_customers/components/customer-address-card.tsx +8 -8
  50. package/src/app/routes/_authenticated/_customers/components/customer-address-form.tsx +3 -3
  51. package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-container.tsx +1 -1
  52. package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-utils.tsx +1 -1
  53. package/src/app/routes/_authenticated/_customers/components/customer-history/default-customer-history-components.tsx +1 -1
  54. package/src/app/routes/_authenticated/_customers/components/customer-history/use-customer-history.ts +1 -1
  55. package/src/app/routes/_authenticated/_customers/components/customer-status-badge.tsx +1 -1
  56. package/src/app/routes/_authenticated/_customers/customers.graphql.ts +4 -0
  57. package/src/app/routes/_authenticated/_customers/customers.tsx +23 -11
  58. package/src/app/routes/_authenticated/_customers/customers_.$id.tsx +10 -8
  59. package/src/app/routes/_authenticated/_facets/components/edit-facet-value.tsx +1 -1
  60. package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +6 -5
  61. package/src/app/routes/_authenticated/_facets/components/facet-values-sheet.tsx +1 -1
  62. package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +1 -1
  63. package/src/app/routes/_authenticated/_facets/facets.tsx +5 -5
  64. package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +7 -5
  65. package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +18 -6
  66. package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +5 -5
  67. package/src/app/routes/_authenticated/_orders/components/add-manual-payment-dialog.tsx +19 -21
  68. package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +1 -1
  69. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +22 -22
  70. package/src/app/routes/_authenticated/_orders/components/fulfill-order-dialog.tsx +6 -6
  71. package/src/app/routes/_authenticated/_orders/components/fulfillment-details.tsx +15 -9
  72. package/src/app/routes/_authenticated/_orders/components/order-address.tsx +1 -1
  73. package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +11 -9
  74. package/src/app/routes/_authenticated/_orders/components/order-history/default-order-history-components.tsx +1 -1
  75. package/src/app/routes/_authenticated/_orders/components/order-history/order-history-container.tsx +1 -1
  76. package/src/app/routes/_authenticated/_orders/components/order-history/order-history-utils.tsx +1 -1
  77. package/src/app/routes/_authenticated/_orders/components/order-history/use-order-history.ts +1 -1
  78. package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +1 -1
  79. package/src/app/routes/_authenticated/_orders/components/order-modification-preview-dialog.tsx +4 -4
  80. package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +1 -1
  81. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +27 -27
  82. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +2 -2
  83. package/src/app/routes/_authenticated/_orders/components/order-tax-summary.tsx +1 -1
  84. package/src/app/routes/_authenticated/_orders/components/payment-details.tsx +26 -20
  85. package/src/app/routes/_authenticated/_orders/components/seller-orders-card.tsx +3 -1
  86. package/src/app/routes/_authenticated/_orders/components/settle-refund-dialog.tsx +6 -6
  87. package/src/app/routes/_authenticated/_orders/components/shipping-method-selector.tsx +1 -1
  88. package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +1 -1
  89. package/src/app/routes/_authenticated/_orders/components/use-transition-order-to-state.tsx +3 -2
  90. package/src/app/routes/_authenticated/_orders/orders.tsx +5 -9
  91. package/src/app/routes/_authenticated/_orders/orders_.$aggregateOrderId_.seller-orders.$sellerOrderId.tsx +1 -1
  92. package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +1 -1
  93. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +4 -4
  94. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +17 -17
  95. package/src/app/routes/_authenticated/_orders/utils/order-detail-loaders.tsx +1 -1
  96. package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +5 -6
  97. package/src/app/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx +13 -6
  98. package/src/app/routes/_authenticated/_product-variants/components/product-variant-bulk-actions.tsx +1 -1
  99. package/src/app/routes/_authenticated/_product-variants/components/variant-price-detail.tsx +1 -1
  100. package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +10 -0
  101. package/src/app/routes/_authenticated/_product-variants/product-variants.tsx +9 -2
  102. package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +13 -6
  103. package/src/app/routes/_authenticated/_products/components/add-option-group-dialog.tsx +5 -5
  104. package/src/app/routes/_authenticated/_products/components/add-product-variant-dialog.tsx +5 -5
  105. package/src/app/routes/_authenticated/_products/components/assign-facet-values-dialog.tsx +5 -4
  106. package/src/app/routes/_authenticated/_products/components/create-product-options-dialog.tsx +9 -12
  107. package/src/app/routes/_authenticated/_products/components/create-product-variants-dialog.tsx +1 -1
  108. package/src/app/routes/_authenticated/_products/components/create-product-variants.tsx +4 -4
  109. package/src/app/routes/_authenticated/_products/components/option-groups-editor.tsx +1 -1
  110. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +1 -1
  111. package/src/app/routes/_authenticated/_products/components/product-option-group-badge.tsx +19 -0
  112. package/src/app/routes/_authenticated/_products/components/product-option-select.tsx +3 -3
  113. package/src/app/routes/_authenticated/_products/components/product-options-table.tsx +114 -0
  114. package/src/app/routes/_authenticated/_products/product-option-groups.graphql.ts +103 -0
  115. package/src/app/routes/_authenticated/_products/products.graphql.ts +44 -32
  116. package/src/app/routes/_authenticated/_products/products.tsx +34 -5
  117. package/src/app/routes/_authenticated/_products/products_.$id.tsx +29 -12
  118. package/src/app/routes/_authenticated/_products/products_.$id_.variants.tsx +11 -11
  119. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$id.tsx +177 -0
  120. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +208 -0
  121. package/src/app/routes/_authenticated/_profile/profile.tsx +4 -4
  122. package/src/app/routes/_authenticated/_promotions/promotions.tsx +2 -4
  123. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +16 -9
  124. package/src/app/routes/_authenticated/_roles/components/permissions-table-grid.tsx +1 -1
  125. package/src/app/routes/_authenticated/_roles/roles.tsx +3 -6
  126. package/src/app/routes/_authenticated/_roles/roles_.$id.tsx +4 -6
  127. package/src/app/routes/_authenticated/_sellers/sellers.tsx +3 -4
  128. package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +4 -4
  129. package/src/app/routes/_authenticated/_shipping-methods/components/price-display.tsx +5 -5
  130. package/src/app/routes/_authenticated/_shipping-methods/components/shipping-method-test-result-wrapper.tsx +1 -1
  131. package/src/app/routes/_authenticated/_shipping-methods/components/test-address-form.tsx +11 -11
  132. package/src/app/routes/_authenticated/_shipping-methods/components/test-order-builder.tsx +1 -1
  133. package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-methods-result.tsx +8 -8
  134. package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-methods-sheet.tsx +1 -1
  135. package/src/app/routes/_authenticated/_shipping-methods/components/test-single-method-result.tsx +8 -8
  136. package/src/app/routes/_authenticated/_shipping-methods/components/test-single-shipping-method-sheet.tsx +4 -4
  137. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +2 -3
  138. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +2 -2
  139. package/src/app/routes/_authenticated/_stock-locations/stock-locations.tsx +3 -4
  140. package/src/app/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx +13 -6
  141. package/src/app/routes/_authenticated/_system/healthchecks.tsx +10 -4
  142. package/src/app/routes/_authenticated/_system/job-queue.tsx +10 -13
  143. package/src/app/routes/_authenticated/_system/scheduled-tasks.tsx +18 -16
  144. package/src/app/routes/_authenticated/_tax-categories/tax-categories.tsx +2 -4
  145. package/src/app/routes/_authenticated/_tax-categories/tax-categories_.$id.tsx +13 -6
  146. package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +8 -12
  147. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +6 -4
  148. package/src/app/routes/_authenticated/_zones/components/zone-countries-sheet.tsx +4 -1
  149. package/src/app/routes/_authenticated/_zones/zones.tsx +4 -4
  150. package/src/app/routes/_authenticated/_zones/zones_.$id.tsx +8 -5
  151. package/src/app/routes/_authenticated/index.tsx +46 -25
  152. package/src/app/styles.css +4 -0
  153. package/src/i18n/common-strings.ts +111 -0
  154. package/src/i18n/locales/ar.po +4777 -0
  155. package/src/i18n/locales/cs.po +4777 -0
  156. package/src/i18n/locales/de.po +4299 -1101
  157. package/src/i18n/locales/en.po +3857 -659
  158. package/src/i18n/locales/es.po +4777 -0
  159. package/src/i18n/locales/fa.po +4777 -0
  160. package/src/i18n/locales/fr.po +4777 -0
  161. package/src/i18n/locales/he.po +4777 -0
  162. package/src/i18n/locales/hr.po +4777 -0
  163. package/src/i18n/locales/it.po +4777 -0
  164. package/src/i18n/locales/ja.po +4777 -0
  165. package/src/i18n/locales/ko.po +4628 -0
  166. package/src/i18n/locales/nb.po +4777 -0
  167. package/src/i18n/locales/ne.po +4777 -0
  168. package/src/i18n/locales/nl.po +4628 -0
  169. package/src/i18n/locales/pl.po +4777 -0
  170. package/src/i18n/locales/pt_BR.po +4777 -0
  171. package/src/i18n/locales/pt_PT.po +4777 -0
  172. package/src/i18n/locales/ru.po +4777 -0
  173. package/src/i18n/locales/sv.po +4777 -0
  174. package/src/i18n/locales/tr.po +4777 -0
  175. package/src/i18n/locales/uk.po +4777 -0
  176. package/src/i18n/locales/zh_Hans.po +4777 -0
  177. package/src/i18n/locales/zh_Hant.po +4777 -0
  178. package/src/lib/components/data-display/json.tsx +16 -1
  179. package/src/lib/components/data-input/combination-mode-input.tsx +1 -1
  180. package/src/lib/components/data-input/custom-field-list-input.tsx +11 -7
  181. package/src/lib/components/data-input/customer-group-input.tsx +27 -33
  182. package/src/lib/components/data-input/datetime-input.tsx +40 -1
  183. package/src/lib/components/data-input/default-relation-input.tsx +5 -4
  184. package/src/lib/components/data-input/index.ts +3 -0
  185. package/src/lib/components/data-input/product-multi-selector-input.tsx +14 -14
  186. package/src/lib/components/data-input/relation-selector.tsx +1 -1
  187. package/src/lib/components/data-input/select-with-options.tsx +1 -1
  188. package/src/lib/components/data-input/slug-input.tsx +290 -0
  189. package/src/lib/components/data-table/add-filter-menu.tsx +17 -10
  190. package/src/lib/components/data-table/data-table-bulk-action-item.tsx +45 -8
  191. package/src/lib/components/data-table/data-table-bulk-actions.tsx +4 -4
  192. package/src/lib/components/data-table/data-table-column-header.tsx +13 -8
  193. package/src/lib/components/data-table/data-table-context.tsx +91 -0
  194. package/src/lib/components/data-table/data-table-faceted-filter.tsx +2 -1
  195. package/src/lib/components/data-table/data-table-filter-badge.tsx +9 -5
  196. package/src/lib/components/data-table/data-table-filter-dialog.tsx +1 -1
  197. package/src/lib/components/data-table/data-table-utils.ts +21 -4
  198. package/src/lib/components/data-table/data-table-view-options.tsx +21 -10
  199. package/src/lib/components/data-table/data-table.tsx +146 -94
  200. package/src/lib/components/data-table/filters/data-table-boolean-filter.tsx +4 -4
  201. package/src/lib/components/data-table/global-views-bar.tsx +97 -0
  202. package/src/lib/components/data-table/global-views-sheet.tsx +11 -0
  203. package/src/lib/components/data-table/human-readable-operator.tsx +1 -1
  204. package/src/lib/components/data-table/manage-global-views-button.tsx +26 -0
  205. package/src/lib/components/data-table/my-views-button.tsx +47 -0
  206. package/src/lib/components/data-table/refresh-button.tsx +12 -3
  207. package/src/lib/components/data-table/save-view-button.tsx +41 -0
  208. package/src/lib/components/data-table/save-view-dialog.tsx +113 -0
  209. package/src/lib/components/data-table/use-generated-columns.tsx +13 -8
  210. package/src/lib/components/data-table/user-views-sheet.tsx +11 -0
  211. package/src/lib/components/data-table/views-sheet.tsx +305 -0
  212. package/src/lib/components/date-range-picker.tsx +186 -0
  213. package/src/lib/components/layout/app-sidebar.tsx +3 -1
  214. package/src/lib/components/layout/channel-switcher.tsx +8 -10
  215. package/src/lib/components/layout/dev-mode-indicator.tsx +1 -1
  216. package/src/lib/components/layout/generated-breadcrumbs.tsx +10 -8
  217. package/src/lib/components/layout/language-dialog.tsx +34 -13
  218. package/src/lib/components/layout/manage-languages-dialog.tsx +1 -1
  219. package/src/lib/components/layout/nav-main.tsx +23 -13
  220. package/src/lib/components/layout/nav-user.tsx +19 -23
  221. package/src/lib/components/login/login-form.tsx +1 -1
  222. package/src/lib/components/shared/asset/asset-bulk-actions.tsx +4 -4
  223. package/src/lib/components/shared/asset/asset-focal-point-editor.tsx +1 -1
  224. package/src/lib/components/shared/asset/asset-gallery.tsx +15 -14
  225. package/src/lib/components/shared/assign-to-channel-bulk-action.tsx +11 -11
  226. package/src/lib/components/shared/assign-to-channel-dialog.tsx +6 -5
  227. package/src/lib/components/shared/channel-code-label.tsx +1 -1
  228. package/src/lib/components/shared/channel-selector.tsx +4 -4
  229. package/src/lib/components/shared/configurable-operation-multi-selector.tsx +16 -14
  230. package/src/lib/components/shared/configurable-operation-selector.tsx +1 -1
  231. package/src/lib/components/shared/confirmation-dialog.tsx +8 -8
  232. package/src/lib/components/shared/country-selector.tsx +1 -1
  233. package/src/lib/components/shared/currency-selector.tsx +4 -4
  234. package/src/lib/components/shared/custom-fields-form.tsx +8 -24
  235. package/src/lib/components/shared/customer-address-form.tsx +3 -3
  236. package/src/lib/components/shared/customer-group-selector.tsx +1 -1
  237. package/src/lib/components/shared/customer-selector.tsx +1 -1
  238. package/src/lib/components/shared/error-page.tsx +1 -1
  239. package/src/lib/components/shared/facet-value-selector.tsx +10 -10
  240. package/src/lib/components/shared/history-timeline/history-note-checkbox.tsx +1 -1
  241. package/src/lib/components/shared/history-timeline/history-note-editor.tsx +1 -1
  242. package/src/lib/components/shared/history-timeline/history-note-entry.tsx +1 -1
  243. package/src/lib/components/shared/language-selector.tsx +4 -4
  244. package/src/lib/components/shared/navigation-confirmation.tsx +1 -1
  245. package/src/lib/components/shared/paginated-list-data-table.tsx +64 -34
  246. package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +6 -5
  247. package/src/lib/components/shared/rich-text-editor/image-dialog.tsx +1 -1
  248. package/src/lib/components/shared/rich-text-editor/link-dialog.tsx +1 -1
  249. package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +1 -1
  250. package/src/lib/components/shared/rich-text-editor/table-edit-icons.tsx +1 -1
  251. package/src/lib/components/shared/role-code-label.tsx +1 -1
  252. package/src/lib/components/shared/role-selector.tsx +4 -4
  253. package/src/lib/components/shared/seller-selector.tsx +1 -1
  254. package/src/lib/components/shared/stock-level-label.tsx +3 -5
  255. package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +3 -1
  256. package/src/lib/components/shared/tax-category-selector.tsx +1 -1
  257. package/src/lib/components/shared/translatable-form-field.tsx +15 -15
  258. package/src/lib/components/shared/zone-selector.tsx +1 -1
  259. package/src/lib/components/ui/button.tsx +1 -1
  260. package/src/lib/framework/dashboard-widget/base-widget.tsx +11 -9
  261. package/src/lib/framework/dashboard-widget/latest-orders-widget/index.tsx +35 -6
  262. package/src/lib/framework/dashboard-widget/metrics-widget/index.tsx +18 -12
  263. package/src/lib/framework/dashboard-widget/metrics-widget/metrics-widget.graphql.ts +9 -3
  264. package/src/lib/framework/dashboard-widget/orders-summary/index.tsx +26 -79
  265. package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +35 -0
  266. package/src/lib/framework/defaults.ts +34 -63
  267. package/src/lib/framework/document-introspection/add-custom-fields.spec.ts +319 -9
  268. package/src/lib/framework/document-introspection/add-custom-fields.ts +60 -31
  269. package/src/lib/framework/document-introspection/get-document-structure.spec.ts +1 -159
  270. package/src/lib/framework/document-introspection/include-only-selected-list-fields.spec.ts +1840 -0
  271. package/src/lib/framework/document-introspection/include-only-selected-list-fields.ts +940 -0
  272. package/src/lib/framework/document-introspection/testing-utils.ts +161 -0
  273. package/src/lib/framework/extension-api/display-component-extensions.tsx +2 -0
  274. package/src/lib/framework/extension-api/types/data-table.ts +62 -4
  275. package/src/lib/framework/extension-api/types/navigation.ts +16 -0
  276. package/src/lib/framework/form-engine/utils.ts +34 -0
  277. package/src/lib/framework/layout-engine/page-layout.tsx +36 -36
  278. package/src/lib/framework/page/detail-page.tsx +10 -10
  279. package/src/lib/framework/page/list-page.tsx +289 -4
  280. package/src/lib/framework/page/use-extended-router.tsx +101 -34
  281. package/src/lib/graphql/api.ts +6 -2
  282. package/src/lib/graphql/graphql-env.d.ts +38 -26
  283. package/src/lib/hooks/use-display-locale.ts +40 -0
  284. package/src/lib/hooks/use-dynamic-translations.ts +46 -0
  285. package/src/lib/hooks/use-extended-detail-query.ts +1 -1
  286. package/src/lib/hooks/use-extended-list-query.ts +6 -1
  287. package/src/lib/hooks/use-local-format.ts +15 -1
  288. package/src/lib/hooks/use-saved-views.ts +230 -0
  289. package/src/lib/hooks/use-ui-language-loader.ts +30 -0
  290. package/src/lib/index.ts +15 -0
  291. package/src/lib/lib/load-i18n-messages.ts +17 -0
  292. package/src/lib/lib/trans.tsx +15 -11
  293. package/src/lib/providers/i18n-provider.tsx +7 -14
  294. package/src/lib/types/saved-views.ts +39 -0
  295. package/src/lib/utils/saved-views-utils.ts +40 -0
@@ -0,0 +1,113 @@
1
+ import { ColumnFiltersState } from '@tanstack/react-table';
2
+ import React, { useState } from 'react';
3
+ import { useSavedViews } from '../../hooks/use-saved-views.js';
4
+ import { Button } from '../ui/button.js';
5
+ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../ui/dialog.js';
6
+ import { Input } from '../ui/input.js';
7
+ import { Label } from '../ui/label.js';
8
+ import { RadioGroup, RadioGroupItem } from '../ui/radio-group.js';
9
+ import { toast } from 'sonner';
10
+
11
+ interface SaveViewDialogProps {
12
+ open: boolean;
13
+ onOpenChange: (open: boolean) => void;
14
+ filters: ColumnFiltersState;
15
+ searchTerm?: string;
16
+ }
17
+
18
+ export const SaveViewDialog: React.FC<SaveViewDialogProps> = ({
19
+ open,
20
+ onOpenChange,
21
+ filters,
22
+ searchTerm,
23
+ }) => {
24
+ const [name, setName] = useState('');
25
+ const [scope, setScope] = useState<'user' | 'global'>('user');
26
+ const [saving, setSaving] = useState(false);
27
+ const { saveView, userViews, globalViews, canManageGlobalViews } = useSavedViews();
28
+
29
+ const handleSave = async () => {
30
+ if (!name.trim()) {
31
+ toast.error('Please enter a name for the view');
32
+ return;
33
+ }
34
+
35
+ // Check for duplicate names
36
+ const existingViews = scope === 'user' ? userViews : globalViews;
37
+ if (existingViews.some(v => v.name === name.trim())) {
38
+ toast.error(`A ${scope} view with this name already exists`);
39
+ return;
40
+ }
41
+
42
+ setSaving(true);
43
+ try {
44
+ await saveView({
45
+ name: name.trim(),
46
+ scope,
47
+ filters,
48
+ searchTerm,
49
+ });
50
+ toast.success(`View "${name}" saved successfully`);
51
+ onOpenChange(false);
52
+ setName('');
53
+ setScope('user');
54
+ } catch (error) {
55
+ toast.error('Failed to save view');
56
+ console.error('Failed to save view:', error);
57
+ } finally {
58
+ setSaving(false);
59
+ }
60
+ };
61
+
62
+ return (
63
+ <Dialog open={open} onOpenChange={onOpenChange}>
64
+ <DialogContent>
65
+ <DialogHeader>
66
+ <DialogTitle>Save Current View</DialogTitle>
67
+ <DialogDescription>
68
+ Save the current filters and search term as a reusable view.
69
+ </DialogDescription>
70
+ </DialogHeader>
71
+ <div className="space-y-4 py-4">
72
+ <div className="space-y-2">
73
+ <Label htmlFor="view-name">View Name</Label>
74
+ <Input
75
+ id="view-name"
76
+ value={name}
77
+ onChange={e => setName(e.target.value)}
78
+ placeholder="Enter a name for this view"
79
+ autoFocus
80
+ />
81
+ </div>
82
+ {canManageGlobalViews && (
83
+ <div className="space-y-2">
84
+ <Label>View Scope</Label>
85
+ <RadioGroup value={scope} onValueChange={value => setScope(value as 'user' | 'global')}>
86
+ <div className="flex items-center space-x-2">
87
+ <RadioGroupItem value="user" id="scope-user" />
88
+ <Label htmlFor="scope-user" className="font-normal">
89
+ Personal View (only visible to you)
90
+ </Label>
91
+ </div>
92
+ <div className="flex items-center space-x-2">
93
+ <RadioGroupItem value="global" id="scope-global" />
94
+ <Label htmlFor="scope-global" className="font-normal">
95
+ Global View (visible to all users)
96
+ </Label>
97
+ </div>
98
+ </RadioGroup>
99
+ </div>
100
+ )}
101
+ </div>
102
+ <DialogFooter>
103
+ <Button variant="outline" onClick={() => onOpenChange(false)} disabled={saving}>
104
+ Cancel
105
+ </Button>
106
+ <Button onClick={handleSave} disabled={saving || !name.trim()}>
107
+ {saving ? 'Saving...' : 'Save View'}
108
+ </Button>
109
+ </DialogFooter>
110
+ </DialogContent>
111
+ </Dialog>
112
+ );
113
+ };
@@ -7,10 +7,10 @@ import {
7
7
  } from '@/vdb/framework/document-introspection/get-document-structure.js';
8
8
  import { BulkAction } from '@/vdb/framework/extension-api/types/index.js';
9
9
  import { api } from '@/vdb/graphql/api.js';
10
- import { Trans, useLingui } from '@/vdb/lib/trans.js';
11
10
  import { TypedDocumentNode } from '@graphql-typed-document-node/core';
11
+ import { Trans, useLingui } from '@lingui/react/macro';
12
12
  import { useMutation } from '@tanstack/react-query';
13
- import { AccessorKeyColumnDef, createColumnHelper, Row } from '@tanstack/react-table';
13
+ import { AccessorFnColumnDef, AccessorKeyColumnDef, createColumnHelper, Row } from '@tanstack/react-table';
14
14
  import { EllipsisIcon, TrashIcon } from 'lucide-react';
15
15
  import { useMemo } from 'react';
16
16
  import { toast } from 'sonner';
@@ -73,7 +73,10 @@ export function useGeneratedColumns<T extends TypedDocumentNode<any, any>>({
73
73
  includeSelectionColumn?: boolean;
74
74
  includeActionsColumn?: boolean;
75
75
  enableSorting?: boolean;
76
- }>) {
76
+ }>): {
77
+ columns: Array<AccessorKeyColumnDef<any> | AccessorFnColumnDef<any>>;
78
+ customFieldColumnNames: string[];
79
+ } {
77
80
  const columnHelper = createColumnHelper<PaginatedListItemFields<T>>();
78
81
  const allBulkActions = useAllBulkActions(bulkActions ?? []);
79
82
 
@@ -136,7 +139,7 @@ export function useGeneratedColumns<T extends TypedDocumentNode<any, any>>({
136
139
  return <DisplayComponent id="vendure:asset" value={value} />;
137
140
  }
138
141
  if (value !== null && typeof value === 'object') {
139
- return JSON.stringify(value);
142
+ return <DisplayComponent id="vendure:json" value={value} />;
140
143
  }
141
144
  return value;
142
145
  },
@@ -196,6 +199,7 @@ export function useGeneratedColumns<T extends TypedDocumentNode<any, any>>({
196
199
  />
197
200
  ),
198
201
  enableColumnFilter: false,
202
+ enableHiding: false,
199
203
  cell: ({ row }) => {
200
204
  return (
201
205
  <Checkbox
@@ -224,6 +228,7 @@ function getRowActions(
224
228
  accessorKey: 'actions',
225
229
  header: () => <Trans>Actions</Trans>,
226
230
  enableColumnFilter: false,
231
+ enableHiding: false,
227
232
  cell: ({ row, table }) => {
228
233
  return (
229
234
  <DropdownMenu>
@@ -262,7 +267,7 @@ function DeleteMutationRowAction({
262
267
  row: Row<{ id: string }>;
263
268
  }>) {
264
269
  const { refetchPaginatedList } = usePaginatedList();
265
- const { i18n } = useLingui();
270
+ const { t } = useLingui();
266
271
 
267
272
  // Inspect the mutation variables to determine if it expects 'id' or 'ids'
268
273
  const mutationVariables = getOperationVariablesFields(deleteMutation);
@@ -283,15 +288,15 @@ function DeleteMutationRowAction({
283
288
  const resultToCheck = Array.isArray(unwrappedResult) ? unwrappedResult[0] : unwrappedResult;
284
289
  if (resultToCheck.result === 'DELETED') {
285
290
  refetchPaginatedList();
286
- toast.success(i18n.t('Deleted successfully'));
291
+ toast.success(t`Deleted successfully`);
287
292
  } else {
288
- toast.error(i18n.t('Failed to delete'), {
293
+ toast.error(t`Failed to delete`, {
289
294
  description: resultToCheck.message,
290
295
  });
291
296
  }
292
297
  },
293
298
  onError: (err: Error) => {
294
- toast.error(i18n.t('Failed to delete'), {
299
+ toast.error(t`Failed to delete`, {
295
300
  description: err.message,
296
301
  });
297
302
  },
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { ViewsSheet } from './views-sheet.js';
3
+
4
+ interface UserViewsSheetProps {
5
+ open: boolean;
6
+ onOpenChange: (open: boolean) => void;
7
+ }
8
+
9
+ export const UserViewsSheet: React.FC<UserViewsSheetProps> = ({ open, onOpenChange }) => {
10
+ return <ViewsSheet open={open} onOpenChange={onOpenChange} type="user" />;
11
+ };
@@ -0,0 +1,305 @@
1
+ import { Trans, useLingui } from '@lingui/react/macro';
2
+ import { Copy, Edit, Globe, MoreHorizontal, Trash2 } from 'lucide-react';
3
+ import React, { useState } from 'react';
4
+ import { toast } from 'sonner';
5
+ import { useSavedViews } from '../../hooks/use-saved-views.js';
6
+ import { SavedView } from '../../types/saved-views.js';
7
+ import {
8
+ AlertDialog,
9
+ AlertDialogAction,
10
+ AlertDialogCancel,
11
+ AlertDialogContent,
12
+ AlertDialogDescription,
13
+ AlertDialogFooter,
14
+ AlertDialogHeader,
15
+ AlertDialogTitle,
16
+ } from '../ui/alert-dialog.js';
17
+ import { Button } from '../ui/button.js';
18
+ import {
19
+ DropdownMenu,
20
+ DropdownMenuContent,
21
+ DropdownMenuItem,
22
+ DropdownMenuTrigger,
23
+ } from '../ui/dropdown-menu.js';
24
+ import { Input } from '../ui/input.js';
25
+ import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '../ui/sheet.js';
26
+ import { useDataTableContext } from './data-table-context.js';
27
+
28
+ interface ViewsSheetProps {
29
+ open: boolean;
30
+ onOpenChange: (open: boolean) => void;
31
+ type: 'user' | 'global';
32
+ }
33
+
34
+ export const ViewsSheet: React.FC<ViewsSheetProps> = ({ open, onOpenChange, type }) => {
35
+ const { userViews, globalViews, deleteView, updateView, duplicateView, canManageGlobalViews } =
36
+ useSavedViews();
37
+ const { handleApplyView } = useDataTableContext();
38
+ const { t } = useLingui();
39
+ const [editingId, setEditingId] = useState<string | null>(null);
40
+ const [editingName, setEditingName] = useState('');
41
+ const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null);
42
+
43
+ const views = type === 'global' ? globalViews : userViews;
44
+ const isGlobal = type === 'global';
45
+
46
+ const handleViewApply = (view: SavedView) => {
47
+ handleApplyView(view.filters, view.searchTerm);
48
+ const viewName = view.name;
49
+ const message = isGlobal ? t`Applied global view "${viewName}"` : t`Applied view "${viewName}"`;
50
+ toast.success(message);
51
+ };
52
+
53
+ const handleStartEdit = (view: SavedView) => {
54
+ setEditingId(view.id);
55
+ setEditingName(view.name);
56
+ };
57
+
58
+ const handleSaveEdit = async () => {
59
+ if (!editingId || !editingName.trim()) return;
60
+
61
+ try {
62
+ await updateView({ id: editingId, name: editingName.trim() });
63
+ const message = isGlobal ? t`Global view renamed successfully` : t`View renamed successfully`;
64
+ toast.success(message);
65
+ setEditingId(null);
66
+ setEditingName('');
67
+ } catch (error) {
68
+ const message = isGlobal ? t`Failed to rename global view` : t`Failed to rename view`;
69
+ toast.error(message);
70
+ }
71
+ };
72
+
73
+ const handleCancelEdit = () => {
74
+ setEditingId(null);
75
+ setEditingName('');
76
+ };
77
+
78
+ const handleDelete = async () => {
79
+ if (!deleteConfirmId) return;
80
+
81
+ try {
82
+ await deleteView(deleteConfirmId);
83
+ const message = isGlobal ? t`Global view deleted successfully` : t`View deleted successfully`;
84
+ toast.success(message);
85
+ setDeleteConfirmId(null);
86
+ } catch (error) {
87
+ const message = isGlobal ? t`Failed to delete global view` : t`Failed to delete view`;
88
+ toast.error(message);
89
+ }
90
+ };
91
+
92
+ const handleDuplicate = async (view: SavedView) => {
93
+ try {
94
+ await duplicateView(view.id, type);
95
+ const message = isGlobal
96
+ ? t`Global view duplicated successfully`
97
+ : t`View duplicated successfully`;
98
+ toast.success(message);
99
+ } catch (error) {
100
+ const message = isGlobal ? t`Failed to duplicate global view` : t`Failed to duplicate view`;
101
+ toast.error(message);
102
+ }
103
+ };
104
+
105
+ const handleConvertToUser = async (view: SavedView) => {
106
+ try {
107
+ await duplicateView(view.id, 'user');
108
+ toast.success(t`Global view converted to personal view successfully`);
109
+ } catch (error) {
110
+ toast.error(t`Failed to convert global view to personal view`);
111
+ }
112
+ };
113
+
114
+ const handleConvertToGlobal = async (view: SavedView) => {
115
+ try {
116
+ await duplicateView(view.id, 'global');
117
+ await deleteView(view.id);
118
+ toast.success(t`View converted to global successfully`);
119
+ } catch (error) {
120
+ toast.error(t`Failed to convert view to global`);
121
+ }
122
+ };
123
+
124
+ const getTitle = () => {
125
+ return isGlobal ? <Trans>Manage Global Views</Trans> : <Trans>My Saved Views</Trans>;
126
+ };
127
+
128
+ const getDescription = () => {
129
+ return isGlobal ? (
130
+ <Trans>Manage global saved views that are visible to all users</Trans>
131
+ ) : (
132
+ <Trans>Manage your personal saved views for this table</Trans>
133
+ );
134
+ };
135
+
136
+ const getEmptyStateMessage = () => {
137
+ if (isGlobal) {
138
+ return (
139
+ <>
140
+ <p>
141
+ <Trans>No global views have been created yet.</Trans>
142
+ </p>
143
+ <p className="text-sm mt-2">
144
+ <Trans>Save a view as "Global" to make it available to all users.</Trans>
145
+ </p>
146
+ </>
147
+ );
148
+ } else {
149
+ return (
150
+ <>
151
+ <p>
152
+ <Trans>You haven't saved any views yet.</Trans>
153
+ </p>
154
+ <p className="text-sm mt-2">
155
+ <Trans>Apply filters to the table and click "Save View" to get started.</Trans>
156
+ </p>
157
+ </>
158
+ );
159
+ }
160
+ };
161
+
162
+ const getDeleteDialogTitle = () => {
163
+ return isGlobal ? <Trans>Delete Global View</Trans> : <Trans>Delete View</Trans>;
164
+ };
165
+
166
+ const getDeleteDialogDescription = () => {
167
+ return isGlobal ? (
168
+ <Trans>
169
+ Are you sure you want to delete this global view? This action cannot be undone and will affect
170
+ all users.
171
+ </Trans>
172
+ ) : (
173
+ <Trans>Are you sure you want to delete this view? This action cannot be undone.</Trans>
174
+ );
175
+ };
176
+
177
+ return (
178
+ <>
179
+ <Sheet open={open} onOpenChange={onOpenChange}>
180
+ <SheetContent className="w-[400px] sm:w-[540px]">
181
+ <SheetHeader>
182
+ <SheetTitle>{getTitle()}</SheetTitle>
183
+ <SheetDescription>{getDescription()}</SheetDescription>
184
+ </SheetHeader>
185
+ <div className="mt-4">
186
+ {views.length === 0 ? (
187
+ <div className="text-center py-8 text-muted-foreground">
188
+ {getEmptyStateMessage()}
189
+ </div>
190
+ ) : (
191
+ <div className="divide-y">
192
+ {views.map(view => (
193
+ <div
194
+ key={view.id}
195
+ className="flex items-center justify-between py-3 first:pt-0 last:pb-0 hover:bg-accent/50 transition-colors rounded-md px-2"
196
+ >
197
+ {editingId === view.id ? (
198
+ <div className="flex items-center gap-2 flex-1">
199
+ <Input
200
+ value={editingName}
201
+ onChange={e => setEditingName(e.target.value)}
202
+ onKeyDown={e => {
203
+ if (e.key === 'Enter') handleSaveEdit();
204
+ if (e.key === 'Escape') handleCancelEdit();
205
+ }}
206
+ autoFocus
207
+ className="flex-1"
208
+ />
209
+ <Button size="sm" onClick={handleSaveEdit}>
210
+ <Trans>Save</Trans>
211
+ </Button>
212
+ <Button
213
+ size="sm"
214
+ variant="outline"
215
+ onClick={handleCancelEdit}
216
+ >
217
+ <Trans>Cancel</Trans>
218
+ </Button>
219
+ </div>
220
+ ) : (
221
+ <>
222
+ <span className="font-medium text-sm truncate flex-1">
223
+ {view.name}
224
+ </span>
225
+ <div className="flex items-center gap-1">
226
+ <Button size="sm" onClick={() => handleViewApply(view)}>
227
+ <Trans>Apply</Trans>
228
+ </Button>
229
+ <DropdownMenu>
230
+ <DropdownMenuTrigger asChild>
231
+ <Button variant="ghost" size="sm">
232
+ <MoreHorizontal className="h-4 w-4" />
233
+ </Button>
234
+ </DropdownMenuTrigger>
235
+ <DropdownMenuContent align="end">
236
+ <DropdownMenuItem
237
+ onClick={() => handleStartEdit(view)}
238
+ >
239
+ <Edit className="h-4 w-4 mr-2" />
240
+ <Trans>Rename</Trans>
241
+ </DropdownMenuItem>
242
+ <DropdownMenuItem
243
+ onClick={() => handleDuplicate(view)}
244
+ >
245
+ <Copy className="h-4 w-4 mr-2" />
246
+ <Trans>Duplicate</Trans>
247
+ </DropdownMenuItem>
248
+ {isGlobal ? (
249
+ <DropdownMenuItem
250
+ onClick={() => handleConvertToUser(view)}
251
+ >
252
+ <Copy className="h-4 w-4 mr-2" />
253
+ <Trans>Copy to Personal</Trans>
254
+ </DropdownMenuItem>
255
+ ) : (
256
+ canManageGlobalViews && (
257
+ <DropdownMenuItem
258
+ onClick={() =>
259
+ handleConvertToGlobal(view)
260
+ }
261
+ >
262
+ <Globe className="h-4 w-4 mr-2" />
263
+ <Trans>Make Global</Trans>
264
+ </DropdownMenuItem>
265
+ )
266
+ )}
267
+ <DropdownMenuItem
268
+ onClick={() => setDeleteConfirmId(view.id)}
269
+ className="text-destructive"
270
+ >
271
+ <Trash2 className="h-4 w-4 mr-2" />
272
+ <Trans>Delete</Trans>
273
+ </DropdownMenuItem>
274
+ </DropdownMenuContent>
275
+ </DropdownMenu>
276
+ </div>
277
+ </>
278
+ )}
279
+ </div>
280
+ ))}
281
+ </div>
282
+ )}
283
+ </div>
284
+ </SheetContent>
285
+ </Sheet>
286
+
287
+ <AlertDialog open={!!deleteConfirmId} onOpenChange={() => setDeleteConfirmId(null)}>
288
+ <AlertDialogContent>
289
+ <AlertDialogHeader>
290
+ <AlertDialogTitle>{getDeleteDialogTitle()}</AlertDialogTitle>
291
+ <AlertDialogDescription>{getDeleteDialogDescription()}</AlertDialogDescription>
292
+ </AlertDialogHeader>
293
+ <AlertDialogFooter>
294
+ <AlertDialogCancel>
295
+ <Trans>Cancel</Trans>
296
+ </AlertDialogCancel>
297
+ <AlertDialogAction onClick={handleDelete}>
298
+ <Trans>Delete</Trans>
299
+ </AlertDialogAction>
300
+ </AlertDialogFooter>
301
+ </AlertDialogContent>
302
+ </AlertDialog>
303
+ </>
304
+ );
305
+ };