@vendure/dashboard 3.4.0-minor-202506250934 → 3.4.0

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 (523) hide show
  1. package/README.md +102 -3
  2. package/dist/plugin/api/api-extensions.d.ts +1 -0
  3. package/dist/plugin/api/api-extensions.js +38 -0
  4. package/dist/plugin/api/metrics.resolver.d.ts +8 -0
  5. package/dist/plugin/api/metrics.resolver.js +40 -0
  6. package/dist/plugin/config/metrics-strategies.d.ts +39 -0
  7. package/dist/plugin/config/metrics-strategies.js +74 -0
  8. package/dist/plugin/constants.d.ts +4 -3
  9. package/dist/plugin/constants.js +10 -277
  10. package/dist/plugin/dashboard.plugin.d.ts +95 -0
  11. package/dist/plugin/dashboard.plugin.js +168 -0
  12. package/dist/plugin/index.d.ts +2 -1
  13. package/dist/plugin/index.js +18 -1
  14. package/dist/plugin/package.json +3 -0
  15. package/dist/plugin/service/metrics.service.d.ts +15 -0
  16. package/dist/plugin/service/metrics.service.js +145 -0
  17. package/dist/plugin/types.d.ts +23 -0
  18. package/dist/plugin/types.js +13 -0
  19. package/dist/vite/constants.d.ts +5 -0
  20. package/dist/vite/constants.js +277 -0
  21. package/dist/vite/index.d.ts +1 -0
  22. package/dist/vite/index.js +1 -0
  23. package/dist/vite/types.d.ts +40 -0
  24. package/dist/{plugin → vite}/utils/ast-utils.d.ts +0 -5
  25. package/dist/vite/utils/ast-utils.js +29 -0
  26. package/dist/vite/utils/ast-utils.spec.d.ts +1 -0
  27. package/dist/vite/utils/ast-utils.spec.js +45 -0
  28. package/dist/vite/utils/compiler.d.ts +22 -0
  29. package/dist/vite/utils/compiler.js +162 -0
  30. package/dist/vite/utils/config-loader.d.ts +1 -0
  31. package/dist/vite/utils/config-loader.js +1 -0
  32. package/dist/vite/utils/logger.d.ts +3 -0
  33. package/dist/vite/utils/logger.js +39 -0
  34. package/dist/vite/utils/plugin-discovery.d.ts +27 -0
  35. package/dist/vite/utils/plugin-discovery.js +387 -0
  36. package/dist/vite/utils/tsconfig-utils.d.ts +9 -0
  37. package/dist/vite/utils/tsconfig-utils.js +50 -0
  38. package/dist/vite/utils/ui-config.d.ts +3 -0
  39. package/dist/vite/utils/ui-config.js +30 -0
  40. package/dist/{plugin → vite}/vite-plugin-config-loader.d.ts +3 -3
  41. package/dist/{plugin → vite}/vite-plugin-config-loader.js +18 -13
  42. package/dist/{plugin → vite}/vite-plugin-config.js +6 -7
  43. package/dist/{plugin → vite}/vite-plugin-dashboard-metadata.d.ts +1 -3
  44. package/dist/{plugin → vite}/vite-plugin-dashboard-metadata.js +20 -10
  45. package/dist/vite/vite-plugin-tailwind-source.d.ts +7 -0
  46. package/dist/vite/vite-plugin-tailwind-source.js +61 -0
  47. package/dist/vite/vite-plugin-theme.js +130 -0
  48. package/dist/vite/vite-plugin-ui-config.d.ts +123 -0
  49. package/dist/{plugin → vite}/vite-plugin-ui-config.js +3 -11
  50. package/dist/vite/vite-plugin-vendure-dashboard.d.ts +85 -0
  51. package/dist/{plugin → vite}/vite-plugin-vendure-dashboard.js +10 -7
  52. package/index.html +1 -1
  53. package/package.json +56 -31
  54. package/src/app/app-providers.tsx +7 -7
  55. package/src/app/common/delete-bulk-action.tsx +148 -0
  56. package/src/app/common/duplicate-bulk-action.tsx +134 -0
  57. package/src/app/main.tsx +9 -9
  58. package/src/app/routes/__root.tsx +1 -2
  59. package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +10 -1
  60. package/src/app/routes/_authenticated/_administrators/administrators.tsx +15 -8
  61. package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +16 -12
  62. package/src/app/routes/_authenticated/_administrators/components/administrator-bulk-actions.tsx +15 -0
  63. package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +16 -16
  64. package/src/app/routes/_authenticated/_assets/assets.graphql.ts +13 -2
  65. package/src/app/routes/_authenticated/_assets/assets.tsx +16 -4
  66. package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +52 -38
  67. package/src/app/routes/_authenticated/_assets/components/asset-bulk-actions.tsx +45 -0
  68. package/src/app/routes/_authenticated/_channels/channels.graphql.ts +10 -1
  69. package/src/app/routes/_authenticated/_channels/channels.tsx +17 -10
  70. package/src/app/routes/_authenticated/_channels/channels_.$id.tsx +21 -17
  71. package/src/app/routes/_authenticated/_channels/components/channel-bulk-actions.tsx +15 -0
  72. package/src/app/routes/_authenticated/_collections/collections.graphql.ts +60 -3
  73. package/src/app/routes/_authenticated/_collections/collections.tsx +168 -124
  74. package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +21 -17
  75. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +123 -0
  76. package/src/app/routes/_authenticated/_collections/components/collection-contents-preview-table.tsx +8 -9
  77. package/src/app/routes/_authenticated/_collections/components/collection-contents-sheet.tsx +9 -5
  78. package/src/app/routes/_authenticated/_collections/components/collection-contents-table.tsx +10 -9
  79. package/src/app/routes/_authenticated/_collections/components/collection-filters-selector.tsx +12 -79
  80. package/src/app/routes/_authenticated/_collections/components/move-collections-dialog.tsx +430 -0
  81. package/src/app/routes/_authenticated/_collections/components/move-single-collection.tsx +33 -0
  82. package/src/app/routes/_authenticated/_countries/components/country-bulk-actions.tsx +15 -0
  83. package/src/app/routes/_authenticated/_countries/countries.graphql.ts +11 -2
  84. package/src/app/routes/_authenticated/_countries/countries.tsx +13 -6
  85. package/src/app/routes/_authenticated/_countries/countries_.$id.tsx +21 -17
  86. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-bulk-actions.tsx +15 -0
  87. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-members-sheet.tsx +12 -5
  88. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-members-table.tsx +22 -17
  89. package/src/app/routes/_authenticated/_customer-groups/customer-groups.graphql.ts +11 -2
  90. package/src/app/routes/_authenticated/_customer-groups/customer-groups.tsx +13 -6
  91. package/src/app/routes/_authenticated/_customer-groups/customer-groups_.$id.tsx +18 -15
  92. package/src/app/routes/_authenticated/_customers/components/customer-address-card.tsx +19 -19
  93. package/src/app/routes/_authenticated/_customers/components/customer-address-form.tsx +10 -10
  94. package/src/app/routes/_authenticated/_customers/components/customer-bulk-actions.tsx +15 -0
  95. package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-container.tsx +5 -5
  96. package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history.tsx +11 -7
  97. package/src/app/routes/_authenticated/_customers/components/customer-history/use-customer-history.ts +4 -4
  98. package/src/app/routes/_authenticated/_customers/components/customer-order-table.tsx +75 -73
  99. package/src/app/routes/_authenticated/_customers/components/customer-status-badge.tsx +4 -4
  100. package/src/app/routes/_authenticated/_customers/customers.graphql.ts +10 -2
  101. package/src/app/routes/_authenticated/_customers/customers.tsx +13 -6
  102. package/src/app/routes/_authenticated/_customers/customers_.$id.tsx +19 -15
  103. package/src/app/routes/_authenticated/_facets/components/edit-facet-value.tsx +9 -9
  104. package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +104 -0
  105. package/src/app/routes/_authenticated/_facets/components/facet-values-sheet.tsx +4 -4
  106. package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +76 -65
  107. package/src/app/routes/_authenticated/_facets/facets.graphql.ts +80 -1
  108. package/src/app/routes/_authenticated/_facets/facets.tsx +31 -7
  109. package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +147 -0
  110. package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +17 -13
  111. package/src/app/routes/_authenticated/_global-settings/global-settings.graphql.ts +1 -1
  112. package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +26 -18
  113. package/src/app/routes/_authenticated/_orders/components/add-manual-payment-dialog.tsx +191 -0
  114. package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +11 -15
  115. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +111 -80
  116. package/src/app/routes/_authenticated/_orders/components/fulfill-order-dialog.tsx +320 -0
  117. package/src/app/routes/_authenticated/_orders/components/fulfillment-details.tsx +153 -0
  118. package/src/app/routes/_authenticated/_orders/components/money-gross-net.tsx +11 -9
  119. package/src/app/routes/_authenticated/_orders/components/order-address.tsx +19 -13
  120. package/src/app/routes/_authenticated/_orders/components/order-history/order-history-container.tsx +67 -62
  121. package/src/app/routes/_authenticated/_orders/components/order-history/order-history.tsx +391 -39
  122. package/src/app/routes/_authenticated/_orders/components/order-history/use-order-history.ts +9 -5
  123. package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +28 -13
  124. package/src/app/routes/_authenticated/_orders/components/order-modification-preview-dialog.tsx +364 -0
  125. package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +222 -0
  126. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +39 -22
  127. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +148 -87
  128. package/src/app/routes/_authenticated/_orders/components/order-tax-summary.tsx +37 -36
  129. package/src/app/routes/_authenticated/_orders/components/payment-details.tsx +274 -48
  130. package/src/app/routes/_authenticated/_orders/components/settle-refund-dialog.tsx +80 -0
  131. package/src/app/routes/_authenticated/_orders/components/shipping-method-selector.tsx +43 -44
  132. package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +102 -0
  133. package/src/app/routes/_authenticated/_orders/components/use-transition-order-to-state.tsx +144 -0
  134. package/src/app/routes/_authenticated/_orders/orders.graphql.ts +219 -5
  135. package/src/app/routes/_authenticated/_orders/orders.tsx +23 -22
  136. package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +190 -42
  137. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +550 -0
  138. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +187 -107
  139. package/src/app/routes/_authenticated/_orders/utils/order-types.ts +10 -0
  140. package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +78 -0
  141. package/src/app/routes/_authenticated/_payment-methods/components/payment-eligibility-checker-selector.tsx +12 -79
  142. package/src/app/routes/_authenticated/_payment-methods/components/payment-handler-selector.tsx +12 -79
  143. package/src/app/routes/_authenticated/_payment-methods/components/payment-method-bulk-actions.tsx +58 -0
  144. package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +29 -2
  145. package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +36 -14
  146. package/src/app/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx +22 -15
  147. package/src/app/routes/_authenticated/_product-variants/components/product-variant-bulk-actions.tsx +110 -0
  148. package/src/app/routes/_authenticated/_product-variants/components/variant-price-detail.tsx +7 -6
  149. package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +64 -3
  150. package/src/app/routes/_authenticated/_product-variants/product-variants.tsx +39 -9
  151. package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +33 -22
  152. package/src/app/routes/_authenticated/_products/components/add-product-variant-dialog.tsx +10 -10
  153. package/src/app/routes/_authenticated/_products/components/assign-facet-values-dialog.tsx +281 -0
  154. package/src/app/routes/_authenticated/_products/components/create-product-options-dialog.tsx +57 -41
  155. package/src/app/routes/_authenticated/_products/components/create-product-variants-dialog.tsx +11 -11
  156. package/src/app/routes/_authenticated/_products/components/create-product-variants.tsx +18 -14
  157. package/src/app/routes/_authenticated/_products/components/option-value-input.tsx +23 -17
  158. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +123 -0
  159. package/src/app/routes/_authenticated/_products/components/product-option-select.tsx +28 -34
  160. package/src/app/routes/_authenticated/_products/components/product-variants-table.tsx +84 -53
  161. package/src/app/routes/_authenticated/_products/products.graphql.ts +70 -2
  162. package/src/app/routes/_authenticated/_products/products.tsx +36 -7
  163. package/src/app/routes/_authenticated/_products/products_.$id.tsx +34 -26
  164. package/src/app/routes/_authenticated/_profile/profile.graphql.ts +1 -1
  165. package/src/app/routes/_authenticated/_profile/profile.tsx +8 -8
  166. package/src/app/routes/_authenticated/_promotions/components/promotion-actions-selector.tsx +14 -86
  167. package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +82 -0
  168. package/src/app/routes/_authenticated/_promotions/components/promotion-conditions-selector.tsx +14 -86
  169. package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +27 -2
  170. package/src/app/routes/_authenticated/_promotions/promotions.tsx +31 -7
  171. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +20 -16
  172. package/src/app/routes/_authenticated/_roles/components/expandable-permissions.tsx +5 -5
  173. package/src/app/routes/_authenticated/_roles/components/permissions-grid.tsx +21 -17
  174. package/src/app/routes/_authenticated/_roles/components/role-bulk-actions.tsx +15 -0
  175. package/src/app/routes/_authenticated/_roles/roles.graphql.ts +10 -1
  176. package/src/app/routes/_authenticated/_roles/roles.tsx +17 -10
  177. package/src/app/routes/_authenticated/_roles/roles_.$id.tsx +16 -12
  178. package/src/app/routes/_authenticated/_sellers/components/seller-bulk-actions.tsx +15 -0
  179. package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +10 -1
  180. package/src/app/routes/_authenticated/_sellers/sellers.tsx +13 -6
  181. package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +17 -13
  182. package/src/app/routes/_authenticated/_shipping-methods/components/fulfillment-handler-selector.tsx +6 -6
  183. package/src/app/routes/_authenticated/_shipping-methods/components/shipping-calculator-selector.tsx +12 -80
  184. package/src/app/routes/_authenticated/_shipping-methods/components/shipping-eligibility-checker-selector.tsx +15 -80
  185. package/src/app/routes/_authenticated/_shipping-methods/components/shipping-method-bulk-actions.tsx +61 -0
  186. package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-method-dialog.tsx +3 -3
  187. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +29 -2
  188. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +25 -6
  189. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +25 -19
  190. package/src/app/routes/_authenticated/_stock-locations/components/stock-location-bulk-actions.tsx +58 -0
  191. package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +26 -1
  192. package/src/app/routes/_authenticated/_stock-locations/stock-locations.tsx +25 -6
  193. package/src/app/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx +16 -12
  194. package/src/app/routes/_authenticated/_system/components/payload-dialog.tsx +5 -5
  195. package/src/app/routes/_authenticated/_system/healthchecks.tsx +5 -5
  196. package/src/app/routes/_authenticated/_system/job-queue.graphql.ts +12 -1
  197. package/src/app/routes/_authenticated/_system/job-queue.tsx +110 -11
  198. package/src/app/routes/_authenticated/_system/scheduled-tasks.tsx +34 -28
  199. package/src/app/routes/_authenticated/_tax-categories/components/tax-category-bulk-actions.tsx +15 -0
  200. package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +11 -2
  201. package/src/app/routes/_authenticated/_tax-categories/tax-categories.tsx +14 -7
  202. package/src/app/routes/_authenticated/_tax-categories/tax-categories_.$id.tsx +20 -16
  203. package/src/app/routes/_authenticated/_tax-rates/components/tax-rate-bulk-actions.tsx +15 -0
  204. package/src/app/routes/_authenticated/_tax-rates/tax-rates.graphql.ts +11 -2
  205. package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +15 -8
  206. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +22 -18
  207. package/src/app/routes/_authenticated/_zones/components/zone-bulk-actions.tsx +15 -0
  208. package/src/app/routes/_authenticated/_zones/components/zone-countries-sheet.tsx +4 -4
  209. package/src/app/routes/_authenticated/_zones/components/zone-countries-table.tsx +12 -15
  210. package/src/app/routes/_authenticated/_zones/zones.graphql.ts +11 -2
  211. package/src/app/routes/_authenticated/_zones/zones.tsx +13 -6
  212. package/src/app/routes/_authenticated/_zones/zones_.$id.tsx +18 -14
  213. package/src/app/routes/_authenticated/index.tsx +6 -6
  214. package/src/app/routes/_authenticated.tsx +4 -4
  215. package/src/app/routes/login.tsx +2 -2
  216. package/src/app/styles.css +5 -2
  217. package/src/lib/components/data-display/boolean.tsx +1 -1
  218. package/src/lib/components/data-display/date-time.tsx +9 -3
  219. package/src/lib/components/data-display/json.tsx +1 -1
  220. package/src/lib/components/data-display/money.tsx +2 -3
  221. package/src/lib/components/data-input/affixed-input.tsx +3 -8
  222. package/src/lib/components/data-input/combination-mode-input.tsx +52 -0
  223. package/src/lib/components/data-input/configurable-operation-list-input.tsx +433 -0
  224. package/src/lib/components/data-input/custom-field-list-input.tsx +297 -0
  225. package/src/lib/components/data-input/customer-group-input.tsx +2 -2
  226. package/src/lib/components/data-input/datetime-input.tsx +132 -146
  227. package/src/lib/components/data-input/default-relation-input.tsx +599 -0
  228. package/src/lib/components/data-input/facet-value-input.tsx +30 -22
  229. package/src/lib/components/data-input/index.ts +17 -0
  230. package/src/lib/components/data-input/money-input.tsx +5 -12
  231. package/src/lib/components/data-input/product-multi-selector.tsx +426 -0
  232. package/src/lib/components/data-input/relation-input.tsx +164 -0
  233. package/src/lib/components/data-input/relation-selector.tsx +476 -0
  234. package/src/lib/components/data-input/{richt-text-input.tsx → rich-text-input.tsx} +15 -9
  235. package/src/lib/components/data-input/select-with-options.tsx +84 -0
  236. package/src/lib/components/data-input/struct-form-input.tsx +324 -0
  237. package/src/lib/components/data-table/add-filter-menu.tsx +9 -18
  238. package/src/lib/components/data-table/data-table-bulk-action-item.tsx +101 -0
  239. package/src/lib/components/data-table/data-table-bulk-actions.tsx +108 -0
  240. package/src/lib/components/data-table/data-table-column-header.tsx +4 -22
  241. package/src/lib/components/data-table/data-table-faceted-filter.tsx +8 -8
  242. package/src/lib/components/data-table/data-table-filter-badge.tsx +16 -8
  243. package/src/lib/components/data-table/data-table-filter-dialog.tsx +8 -8
  244. package/src/lib/components/data-table/data-table-pagination.tsx +4 -4
  245. package/src/lib/components/data-table/data-table-utils.ts +34 -0
  246. package/src/lib/components/data-table/data-table-view-options.tsx +27 -23
  247. package/src/lib/components/data-table/data-table.tsx +90 -43
  248. package/src/lib/components/data-table/filters/data-table-boolean-filter.tsx +11 -8
  249. package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +14 -23
  250. package/src/lib/components/data-table/filters/data-table-id-filter.tsx +15 -10
  251. package/src/lib/components/data-table/filters/data-table-number-filter.tsx +18 -17
  252. package/src/lib/components/data-table/filters/data-table-string-filter.tsx +29 -12
  253. package/src/lib/components/data-table/human-readable-operator.tsx +3 -3
  254. package/src/lib/components/data-table/refresh-button.tsx +30 -15
  255. package/src/lib/components/data-table/use-generated-columns.tsx +322 -0
  256. package/src/lib/components/labeled-data.tsx +21 -0
  257. package/src/lib/components/layout/app-layout.tsx +8 -13
  258. package/src/lib/components/layout/app-sidebar.tsx +5 -5
  259. package/src/lib/components/layout/channel-switcher.tsx +171 -62
  260. package/src/lib/components/layout/content-language-selector.tsx +16 -10
  261. package/src/lib/components/layout/dev-mode-indicator.tsx +18 -0
  262. package/src/lib/components/layout/generated-breadcrumbs.tsx +1 -1
  263. package/src/lib/components/layout/language-dialog.tsx +7 -12
  264. package/src/lib/components/layout/manage-languages-dialog.tsx +405 -0
  265. package/src/lib/components/layout/nav-item-wrapper.tsx +107 -0
  266. package/src/lib/components/layout/nav-main.tsx +200 -111
  267. package/src/lib/components/layout/nav-projects.tsx +2 -2
  268. package/src/lib/components/layout/nav-user.tsx +7 -7
  269. package/src/lib/components/layout/prerelease-popup.tsx +1 -1
  270. package/src/lib/components/login/login-form.tsx +85 -50
  271. package/src/lib/components/shared/alerts.tsx +3 -3
  272. package/src/lib/components/shared/animated-number.tsx +2 -2
  273. package/src/lib/components/shared/asset/asset-bulk-actions.tsx +109 -0
  274. package/src/lib/components/shared/asset/asset-focal-point-editor.tsx +29 -19
  275. package/src/lib/components/shared/asset/asset-gallery.tsx +25 -20
  276. package/src/lib/components/shared/asset/asset-picker-dialog.tsx +63 -66
  277. package/src/lib/components/shared/asset/asset-preview-dialog.tsx +3 -7
  278. package/src/lib/components/shared/asset/asset-preview-selector.tsx +4 -6
  279. package/src/lib/components/shared/asset/asset-preview.tsx +7 -21
  280. package/src/lib/components/shared/asset/asset-properties.tsx +7 -9
  281. package/src/lib/components/shared/asset/focal-point-control.tsx +5 -13
  282. package/src/lib/components/shared/assign-to-channel-bulk-action.tsx +71 -0
  283. package/src/lib/components/shared/assign-to-channel-dialog.tsx +155 -0
  284. package/src/lib/components/shared/assigned-facet-values.tsx +1 -5
  285. package/src/lib/components/shared/channel-code-label.tsx +3 -4
  286. package/src/lib/components/shared/channel-selector.tsx +6 -6
  287. package/src/lib/components/shared/configurable-operation-arg-input.tsx +367 -23
  288. package/src/lib/components/shared/configurable-operation-input.tsx +87 -48
  289. package/src/lib/components/shared/configurable-operation-multi-selector.tsx +260 -0
  290. package/src/lib/components/shared/configurable-operation-selector.tsx +156 -0
  291. package/src/lib/components/shared/confirmation-dialog.tsx +2 -2
  292. package/src/lib/components/shared/copyable-text.tsx +3 -4
  293. package/src/lib/components/shared/country-selector.tsx +21 -18
  294. package/src/lib/components/shared/currency-selector.tsx +5 -5
  295. package/src/lib/components/shared/custom-fields-form.tsx +423 -50
  296. package/src/lib/components/shared/customer-address-form.tsx +18 -13
  297. package/src/lib/components/shared/customer-group-selector.tsx +6 -6
  298. package/src/lib/components/shared/customer-selector.tsx +24 -18
  299. package/src/lib/components/shared/detail-page-button.tsx +45 -1
  300. package/src/lib/components/shared/entity-assets.tsx +33 -34
  301. package/src/lib/components/shared/error-page.tsx +6 -6
  302. package/src/lib/components/shared/facet-value-chip.tsx +12 -5
  303. package/src/lib/components/shared/facet-value-selector.tsx +64 -63
  304. package/src/lib/components/shared/form-field-wrapper.tsx +47 -24
  305. package/src/lib/components/shared/history-timeline/history-entry-date.tsx +37 -0
  306. package/src/lib/components/shared/history-timeline/history-entry.tsx +146 -70
  307. package/src/lib/components/shared/history-timeline/history-note-checkbox.tsx +3 -3
  308. package/src/lib/components/shared/history-timeline/history-note-editor.tsx +4 -4
  309. package/src/lib/components/shared/history-timeline/history-note-input.tsx +7 -7
  310. package/src/lib/components/shared/history-timeline/history-timeline.tsx +8 -48
  311. package/src/lib/components/shared/language-selector.tsx +5 -5
  312. package/src/lib/components/shared/logo-mark.tsx +2 -2
  313. package/src/lib/components/shared/multi-select.tsx +6 -6
  314. package/src/lib/components/shared/navigation-confirmation.tsx +24 -9
  315. package/src/lib/components/shared/option-value-input.tsx +18 -16
  316. package/src/lib/components/shared/paginated-list-data-table.tsx +35 -230
  317. package/src/lib/components/shared/permission-guard.tsx +4 -4
  318. package/src/lib/components/shared/product-variant-selector.tsx +59 -34
  319. package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +90 -0
  320. package/src/lib/components/shared/role-code-label.tsx +10 -6
  321. package/src/lib/components/shared/role-selector.tsx +4 -4
  322. package/src/lib/components/shared/seller-selector.tsx +21 -17
  323. package/src/lib/components/shared/stock-level-label.tsx +5 -5
  324. package/src/lib/components/shared/tax-category-selector.tsx +5 -5
  325. package/src/lib/components/shared/translatable-form-field.tsx +46 -23
  326. package/src/lib/components/shared/vendure-image.tsx +31 -2
  327. package/src/lib/components/shared/zone-selector.tsx +5 -6
  328. package/src/lib/components/ui/accordion.tsx +3 -3
  329. package/src/lib/components/ui/alert-dialog.tsx +10 -10
  330. package/src/lib/components/ui/alert.tsx +3 -3
  331. package/src/lib/components/ui/aspect-ratio.tsx +9 -0
  332. package/src/lib/components/ui/badge.tsx +2 -2
  333. package/src/lib/components/ui/breadcrumb.tsx +4 -4
  334. package/src/lib/components/ui/button.tsx +10 -3
  335. package/src/lib/components/ui/calendar.tsx +392 -459
  336. package/src/lib/components/ui/card.tsx +2 -2
  337. package/src/lib/components/ui/carousel.tsx +241 -0
  338. package/src/lib/components/ui/chart.tsx +351 -0
  339. package/src/lib/components/ui/checkbox.tsx +2 -2
  340. package/src/lib/components/ui/command.tsx +12 -6
  341. package/src/lib/components/ui/context-menu.tsx +252 -0
  342. package/src/lib/components/ui/dialog.tsx +2 -2
  343. package/src/lib/components/ui/drawer.tsx +133 -0
  344. package/src/lib/components/ui/dropdown-menu.tsx +7 -7
  345. package/src/lib/components/ui/form.tsx +8 -8
  346. package/src/lib/components/ui/hover-card.tsx +3 -3
  347. package/src/lib/components/ui/input-otp.tsx +77 -0
  348. package/src/lib/components/ui/input.tsx +1 -1
  349. package/src/lib/components/ui/label.tsx +2 -2
  350. package/src/lib/components/ui/menubar.tsx +274 -0
  351. package/src/lib/components/ui/navigation-menu.tsx +168 -0
  352. package/src/lib/components/ui/pagination.tsx +87 -108
  353. package/src/lib/components/ui/popover.tsx +3 -3
  354. package/src/lib/components/ui/progress.tsx +29 -0
  355. package/src/lib/components/ui/radio-group.tsx +45 -0
  356. package/src/lib/components/ui/resizable.tsx +54 -0
  357. package/src/lib/components/ui/scroll-area.tsx +2 -2
  358. package/src/lib/components/ui/select.tsx +151 -129
  359. package/src/lib/components/ui/separator.tsx +2 -2
  360. package/src/lib/components/ui/sheet.tsx +5 -5
  361. package/src/lib/components/ui/sidebar.tsx +10 -10
  362. package/src/lib/components/ui/skeleton.tsx +1 -1
  363. package/src/lib/components/ui/slider.tsx +63 -0
  364. package/src/lib/components/ui/switch.tsx +2 -2
  365. package/src/lib/components/ui/table.tsx +2 -2
  366. package/src/lib/components/ui/tabs.tsx +3 -3
  367. package/src/lib/components/ui/textarea.tsx +1 -1
  368. package/src/lib/components/ui/toggle-group.tsx +73 -0
  369. package/src/lib/components/ui/toggle.tsx +45 -0
  370. package/src/lib/components/ui/tooltip.tsx +3 -3
  371. package/src/lib/framework/alert/alert-extensions.tsx +2 -3
  372. package/src/lib/framework/alert/alert-item.tsx +5 -3
  373. package/src/lib/framework/component-registry/component-registry.tsx +33 -47
  374. package/src/lib/framework/component-registry/dynamic-component.tsx +3 -3
  375. package/src/lib/framework/dashboard-widget/base-widget.tsx +5 -13
  376. package/src/lib/framework/dashboard-widget/latest-orders-widget/index.tsx +8 -8
  377. package/src/lib/framework/dashboard-widget/latest-orders-widget/latest-orders-widget.graphql.ts +1 -1
  378. package/src/lib/framework/dashboard-widget/metrics-widget/index.tsx +7 -8
  379. package/src/lib/framework/dashboard-widget/metrics-widget/metrics-widget.graphql.ts +1 -1
  380. package/src/lib/framework/dashboard-widget/orders-summary/index.tsx +7 -8
  381. package/src/lib/framework/dashboard-widget/orders-summary/order-summary-widget.graphql.ts +1 -1
  382. package/src/lib/framework/dashboard-widget/widget-extensions.tsx +1 -1
  383. package/src/lib/framework/data-table/data-table-extensions.ts +35 -0
  384. package/src/lib/framework/defaults.ts +5 -11
  385. package/src/lib/framework/document-extension/extend-detail-form-query.ts +50 -0
  386. package/src/lib/framework/document-extension/extend-document.spec.ts +884 -0
  387. package/src/lib/framework/document-extension/extend-document.ts +159 -0
  388. package/src/lib/framework/document-introspection/add-custom-fields.ts +50 -2
  389. package/src/lib/framework/document-introspection/get-document-structure.spec.ts +321 -2
  390. package/src/lib/framework/document-introspection/get-document-structure.ts +187 -36
  391. package/src/lib/framework/document-introspection/hooks.ts +4 -1
  392. package/src/lib/framework/extension-api/define-dashboard-extension.ts +35 -49
  393. package/src/lib/framework/extension-api/display-component-extensions.tsx +69 -0
  394. package/src/lib/framework/extension-api/extension-api-types.ts +34 -98
  395. package/src/lib/framework/extension-api/input-component-extensions.tsx +73 -0
  396. package/src/lib/framework/extension-api/logic/alerts.ts +10 -0
  397. package/src/lib/framework/extension-api/logic/data-table.ts +60 -0
  398. package/src/lib/framework/extension-api/logic/detail-forms.ts +48 -0
  399. package/src/lib/framework/extension-api/logic/form-components.ts +13 -0
  400. package/src/lib/framework/extension-api/logic/index.ts +9 -0
  401. package/src/lib/framework/extension-api/logic/layout.ts +22 -0
  402. package/src/lib/framework/extension-api/logic/login.ts +17 -0
  403. package/src/lib/framework/extension-api/logic/navigation.ts +38 -0
  404. package/src/lib/framework/extension-api/logic/widgets.ts +10 -0
  405. package/src/lib/framework/extension-api/types/alerts.ts +54 -0
  406. package/src/lib/framework/extension-api/types/data-table.ts +96 -0
  407. package/src/lib/framework/extension-api/types/detail-forms.ts +94 -0
  408. package/src/lib/framework/extension-api/types/form-components.ts +43 -0
  409. package/src/lib/framework/extension-api/types/index.ts +9 -0
  410. package/src/lib/framework/extension-api/types/layout.ts +90 -0
  411. package/src/lib/framework/extension-api/types/login.ts +96 -0
  412. package/src/lib/framework/extension-api/types/navigation.ts +76 -0
  413. package/src/lib/framework/extension-api/types/widgets.ts +93 -0
  414. package/src/lib/framework/extension-api/use-dashboard-extensions.ts +2 -1
  415. package/src/lib/framework/extension-api/use-login-extensions.ts +26 -0
  416. package/src/lib/framework/form-engine/custom-form-component-extensions.ts +38 -0
  417. package/src/lib/framework/form-engine/custom-form-component.tsx +33 -0
  418. package/src/lib/framework/form-engine/form-schema-tools.spec.ts +472 -0
  419. package/src/lib/framework/form-engine/form-schema-tools.ts +341 -6
  420. package/src/lib/framework/form-engine/overridden-form-component.tsx +51 -0
  421. package/src/lib/framework/form-engine/use-generated-form.tsx +82 -24
  422. package/src/lib/framework/form-engine/utils.spec.ts +37 -0
  423. package/src/lib/framework/form-engine/utils.ts +99 -0
  424. package/src/lib/framework/layout-engine/dev-mode-button.tsx +24 -0
  425. package/src/lib/framework/layout-engine/layout-extensions.ts +1 -4
  426. package/src/lib/framework/layout-engine/location-wrapper.tsx +98 -72
  427. package/src/lib/framework/layout-engine/page-block-provider.tsx +6 -0
  428. package/src/lib/framework/layout-engine/page-layout.tsx +135 -58
  429. package/src/lib/framework/page/detail-page-route-loader.tsx +26 -7
  430. package/src/lib/framework/page/detail-page.tsx +94 -37
  431. package/src/lib/framework/page/list-page.tsx +18 -11
  432. package/src/lib/framework/page/use-detail-page.ts +47 -13
  433. package/src/lib/framework/page/use-extended-router.tsx +4 -5
  434. package/src/lib/framework/registry/global-registry.ts +4 -0
  435. package/src/lib/framework/registry/registry-types.ts +17 -5
  436. package/src/lib/graphql/api.ts +25 -3
  437. package/src/lib/graphql/common-operations.ts +18 -0
  438. package/src/lib/graphql/{fragments.tsx → fragments.ts} +1 -2
  439. package/src/lib/graphql/graphql-env.d.ts +27 -24
  440. package/src/lib/graphql/settings-store-operations.ts +17 -0
  441. package/src/lib/hooks/use-auth.tsx +1 -1
  442. package/src/lib/hooks/use-channel.ts +1 -1
  443. package/src/lib/hooks/use-extended-detail-query.ts +37 -0
  444. package/src/lib/hooks/use-extended-list-query.ts +75 -0
  445. package/src/lib/hooks/use-floating-bulk-actions.ts +82 -0
  446. package/src/lib/hooks/use-grouped-permissions.ts +3 -2
  447. package/src/lib/hooks/use-local-format.ts +20 -5
  448. package/src/lib/hooks/use-page-block.tsx +18 -0
  449. package/src/lib/hooks/use-page.tsx +2 -2
  450. package/src/lib/hooks/use-permissions.ts +3 -2
  451. package/src/lib/hooks/use-server-config.ts +1 -1
  452. package/src/lib/hooks/use-theme.ts +1 -1
  453. package/src/lib/hooks/use-user-settings.tsx +1 -1
  454. package/src/lib/index.ts +85 -7
  455. package/src/lib/lib/trans.tsx +3 -3
  456. package/src/lib/lib/utils.ts +52 -1
  457. package/src/lib/providers/auth.tsx +37 -14
  458. package/src/lib/providers/channel-provider.tsx +17 -15
  459. package/src/lib/providers/server-config.tsx +13 -11
  460. package/src/lib/providers/theme-provider.tsx +2 -3
  461. package/src/lib/providers/user-settings.tsx +78 -3
  462. package/src/lib/virtual.d.ts +26 -2
  463. package/src/vite-env.d.ts +2 -0
  464. package/vite/tests/barrel-exports.spec.ts +30 -0
  465. package/vite/tests/fixtures-barrel-exports/my-plugin/index.ts +1 -0
  466. package/vite/tests/fixtures-barrel-exports/my-plugin/src/my.plugin.ts +8 -0
  467. package/vite/tests/fixtures-barrel-exports/package.json +6 -0
  468. package/vite/tests/fixtures-barrel-exports/vendure-config.ts +19 -0
  469. package/vite/tests/fixtures-npm-plugin/fake_node_modules/test-plugin/index.js +20 -0
  470. package/vite/tests/fixtures-npm-plugin/fake_node_modules/test-plugin/package.json +8 -0
  471. package/vite/tests/fixtures-npm-plugin/package.json +6 -0
  472. package/vite/tests/fixtures-npm-plugin/vendure-config.ts +18 -0
  473. package/vite/tests/fixtures-path-alias/js-aliased/index.ts +1 -0
  474. package/vite/tests/fixtures-path-alias/js-aliased/src/js-aliased.plugin.ts +8 -0
  475. package/vite/tests/fixtures-path-alias/package.json +6 -0
  476. package/vite/tests/fixtures-path-alias/star-aliased/index.ts +1 -0
  477. package/vite/tests/fixtures-path-alias/star-aliased/src/star-aliased.plugin.ts +8 -0
  478. package/vite/tests/fixtures-path-alias/ts-aliased/index.ts +1 -0
  479. package/vite/tests/fixtures-path-alias/ts-aliased/src/ts-aliased.plugin.ts +8 -0
  480. package/vite/tests/fixtures-path-alias/vendure-config.ts +20 -0
  481. package/vite/tests/npm-plugin.spec.ts +46 -0
  482. package/vite/tests/path-alias.spec.ts +61 -0
  483. package/vite/tests/tsconfig.json +21 -0
  484. package/vite/types.ts +44 -0
  485. package/vite/utils/ast-utils.spec.ts +1 -80
  486. package/vite/utils/ast-utils.ts +0 -86
  487. package/vite/utils/compiler.ts +244 -0
  488. package/vite/utils/config-loader.ts +0 -445
  489. package/vite/utils/logger.ts +43 -0
  490. package/vite/utils/plugin-discovery.ts +494 -0
  491. package/vite/utils/tsconfig-utils.ts +79 -0
  492. package/vite/utils/ui-config.ts +30 -42
  493. package/vite/vite-plugin-config-loader.ts +25 -17
  494. package/vite/vite-plugin-config.ts +6 -7
  495. package/vite/vite-plugin-dashboard-metadata.ts +27 -16
  496. package/vite/vite-plugin-tailwind-source.ts +81 -0
  497. package/vite/vite-plugin-theme.ts +69 -69
  498. package/vite/vite-plugin-ui-config.ts +119 -17
  499. package/vite/vite-plugin-vendure-dashboard.ts +60 -16
  500. package/dist/plugin/utils/ast-utils.js +0 -96
  501. package/dist/plugin/utils/ast-utils.spec.js +0 -120
  502. package/dist/plugin/utils/config-loader.d.ts +0 -52
  503. package/dist/plugin/utils/config-loader.js +0 -343
  504. package/dist/plugin/utils/ui-config.d.ts +0 -3
  505. package/dist/plugin/utils/ui-config.js +0 -34
  506. package/dist/plugin/vite-plugin-theme.js +0 -130
  507. package/dist/plugin/vite-plugin-ui-config.d.ts +0 -15
  508. package/dist/plugin/vite-plugin-vendure-dashboard.d.ts +0 -44
  509. package/src/lib/components/shared/rich-text-editor.tsx +0 -0
  510. package/src/lib/framework/alert/types.ts +0 -13
  511. package/src/lib/framework/dashboard-widget/types.ts +0 -22
  512. /package/dist/{plugin/utils/ast-utils.spec.d.ts → vite/types.js} +0 -0
  513. /package/dist/{plugin → vite}/utils/schema-generator.d.ts +0 -0
  514. /package/dist/{plugin → vite}/utils/schema-generator.js +0 -0
  515. /package/dist/{plugin → vite}/vite-plugin-admin-api-schema.d.ts +0 -0
  516. /package/dist/{plugin → vite}/vite-plugin-admin-api-schema.js +0 -0
  517. /package/dist/{plugin → vite}/vite-plugin-config.d.ts +0 -0
  518. /package/dist/{plugin → vite}/vite-plugin-gql-tada.d.ts +0 -0
  519. /package/dist/{plugin → vite}/vite-plugin-gql-tada.js +0 -0
  520. /package/dist/{plugin → vite}/vite-plugin-theme.d.ts +0 -0
  521. /package/dist/{plugin → vite}/vite-plugin-transform-index.d.ts +0 -0
  522. /package/dist/{plugin → vite}/vite-plugin-transform-index.js +0 -0
  523. /package/src/lib/components/data-table/{data-table-types.ts → types.ts} +0 -0
@@ -0,0 +1,99 @@
1
+ import { FieldInfo } from '../document-introspection/get-document-structure.js';
2
+
3
+ /**
4
+ * Transforms relation fields in an entity, extracting IDs from relation objects.
5
+ * This is primarily used for custom fields of type "ID".
6
+ *
7
+ * @param fields - Array of field information
8
+ * @param entity - The entity to transform
9
+ * @returns A new entity with transformed relation fields
10
+ */
11
+ export function transformRelationFields<E extends Record<string, any>>(fields: FieldInfo[], entity: E): E {
12
+ // Create a shallow copy to avoid mutating the original entity
13
+ const processedEntity = { ...entity, customFields: { ...(entity.customFields ?? {}) } };
14
+
15
+ // Skip processing if there are no custom fields
16
+ if (!entity.customFields || !processedEntity.customFields) {
17
+ return processedEntity;
18
+ }
19
+
20
+ // Find the customFields field info
21
+ const customFieldsInfo = fields.find(field => field.name === 'customFields' && field.typeInfo);
22
+ if (!customFieldsInfo?.typeInfo) {
23
+ return processedEntity;
24
+ }
25
+
26
+ // Process only ID type custom fields
27
+ const idTypeCustomFields = customFieldsInfo.typeInfo.filter(field => field.type === 'ID');
28
+
29
+ for (const customField of idTypeCustomFields) {
30
+ const relationField = customField.name;
31
+
32
+ if (customField.list) {
33
+ // For list fields, the accessor is the field name without the "Ids" suffix
34
+ const propertyAccessorKey = customField.name.replace(/Ids$/, '');
35
+ const relationValue = entity.customFields[propertyAccessorKey];
36
+
37
+ if (relationValue) {
38
+ const relationIdValue = relationValue.map((v: { id: string }) => v.id);
39
+ if (relationIdValue && relationIdValue.length > 0) {
40
+ processedEntity.customFields[relationField] = relationIdValue;
41
+ }
42
+ }
43
+ } else {
44
+ // For single fields, the accessor is the field name without the "Id" suffix
45
+ const propertyAccessorKey = customField.name.replace(/Id$/, '');
46
+ const relationValue = entity.customFields[propertyAccessorKey];
47
+ processedEntity.customFields[relationField] = relationValue?.id;
48
+ delete processedEntity.customFields[propertyAccessorKey];
49
+ }
50
+ }
51
+ return processedEntity;
52
+ }
53
+
54
+ /**
55
+ * @description
56
+ * Due to the schema types, sometimes "create" mutations will have a default empty "id"
57
+ * field which can cause issues if we actually send them with a "create" mutation to the server.
58
+ * This function deletes any empty ID fields on the entity or its nested objects.
59
+ */
60
+ export function removeEmptyIdFields<T extends Record<string, any>>(values: T, fields: FieldInfo[]): T {
61
+ if (!values) {
62
+ return values;
63
+ }
64
+
65
+ // Create a deep copy to avoid mutating the original values
66
+ const result = structuredClone(values);
67
+
68
+ function recursiveRemove(obj: any, fieldDefs: FieldInfo[]) {
69
+ if (Array.isArray(obj)) {
70
+ for (const item of obj) {
71
+ recursiveRemove(item, fieldDefs);
72
+ }
73
+ } else if (typeof obj === 'object' && obj !== null) {
74
+ for (const field of fieldDefs) {
75
+ // Remove empty string ID fields at this level
76
+ if (field.type === 'ID' && typeof obj[field.name] === 'string' && obj[field.name] === '') {
77
+ delete obj[field.name];
78
+ }
79
+ // If the field is an object or array, recurse into it
80
+ if (Array.isArray(obj[field.name])) {
81
+ if (field.typeInfo) {
82
+ for (const item of obj[field.name]) {
83
+ recursiveRemove(item, field.typeInfo);
84
+ }
85
+ }
86
+ } else if (
87
+ typeof obj[field.name] === 'object' &&
88
+ obj[field.name] !== null &&
89
+ field.typeInfo
90
+ ) {
91
+ recursiveRemove(obj[field.name], field.typeInfo);
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ recursiveRemove(result, fields);
98
+ return result;
99
+ }
@@ -0,0 +1,24 @@
1
+ import { Button } from '@/vdb/components/ui/button.js';
2
+ import { cn } from '@/vdb/lib/utils.js';
3
+ import { CodeXmlIcon } from 'lucide-react';
4
+ import { forwardRef } from 'react';
5
+
6
+ export const DevModeButton = forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(
7
+ (props, ref) => {
8
+ const { className, ...rest } = props;
9
+ return (
10
+ <Button
11
+ ref={ref}
12
+ variant="secondary"
13
+ size="icon"
14
+ className={cn(
15
+ 'h-8 w-8 rounded-full bg-dev-mode/20 hover:bg-dev-mode/30 border border-dev-mode/20 shadow-sm',
16
+ className,
17
+ )}
18
+ {...rest}
19
+ >
20
+ <CodeXmlIcon className="text-dev-mode w-4 h-4" />
21
+ </Button>
22
+ );
23
+ },
24
+ );
@@ -1,7 +1,4 @@
1
- import {
2
- DashboardActionBarItem,
3
- DashboardPageBlockDefinition,
4
- } from '../extension-api/extension-api-types.js';
1
+ import { DashboardActionBarItem, DashboardPageBlockDefinition } from '../extension-api/types/layout.js';
5
2
  import { globalRegistry } from '../registry/global-registry.js';
6
3
 
7
4
  globalRegistry.register('dashboardActionBarItemRegistry', new Map<string, DashboardActionBarItem[]>());
@@ -1,95 +1,121 @@
1
- import { CopyableText } from '@/components/shared/copyable-text.js';
2
- import { Button } from '@/components/ui/button.js';
3
- import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover.js';
4
- import { usePage } from '@/hooks/use-page.js';
5
- import { useUserSettings } from '@/hooks/use-user-settings.js';
6
- import { cn } from '@/lib/utils.js';
7
- import { Trans } from '@/lib/trans.js';
8
- import { CodeXmlIcon, InfoIcon } from 'lucide-react';
9
- import { createContext, useContext, useState } from 'react';
1
+ import { CopyableText } from '@/vdb/components/shared/copyable-text.js';
2
+ import { Popover, PopoverContent, PopoverTrigger } from '@/vdb/components/ui/popover.js';
3
+ import { usePageBlock } from '@/vdb/hooks/use-page-block.js';
4
+ import { usePage } from '@/vdb/hooks/use-page.js';
5
+ import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
6
+ import { cn } from '@/vdb/lib/utils.js';
7
+ import React, { useEffect, useState } from 'react';
8
+ import { DevModeButton } from './dev-mode-button.js';
10
9
 
11
- const LocationWrapperContext = createContext<{
12
- parentId: string | null;
13
- hoveredId: string | null;
14
- setHoveredId: ((id: string | null) => void) | null;
15
- }>({
16
- parentId: null,
17
- hoveredId: null,
18
- setHoveredId: null,
19
- });
10
+ // Singleton state for hover tracking
11
+ let globalHoveredId: string | null = null;
12
+ const hoverListeners: Set<(id: string | null) => void> = new Set();
20
13
 
21
- export function LocationWrapper({ children, blockId }: { children: React.ReactNode; blockId?: string }) {
14
+ const setGlobalHoveredId = (id: string | null) => {
15
+ globalHoveredId = id;
16
+ hoverListeners.forEach(listener => listener(id));
17
+ };
18
+
19
+ export interface LocationWrapperProps {
20
+ children: React.ReactNode;
21
+ identifier?: string;
22
+ }
23
+
24
+ export function LocationWrapper({ children, identifier }: Readonly<LocationWrapperProps>) {
22
25
  const page = usePage();
26
+ const pageBlock = usePageBlock({ optional: true });
23
27
  const { settings } = useUserSettings();
24
28
  const [isPopoverOpen, setIsPopoverOpen] = useState(false);
29
+ const blockId = pageBlock?.blockId ?? null;
25
30
  const isPageWrapper = !blockId;
26
31
 
27
- const [hoveredIdTopLevel, setHoveredIdTopLevel] = useState<string | null>(null);
28
- const { hoveredId, setHoveredId, parentId } = useContext(LocationWrapperContext);
29
- const id = `${page.pageId}-${blockId ?? 'page'}`;
30
- const isHovered = hoveredId === id || hoveredIdTopLevel === id;
32
+ const [hoveredId, setHoveredId] = useState<string | null>(globalHoveredId);
33
+ const id = `${page.pageId}-${blockId ?? 'page'}-${identifier ?? ''}`;
34
+ const isHovered = hoveredId === id;
35
+
36
+ // Subscribe to global hover changes
37
+ useEffect(() => {
38
+ const listener = (newHoveredId: string | null) => {
39
+ setHoveredId(newHoveredId);
40
+ };
41
+ hoverListeners.add(listener);
42
+ return () => {
43
+ hoverListeners.delete(listener);
44
+ };
45
+ }, []);
31
46
 
32
47
  const setHoverId = (id: string | null) => {
33
- if (setHoveredId) {
34
- setHoveredId(id);
35
- } else {
36
- setHoveredIdTopLevel(id);
48
+ setGlobalHoveredId(id);
49
+ };
50
+
51
+ const handleMouseEnter = () => {
52
+ // Set this element as hovered
53
+ setHoverId(id);
54
+ };
55
+
56
+ const handleMouseLeave = () => {
57
+ // If we're at the top level (page wrapper), go to null
58
+ // If we're at block level, go to page level
59
+ // If we're at identifier level, go to block level
60
+ if (isPageWrapper) {
61
+ setHoverId(null);
62
+ } else if (blockId && !identifier) {
63
+ // Block level - go to page level
64
+ setHoverId(`${page.pageId}-page-`);
65
+ } else if (identifier) {
66
+ // Identifier level - go to block level
67
+ setHoverId(`${page.pageId}-${blockId}-`);
37
68
  }
38
69
  };
39
70
 
40
71
  if (settings.devMode) {
41
72
  const pageId = page.pageId;
42
73
  return (
43
- <LocationWrapperContext.Provider
44
- value={{ hoveredId: hoveredIdTopLevel, setHoveredId: setHoveredIdTopLevel, parentId: id }}
74
+ <div
75
+ className={cn(
76
+ `ring-2 transition-all ring-offset-4 ring-offset-background delay-50 relative`,
77
+ isHovered || isPopoverOpen ? 'ring-dev-mode' : 'ring-transparent',
78
+ isPageWrapper ? 'ring-offset-8' : '',
79
+ identifier ? 'rounded-md' : 'rounded',
80
+ )}
81
+ onMouseEnter={handleMouseEnter}
82
+ onMouseLeave={handleMouseLeave}
45
83
  >
46
84
  <div
47
- className={cn(
48
- `ring-2 rounded-xl transition-all delay-50 relative`,
49
- isHovered || isPopoverOpen ? 'ring-dev-mode' : 'ring-transparent',
50
- isPageWrapper ? 'ring-inset' : '',
51
- )}
52
- onMouseEnter={() => setHoverId(id)}
53
- onMouseLeave={() => setHoverId(parentId)}
85
+ className={`absolute top-1 right-1 transition-all delay-50 z-10 ${isHovered || isPopoverOpen ? 'visible' : 'invisible'}`}
54
86
  >
55
- <div
56
- className={`absolute top-0.5 right-0.5 transition-all delay-50 z-10 ${isHovered || isPopoverOpen ? 'visible' : 'invisible'}`}
57
- >
58
- <Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
59
- <PopoverTrigger asChild>
60
- <Button variant="ghost" size="icon" className="rounded-lg">
61
- <CodeXmlIcon className="text-dev-mode w-5 h-5" />
62
- </Button>
63
- </PopoverTrigger>
64
- <PopoverContent className="w-60">
65
- <div className="space-y-2">
66
- <div className="flex items-center gap-2">
67
- <InfoIcon className="h-4 w-4 text-dev-mode" />
68
- <span className="font-medium">
69
- <Trans>Location Details</Trans>
70
- </span>
71
- </div>
72
- <div className="space-y-1.5">
73
- {pageId && (
74
- <div>
75
- <div className="text-xs text-muted-foreground">pageId</div>
76
- <CopyableText text={pageId} />
77
- </div>
78
- )}
79
- {blockId && (
80
- <div>
81
- <div className="text-xs text-muted-foreground">blockId</div>
82
- <CopyableText text={blockId} />
83
- </div>
84
- )}
85
- </div>
87
+ <Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
88
+ <PopoverTrigger asChild>
89
+ <DevModeButton />
90
+ </PopoverTrigger>
91
+ <PopoverContent className="w-48 p-3">
92
+ <div className="space-y-2">
93
+ <div className="space-y-1">
94
+ {pageId && (
95
+ <div className="text-xs">
96
+ <div className="text-muted-foreground mb-0.5">pageId</div>
97
+ <CopyableText text={pageId} />
98
+ </div>
99
+ )}
100
+ {blockId && (
101
+ <div className="text-xs">
102
+ <div className="text-muted-foreground mb-0.5">blockId</div>
103
+ <CopyableText text={blockId} />
104
+ </div>
105
+ )}
106
+ {identifier && (
107
+ <div className="text-xs">
108
+ <div className="text-muted-foreground mb-0.5">identifier</div>
109
+ <CopyableText text={identifier} />
110
+ </div>
111
+ )}
86
112
  </div>
87
- </PopoverContent>
88
- </Popover>
89
- </div>
90
- {children}
113
+ </div>
114
+ </PopoverContent>
115
+ </Popover>
91
116
  </div>
92
- </LocationWrapperContext.Provider>
117
+ {children}
118
+ </div>
93
119
  );
94
120
  }
95
121
  return children;
@@ -0,0 +1,6 @@
1
+ import { PageBlockProps } from '@/vdb/framework/layout-engine/page-layout.js';
2
+ import { createContext } from 'react';
3
+
4
+ export type PageBlockContextValue = Pick<PageBlockProps, 'blockId' | 'column' | 'title' | 'description'>;
5
+
6
+ export const PageBlockContext = createContext<PageBlockContextValue | undefined>(undefined);
@@ -1,20 +1,24 @@
1
- import { CustomFieldsForm } from '@/components/shared/custom-fields-form.js';
2
- import { PermissionGuard } from '@/components/shared/permission-guard.js';
3
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.js';
4
- import { Form } from '@/components/ui/form.js';
5
- import { useCustomFieldConfig } from '@/hooks/use-custom-field-config.js';
6
- import { usePage } from '@/hooks/use-page.js';
7
- import { cn } from '@/lib/utils.js';
8
- import { NavigationConfirmation } from '@/components/shared/navigation-confirmation.js';
1
+ import { CustomFieldsForm } from '@/vdb/components/shared/custom-fields-form.js';
2
+ import { NavigationConfirmation } from '@/vdb/components/shared/navigation-confirmation.js';
3
+ import { PermissionGuard } from '@/vdb/components/shared/permission-guard.js';
4
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/vdb/components/ui/card.js';
5
+ import { Form } from '@/vdb/components/ui/form.js';
6
+ import { useCustomFieldConfig } from '@/vdb/hooks/use-custom-field-config.js';
7
+ import { usePage } from '@/vdb/hooks/use-page.js';
8
+ import { cn } from '@/vdb/lib/utils.js';
9
9
  import { useMediaQuery } from '@uidotdev/usehooks';
10
- import React, { ComponentProps } from 'react';
10
+ import { EllipsisVerticalIcon } from 'lucide-react';
11
+ import React, { ComponentProps, useMemo } from 'react';
11
12
  import { Control, UseFormReturn } from 'react-hook-form';
12
13
 
13
- import { DashboardActionBarItem } from '../extension-api/extension-api-types.js';
14
+ import { DashboardActionBarItem } from '../extension-api/types/layout.js';
14
15
 
16
+ import { Button } from '@/vdb/components/ui/button.js';
17
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@/vdb/components/ui/dropdown-menu.js';
18
+ import { PageBlockContext } from '@/vdb/framework/layout-engine/page-block-provider.js';
19
+ import { PageContext, PageContextValue } from '@/vdb/framework/layout-engine/page-provider.js';
15
20
  import { getDashboardActionBarItems, getDashboardPageBlocks } from './layout-extensions.js';
16
21
  import { LocationWrapper } from './location-wrapper.js';
17
- import { PageContext, PageContextValue } from '@/framework/layout-engine/page-provider.js';
18
22
 
19
23
  export interface PageProps extends ComponentProps<'div'> {
20
24
  pageId?: string;
@@ -41,13 +45,11 @@ export interface PageProps extends ComponentProps<'div'> {
41
45
  * @docsWeight 0
42
46
  * @since 3.3.0
43
47
  */
44
- export function Page({ children, pageId, entity, form, submitHandler, ...props }: PageProps) {
48
+ export function Page({ children, pageId, entity, form, submitHandler, ...props }: Readonly<PageProps>) {
45
49
  const childArray = React.Children.toArray(children);
46
50
 
47
51
  const pageTitle = childArray.find(child => React.isValidElement(child) && child.type === PageTitle);
48
- const pageActionBar = childArray.find(
49
- child => isOfType(child, PageActionBar),
50
- );
52
+ const pageActionBar = childArray.find(child => isOfType(child, PageActionBar));
51
53
 
52
54
  const pageContent = childArray.filter(
53
55
  child => !isOfType(child, PageTitle) && !isOfType(child, PageActionBar),
@@ -73,7 +75,13 @@ export function Page({ children, pageId, entity, form, submitHandler, ...props }
73
75
  );
74
76
  }
75
77
 
76
- function PageContent({ pageHeader, pageContent, form, submitHandler, ...props }: {
78
+ function PageContent({
79
+ pageHeader,
80
+ pageContent,
81
+ form,
82
+ submitHandler,
83
+ ...props
84
+ }: {
77
85
  pageHeader: React.ReactNode;
78
86
  pageContent: React.ReactNode;
79
87
  form?: UseFormReturn<any>;
@@ -94,9 +102,14 @@ function PageContent({ pageHeader, pageContent, form, submitHandler, ...props }:
94
102
  );
95
103
  }
96
104
 
97
- export function PageContentWithOptionalForm({ form, pageHeader, pageContent, submitHandler }: {
105
+ export function PageContentWithOptionalForm({
106
+ form,
107
+ pageHeader,
108
+ pageContent,
109
+ submitHandler,
110
+ }: {
98
111
  form?: UseFormReturn<any>;
99
- pageHeader: React.ReactNode
112
+ pageHeader: React.ReactNode;
100
113
  pageContent: React.ReactNode;
101
114
  submitHandler?: any;
102
115
  }) {
@@ -154,7 +167,7 @@ function isPageBlock(child: unknown): child is React.ReactElement<PageBlockProps
154
167
  * @docsWeight 0
155
168
  * @since 3.3.0
156
169
  */
157
- export function PageLayout({ children, className }: PageLayoutProps) {
170
+ export function PageLayout({ children, className }: Readonly<PageLayoutProps>) {
158
171
  const page = usePage();
159
172
  const isDesktop = useMediaQuery('only screen and (min-width : 769px)');
160
173
  // Separate blocks into categories
@@ -184,6 +197,7 @@ export function PageLayout({ children, className }: PageLayoutProps) {
184
197
  if (extensionBlock) {
185
198
  const ExtensionBlock = (
186
199
  <PageBlock
200
+ key={childBlock.key}
187
201
  column={extensionBlock.location.column}
188
202
  blockId={extensionBlock.id}
189
203
  title={extensionBlock.title}
@@ -227,8 +241,8 @@ export function PageLayout({ children, className }: PageLayoutProps) {
227
241
  );
228
242
  }
229
243
 
230
- export function DetailFormGrid({ children }: { children: React.ReactNode }) {
231
- return <div className="md:grid md:grid-cols-2 gap-4 items-start mb-4">{children}</div>;
244
+ export function DetailFormGrid({ children }: Readonly<{ children: React.ReactNode }>) {
245
+ return <div className="grid @md:grid-cols-2 gap-6 items-start mb-6">{children}</div>;
232
246
  }
233
247
 
234
248
  /**
@@ -241,7 +255,7 @@ export function DetailFormGrid({ children }: { children: React.ReactNode }) {
241
255
  * @docsPage PageTitle
242
256
  * @since 3.3.0
243
257
  */
244
- export function PageTitle({ children }: { children: React.ReactNode }) {
258
+ export function PageTitle({ children }: Readonly<{ children: React.ReactNode }>) {
245
259
  return <h1 className="text-2xl font-semibold">{children}</h1>;
246
260
  }
247
261
 
@@ -258,15 +272,11 @@ export function PageTitle({ children }: { children: React.ReactNode }) {
258
272
  * @docsWeight 0
259
273
  * @since 3.3.0
260
274
  */
261
- export function PageActionBar({ children }: { children: React.ReactNode }) {
275
+ export function PageActionBar({ children }: Readonly<{ children: React.ReactNode }>) {
262
276
  let childArray = React.Children.toArray(children);
263
277
 
264
- const leftContent = childArray.filter(
265
- child => isOfType(child, PageActionBarLeft),
266
- );
267
- const rightContent = childArray.filter(
268
- child => isOfType(child, PageActionBarRight),
269
- );
278
+ const leftContent = childArray.filter(child => isOfType(child, PageActionBarLeft));
279
+ const rightContent = childArray.filter(child => isOfType(child, PageActionBarRight));
270
280
 
271
281
  return (
272
282
  <div className={cn('flex gap-2', leftContent.length > 0 ? 'justify-between' : 'justify-end')}>
@@ -284,10 +294,12 @@ export function PageActionBar({ children }: { children: React.ReactNode }) {
284
294
  * @docsPage PageActionBar
285
295
  * @since 3.3.0
286
296
  */
287
- export function PageActionBarLeft({ children }: { children: React.ReactNode }) {
297
+ export function PageActionBarLeft({ children }: Readonly<{ children: React.ReactNode }>) {
288
298
  return <div className="flex justify-start gap-2">{children}</div>;
289
299
  }
290
300
 
301
+ type InlineDropdownItem = Omit<DashboardActionBarItem, 'type' | 'pageId'>;
302
+
291
303
  /**
292
304
  * @description
293
305
  * **Status: Developer Preview**
@@ -296,20 +308,42 @@ export function PageActionBarLeft({ children }: { children: React.ReactNode }) {
296
308
  * @docsPage PageActionBar
297
309
  * @since 3.3.0
298
310
  */
299
- export function PageActionBarRight({ children }: { children: React.ReactNode }) {
311
+ export function PageActionBarRight({
312
+ children,
313
+ dropdownMenuItems,
314
+ }: Readonly<{
315
+ children: React.ReactNode;
316
+ dropdownMenuItems?: InlineDropdownItem[];
317
+ }>) {
300
318
  const page = usePage();
301
319
  const actionBarItems = page.pageId ? getDashboardActionBarItems(page.pageId) : [];
320
+ const actionBarButtonItems = actionBarItems.filter(item => item.type !== 'dropdown');
321
+ const actionBarDropdownItems = [
322
+ ...(dropdownMenuItems ?? []).map(item => ({
323
+ ...item,
324
+ pageId: page.pageId ?? '',
325
+ type: 'dropdown' as const,
326
+ })),
327
+ ...actionBarItems.filter(item => item.type === 'dropdown'),
328
+ ];
329
+
302
330
  return (
303
331
  <div className="flex justify-end gap-2">
304
- {actionBarItems.map((item, index) => (
305
- <PageActionBarItem key={index} item={item} page={page} />
332
+ {actionBarButtonItems.map((item, index) => (
333
+ <PageActionBarItem key={item.pageId + index} item={item} page={page} />
306
334
  ))}
307
335
  {children}
336
+ {actionBarDropdownItems.length > 0 && (
337
+ <PageActionBarDropdown items={actionBarDropdownItems} page={page} />
338
+ )}
308
339
  </div>
309
340
  );
310
341
  }
311
342
 
312
- function PageActionBarItem({ item, page }: { item: DashboardActionBarItem; page: PageContextValue }) {
343
+ function PageActionBarItem({
344
+ item,
345
+ page,
346
+ }: Readonly<{ item: DashboardActionBarItem; page: PageContextValue }>) {
313
347
  return (
314
348
  <PermissionGuard requires={item.requiresPermission ?? []}>
315
349
  <item.component context={page} />
@@ -317,6 +351,28 @@ function PageActionBarItem({ item, page }: { item: DashboardActionBarItem; page:
317
351
  );
318
352
  }
319
353
 
354
+ function PageActionBarDropdown({
355
+ items,
356
+ page,
357
+ }: Readonly<{ items: DashboardActionBarItem[]; page: PageContextValue }>) {
358
+ return (
359
+ <DropdownMenu>
360
+ <DropdownMenuTrigger asChild>
361
+ <Button variant="ghost" size="icon">
362
+ <EllipsisVerticalIcon className="w-4 h-4" />
363
+ </Button>
364
+ </DropdownMenuTrigger>
365
+ <DropdownMenuContent>
366
+ {items.map((item, index) => (
367
+ <PermissionGuard key={item.pageId + index} requires={item.requiresPermission ?? []}>
368
+ <item.component context={page} />
369
+ </PermissionGuard>
370
+ ))}
371
+ </DropdownMenuContent>
372
+ </DropdownMenu>
373
+ );
374
+ }
375
+
320
376
  /**
321
377
  * @description
322
378
  * **Status: Developer Preview**
@@ -348,19 +404,37 @@ export type PageBlockProps = {
348
404
  * @docsWeight 0
349
405
  * @since 3.3.0
350
406
  */
351
- export function PageBlock({ children, title, description, className, blockId }: PageBlockProps) {
407
+ export function PageBlock({
408
+ children,
409
+ title,
410
+ description,
411
+ className,
412
+ blockId,
413
+ column,
414
+ }: Readonly<PageBlockProps>) {
415
+ const contextValue = useMemo(
416
+ () => ({
417
+ blockId,
418
+ title,
419
+ description,
420
+ column,
421
+ }),
422
+ [blockId, title, description, column],
423
+ );
352
424
  return (
353
- <LocationWrapper blockId={blockId}>
354
- <Card className={cn('w-full', className)}>
355
- {title || description ? (
356
- <CardHeader>
357
- {title && <CardTitle>{title}</CardTitle>}
358
- {description && <CardDescription>{description}</CardDescription>}
359
- </CardHeader>
360
- ) : null}
361
- <CardContent className={cn(!title ? 'pt-6' : '')}>{children}</CardContent>
362
- </Card>
363
- </LocationWrapper>
425
+ <PageBlockContext.Provider value={contextValue}>
426
+ <LocationWrapper>
427
+ <Card className={cn('@container w-full', className)}>
428
+ {title || description ? (
429
+ <CardHeader>
430
+ {title && <CardTitle>{title}</CardTitle>}
431
+ {description && <CardDescription>{description}</CardDescription>}
432
+ </CardHeader>
433
+ ) : null}
434
+ <CardContent className={cn(!title ? 'pt-6' : '')}>{children}</CardContent>
435
+ </Card>
436
+ </LocationWrapper>
437
+ </PageBlockContext.Provider>
364
438
  );
365
439
  }
366
440
 
@@ -376,14 +450,17 @@ export function PageBlock({ children, title, description, className, blockId }:
376
450
  * @since 3.3.0
377
451
  */
378
452
  export function FullWidthPageBlock({
379
- children,
380
- className,
381
- blockId,
382
- }: Pick<PageBlockProps, 'children' | 'className' | 'blockId'>) {
453
+ children,
454
+ className,
455
+ blockId,
456
+ }: Readonly<Pick<PageBlockProps, 'children' | 'className' | 'blockId'>>) {
457
+ const contextValue = useMemo(() => ({ blockId, column: 'main' as const }), [blockId]);
383
458
  return (
384
- <LocationWrapper blockId={blockId}>
385
- <div className={cn('w-full', className)}>{children}</div>
386
- </LocationWrapper>
459
+ <PageBlockContext.Provider value={contextValue}>
460
+ <LocationWrapper>
461
+ <div className={cn('w-full', className)}>{children}</div>
462
+ </LocationWrapper>
463
+ </PageBlockContext.Provider>
387
464
  );
388
465
  }
389
466
 
@@ -398,14 +475,14 @@ export function FullWidthPageBlock({
398
475
  * @since 3.3.0
399
476
  */
400
477
  export function CustomFieldsPageBlock({
401
- column,
402
- entityType,
403
- control,
404
- }: {
478
+ column,
479
+ entityType,
480
+ control,
481
+ }: Readonly<{
405
482
  column: 'main' | 'side';
406
483
  entityType: string;
407
484
  control: Control<any, any>;
408
- }) {
485
+ }>) {
409
486
  const customFieldConfig = useCustomFieldConfig(entityType);
410
487
  if (!customFieldConfig || customFieldConfig.length === 0) {
411
488
  return null;