@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
@@ -2,27 +2,349 @@ import {
2
2
  FieldInfo,
3
3
  isEnumType,
4
4
  isScalarType,
5
- } from '@/framework/document-introspection/get-document-structure.js';
5
+ } from '@/vdb/framework/document-introspection/get-document-structure.js';
6
+ import { StructCustomFieldConfig } from '@vendure/common/lib/generated-types';
7
+ import { ResultOf } from 'gql.tada';
6
8
  import { z, ZodRawShape, ZodType, ZodTypeAny } from 'zod';
7
9
 
8
- export function createFormSchemaFromFields(fields: FieldInfo[]) {
10
+ import { structCustomFieldFragment } from '../../providers/server-config.js';
11
+
12
+ type CustomFieldConfig = {
13
+ name: string;
14
+ type: string;
15
+ pattern?: string;
16
+ intMin?: number;
17
+ intMax?: number;
18
+ floatMin?: number;
19
+ floatMax?: number;
20
+ datetimeMin?: string;
21
+ datetimeMax?: string;
22
+ list?: boolean;
23
+ nullable?: boolean;
24
+ };
25
+
26
+ type StructFieldConfig = ResultOf<typeof structCustomFieldFragment>['fields'][number];
27
+
28
+ function mapGraphQLCustomFieldToConfig(field: StructFieldConfig): CustomFieldConfig {
29
+ const baseConfig = {
30
+ name: field.name,
31
+ type: field.type,
32
+ list: field.list ?? false,
33
+ nullable: true, // Default to true since GraphQL fields are nullable by default
34
+ };
35
+
36
+ switch (field.__typename) {
37
+ case 'StringStructFieldConfig':
38
+ return {
39
+ ...baseConfig,
40
+ pattern: field.pattern ?? undefined,
41
+ };
42
+ case 'IntStructFieldConfig':
43
+ return {
44
+ ...baseConfig,
45
+ intMin: field.intMin ?? undefined,
46
+ intMax: field.intMax ?? undefined,
47
+ };
48
+ case 'FloatStructFieldConfig':
49
+ return {
50
+ ...baseConfig,
51
+ floatMin: field.floatMin ?? undefined,
52
+ floatMax: field.floatMax ?? undefined,
53
+ };
54
+ case 'DateTimeStructFieldConfig':
55
+ return {
56
+ ...baseConfig,
57
+ datetimeMin: field.datetimeMin ?? undefined,
58
+ datetimeMax: field.datetimeMax ?? undefined,
59
+ };
60
+ default:
61
+ return baseConfig;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Safely parses a date string into a Date object.
67
+ * Used for parsing datetime constraints in custom field validation.
68
+ *
69
+ * @param dateStr - The date string to parse
70
+ * @returns Parsed Date object or undefined if invalid
71
+ */
72
+ function parseDate(dateStr: string | undefined | null): Date | undefined {
73
+ if (!dateStr) return undefined;
74
+ const date = new Date(dateStr);
75
+ return isNaN(date.getTime()) ? undefined : date;
76
+ }
77
+
78
+ /**
79
+ * Creates a Zod validation schema for datetime fields with optional min/max constraints.
80
+ * Supports both string and Date inputs, which is common in form handling.
81
+ *
82
+ * @param minDate - Optional minimum date constraint
83
+ * @param maxDate - Optional maximum date constraint
84
+ * @returns Zod schema that validates date ranges
85
+ */
86
+ function createDateValidationSchema(minDate: Date | undefined, maxDate: Date | undefined): ZodType {
87
+ const baseSchema = z.union([z.string(), z.date()]);
88
+ if (!minDate && !maxDate) return baseSchema;
89
+
90
+ const dateMinString = minDate?.toLocaleDateString() ?? '';
91
+ const dateMaxString = maxDate?.toLocaleDateString() ?? '';
92
+ const dateMinMessage = minDate ? `Date must be after ${dateMinString}` : '';
93
+ const dateMaxMessage = maxDate ? `Date must be before ${dateMaxString}` : '';
94
+
95
+ return baseSchema.refine(
96
+ val => {
97
+ if (!val) return true;
98
+ const date = val instanceof Date ? val : new Date(val);
99
+ if (minDate && date < minDate) return false;
100
+ if (maxDate && date > maxDate) return false;
101
+ return true;
102
+ },
103
+ val => {
104
+ const date = val instanceof Date ? val : new Date(val);
105
+ if (minDate && date < minDate) return { message: dateMinMessage };
106
+ if (maxDate && date > maxDate) return { message: dateMaxMessage };
107
+ return { message: '' };
108
+ },
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Creates a Zod validation schema for string fields with optional regex pattern validation.
114
+ * Used for string-type custom fields that may have pattern constraints.
115
+ *
116
+ * @param pattern - Optional regex pattern string for validation
117
+ * @returns Zod string schema with optional pattern validation
118
+ */
119
+ function createStringValidationSchema(pattern?: string): ZodType {
120
+ let schema = z.string();
121
+ if (pattern) {
122
+ schema = schema.regex(new RegExp(pattern), {
123
+ message: `Value must match pattern: ${pattern}`,
124
+ });
125
+ }
126
+ return schema;
127
+ }
128
+
129
+ /**
130
+ * Creates a Zod validation schema for integer fields with optional min/max constraints.
131
+ * Used for int-type custom fields that may have numeric range limits.
132
+ *
133
+ * @param min - Optional minimum value constraint
134
+ * @param max - Optional maximum value constraint
135
+ * @returns Zod number schema with optional range validation
136
+ */
137
+ function createIntValidationSchema(min?: number, max?: number): ZodType {
138
+ let schema = z.number();
139
+ if (min !== undefined) {
140
+ schema = schema.min(min, {
141
+ message: `Value must be at least ${min}`,
142
+ });
143
+ }
144
+ if (max !== undefined) {
145
+ schema = schema.max(max, {
146
+ message: `Value must be at most ${max}`,
147
+ });
148
+ }
149
+ return schema;
150
+ }
151
+
152
+ /**
153
+ * Creates a Zod validation schema for float fields with optional min/max constraints.
154
+ * Used for float-type custom fields that may have numeric range limits.
155
+ *
156
+ * @param min - Optional minimum value constraint
157
+ * @param max - Optional maximum value constraint
158
+ * @returns Zod number schema with optional range validation
159
+ */
160
+ function createFloatValidationSchema(min?: number, max?: number): ZodType {
161
+ let schema = z.number();
162
+ if (min !== undefined) {
163
+ schema = schema.min(min, {
164
+ message: `Value must be at least ${min}`,
165
+ });
166
+ }
167
+ if (max !== undefined) {
168
+ schema = schema.max(max, {
169
+ message: `Value must be at most ${max}`,
170
+ });
171
+ }
172
+ return schema;
173
+ }
174
+
175
+ /**
176
+ * Creates a Zod validation schema for a single custom field based on its type and constraints.
177
+ * This is the main dispatcher that routes different custom field types to their specific
178
+ * validation schema creators. Handles all standard custom field types in Vendure.
179
+ *
180
+ * @param customField - The custom field configuration object
181
+ * @returns Zod schema appropriate for the custom field type
182
+ */
183
+ function createCustomFieldValidationSchema(customField: CustomFieldConfig): ZodType {
184
+ let zodType: ZodType;
185
+
186
+ switch (customField.type) {
187
+ case 'localeString':
188
+ case 'localeText':
189
+ case 'string':
190
+ zodType = createStringValidationSchema(customField.pattern);
191
+ break;
192
+ case 'int':
193
+ zodType = createIntValidationSchema(customField.intMin, customField.intMax);
194
+ break;
195
+ case 'float':
196
+ zodType = createFloatValidationSchema(customField.floatMin, customField.floatMax);
197
+ break;
198
+ case 'datetime': {
199
+ const minDate = parseDate(customField.datetimeMin);
200
+ const maxDate = parseDate(customField.datetimeMax);
201
+ zodType = createDateValidationSchema(minDate, maxDate);
202
+ break;
203
+ }
204
+ case 'boolean':
205
+ zodType = z.boolean();
206
+ break;
207
+ default:
208
+ zodType = z.any();
209
+ break;
210
+ }
211
+
212
+ return zodType;
213
+ }
214
+
215
+ /**
216
+ * Creates a Zod validation schema for struct-type custom fields.
217
+ * Struct fields contain nested sub-fields, each with their own validation rules.
218
+ * This recursively processes each sub-field to create a nested object schema.
219
+ *
220
+ * @param structFieldConfig - The struct custom field configuration with nested fields
221
+ * @returns Zod object schema representing the struct with all sub-field validations
222
+ */
223
+ function createStructFieldSchema(structFieldConfig: StructCustomFieldConfig): ZodType {
224
+ if (!structFieldConfig.fields || !Array.isArray(structFieldConfig.fields)) {
225
+ return z.object({}).passthrough();
226
+ }
227
+
228
+ const nestedSchema: ZodRawShape = {};
229
+ for (const structSubField of structFieldConfig.fields) {
230
+ const config = mapGraphQLCustomFieldToConfig(structSubField as StructFieldConfig);
231
+ let subFieldType = createCustomFieldValidationSchema(config);
232
+
233
+ // Handle list and nullable for struct sub-fields
234
+ if (config.list) {
235
+ subFieldType = z.array(subFieldType);
236
+ }
237
+ if (config.nullable) {
238
+ subFieldType = subFieldType.optional().nullable();
239
+ }
240
+
241
+ nestedSchema[config.name] = subFieldType;
242
+ }
243
+
244
+ return z.object(nestedSchema);
245
+ }
246
+
247
+ /**
248
+ * Applies common list and nullable modifiers to a Zod schema based on custom field configuration.
249
+ * Many custom fields can be configured as lists (arrays) and/or nullable, so this helper
250
+ * centralizes that logic to avoid duplication.
251
+ *
252
+ * @param zodType - The base Zod schema to modify
253
+ * @param customField - Custom field config containing list/nullable flags
254
+ * @returns Modified Zod schema with list/nullable modifiers applied
255
+ */
256
+ function applyListAndNullableModifiers(zodType: ZodType, customField: CustomFieldConfig): ZodType {
257
+ let modifiedType = zodType;
258
+
259
+ if (customField.list) {
260
+ modifiedType = z.array(modifiedType);
261
+ }
262
+ if (customField.nullable !== false) {
263
+ modifiedType = modifiedType.optional().nullable();
264
+ }
265
+
266
+ return modifiedType;
267
+ }
268
+
269
+ /**
270
+ * Processes all custom fields and creates a complete validation schema for the customFields object.
271
+ * Handles context-aware filtering (translation vs root context) and orchestrates the creation
272
+ * of validation schemas for all custom field types including complex struct fields.
273
+ *
274
+ * @param customFieldConfigs - Array of all custom field configurations
275
+ * @param isTranslationContext - Whether we're processing fields for translation forms
276
+ * @returns Zod schema shape for the entire customFields object
277
+ */
278
+ function processCustomFieldsSchema(
279
+ customFieldConfigs: CustomFieldConfig[],
280
+ isTranslationContext: boolean,
281
+ ): ZodRawShape {
282
+ const customFieldsSchema: ZodRawShape = {};
283
+ const translatableTypes = ['localeString', 'localeText'];
284
+
285
+ const filteredCustomFields = customFieldConfigs.filter(cf => {
286
+ if (isTranslationContext) {
287
+ return translatableTypes.includes(cf.type);
288
+ } else {
289
+ return !translatableTypes.includes(cf.type);
290
+ }
291
+ });
292
+
293
+ for (const customField of filteredCustomFields) {
294
+ let zodType: ZodType;
295
+
296
+ if (customField.type === 'struct') {
297
+ zodType = createStructFieldSchema(customField as StructCustomFieldConfig);
298
+ } else {
299
+ zodType = createCustomFieldValidationSchema(customField);
300
+ }
301
+
302
+ zodType = applyListAndNullableModifiers(zodType, customField);
303
+ const schemaPropertyName = getGraphQlInputName(customField);
304
+ customFieldsSchema[schemaPropertyName] = zodType;
305
+ }
306
+
307
+ return customFieldsSchema;
308
+ }
309
+
310
+ export function createFormSchemaFromFields(
311
+ fields: FieldInfo[],
312
+ customFieldConfigs?: CustomFieldConfig[],
313
+ isTranslationContext = false,
314
+ ) {
9
315
  const schemaConfig: ZodRawShape = {};
316
+
10
317
  for (const field of fields) {
11
318
  const isScalar = isScalarType(field.type);
12
319
  const isEnum = isEnumType(field.type);
13
- if (isScalar || isEnum) {
14
- schemaConfig[field.name] = getZodTypeFromField(field);
320
+
321
+ if ((isScalar || isEnum) && field.name !== 'customFields') {
322
+ schemaConfig[field.name] = getZodTypeFromField(field, customFieldConfigs);
323
+ } else if (field.name === 'customFields') {
324
+ const customFieldsSchema =
325
+ customFieldConfigs && customFieldConfigs.length > 0
326
+ ? processCustomFieldsSchema(customFieldConfigs, isTranslationContext)
327
+ : {};
328
+ schemaConfig[field.name] = z.object(customFieldsSchema).optional();
15
329
  } else if (field.typeInfo) {
16
- let nestedType: ZodType = createFormSchemaFromFields(field.typeInfo);
330
+ const isNestedTranslationContext = field.name === 'translations' || isTranslationContext;
331
+ let nestedType: ZodType = createFormSchemaFromFields(
332
+ field.typeInfo,
333
+ customFieldConfigs,
334
+ isNestedTranslationContext,
335
+ );
336
+
17
337
  if (field.nullable) {
18
338
  nestedType = nestedType.optional().nullable();
19
339
  }
20
340
  if (field.list) {
21
341
  nestedType = z.array(nestedType);
22
342
  }
343
+
23
344
  schemaConfig[field.name] = nestedType;
24
345
  }
25
346
  }
347
+
26
348
  return z.object(schemaConfig);
27
349
  }
28
350
 
@@ -69,8 +391,12 @@ export function getDefaultValueFromField(field: FieldInfo, defaultLanguageCode?:
69
391
  }
70
392
  }
71
393
 
72
- export function getZodTypeFromField(field: FieldInfo): ZodTypeAny {
394
+ export function getZodTypeFromField(field: FieldInfo, customFieldConfigs?: CustomFieldConfig[]): ZodTypeAny {
73
395
  let zodType: ZodType;
396
+
397
+ // This function is only used for non-custom fields, so we don't need custom field logic here
398
+ // Custom fields are handled separately in createFormSchemaFromFields
399
+
74
400
  switch (field.type) {
75
401
  case 'String':
76
402
  case 'ID':
@@ -88,6 +414,7 @@ export function getZodTypeFromField(field: FieldInfo): ZodTypeAny {
88
414
  default:
89
415
  zodType = z.any();
90
416
  }
417
+
91
418
  if (field.list) {
92
419
  zodType = z.array(zodType);
93
420
  }
@@ -96,3 +423,11 @@ export function getZodTypeFromField(field: FieldInfo): ZodTypeAny {
96
423
  }
97
424
  return zodType;
98
425
  }
426
+
427
+ export function getGraphQlInputName(config: { name: string; type: string; list?: boolean }): string {
428
+ if (config.type === 'relation') {
429
+ return config.list === true ? `${config.name}Ids` : `${config.name}Id`;
430
+ } else {
431
+ return config.name;
432
+ }
433
+ }
@@ -0,0 +1,51 @@
1
+ import {
2
+ DataDisplayComponent,
3
+ DataInputComponent,
4
+ useComponentRegistry,
5
+ } from '@/vdb/framework/component-registry/component-registry.js';
6
+ import { generateInputComponentKey } from '@/vdb/framework/extension-api/input-component-extensions.js';
7
+ import { usePageBlock } from '@/vdb/hooks/use-page-block.js';
8
+ import { usePage } from '@/vdb/hooks/use-page.js';
9
+ import { ControllerRenderProps, FieldPath, FieldValues } from 'react-hook-form';
10
+
11
+ export interface OverriddenFormComponent<
12
+ TFieldValues extends FieldValues = any,
13
+ TName extends FieldPath<TFieldValues> = any,
14
+ > {
15
+ fieldName: string;
16
+ field: ControllerRenderProps<TFieldValues, TName>;
17
+ children?: React.ReactNode;
18
+ }
19
+
20
+ /**
21
+ * @description
22
+ * Based on the pageId and blockId of where this is placed, it will check whether any custom components
23
+ * are registered and render them if so. Otherwise, it will render the children, which act as the
24
+ * default if this location has not been overridden.
25
+ *
26
+ * ```tsx
27
+ * <OverriddenFormComponent fieldName="myField" field={field}>
28
+ * <Input {...field} />
29
+ * </OverriddenFormComponent>
30
+ * ```
31
+ */
32
+ export function OverriddenFormComponent({ fieldName, field, children }: Readonly<OverriddenFormComponent>) {
33
+ const page = usePage();
34
+ const pageBlock = usePageBlock({ optional: true });
35
+ const componentRegistry = useComponentRegistry();
36
+ let DisplayComponent: DataDisplayComponent | undefined;
37
+ let InputComponent: DataInputComponent | undefined;
38
+ if (page.pageId && pageBlock?.blockId) {
39
+ const customInputComponentKey = generateInputComponentKey(page.pageId, pageBlock.blockId, fieldName);
40
+ DisplayComponent = componentRegistry.getDisplayComponent(customInputComponentKey);
41
+ InputComponent = componentRegistry.getInputComponent(customInputComponentKey);
42
+ }
43
+ if (DisplayComponent) {
44
+ return <DisplayComponent {...field} />;
45
+ }
46
+
47
+ if (InputComponent) {
48
+ return <InputComponent {...field} />;
49
+ }
50
+ return children ?? null;
51
+ }
@@ -1,26 +1,29 @@
1
- import { getOperationVariablesFields } from '@/framework/document-introspection/get-document-structure.js';
2
- import {
3
- createFormSchemaFromFields,
4
- getDefaultValuesFromFields,
5
- } from '@/framework/form-engine/form-schema-tools.js';
6
- import { useChannel } from '@/hooks/use-channel.js';
7
- import { useServerConfig } from '@/hooks/use-server-config.js';
8
1
  import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
9
2
  import { zodResolver } from '@hookform/resolvers/zod';
10
3
  import { VariablesOf } from 'gql.tada';
11
4
  import { FormEvent } from 'react';
12
5
  import { useForm } from 'react-hook-form';
6
+ import { useChannel } from '../../hooks/use-channel.js';
7
+ import { useServerConfig } from '../../hooks/use-server-config.js';
8
+ import { getOperationVariablesFields } from '../document-introspection/get-document-structure.js';
9
+ import { createFormSchemaFromFields, getDefaultValuesFromFields } from './form-schema-tools.js';
10
+ import { removeEmptyIdFields, transformRelationFields } from './utils.js';
13
11
 
14
12
  export interface GeneratedFormOptions<
15
13
  T extends TypedDocumentNode<any, any>,
16
- VarName extends (keyof VariablesOf<T>) | undefined = 'input',
14
+ VarName extends keyof VariablesOf<T> | undefined = 'input',
17
15
  E extends Record<string, any> = Record<string, any>,
18
16
  > {
19
17
  document?: T;
20
18
  varName?: VarName;
21
19
  entity: E | null | undefined;
22
- setValues: (entity: NonNullable<E>) => VarName extends keyof VariablesOf<T> ? VariablesOf<T>[VarName] : VariablesOf<T>;
23
- onSubmit?: (values: VarName extends keyof VariablesOf<T> ? VariablesOf<T>[VarName] : VariablesOf<T>) => void;
20
+ customFieldConfig?: any[]; // Add custom field config for validation
21
+ setValues: (
22
+ entity: NonNullable<E>,
23
+ ) => VarName extends keyof VariablesOf<T> ? VariablesOf<T>[VarName] : VariablesOf<T>;
24
+ onSubmit?: (
25
+ values: VarName extends keyof VariablesOf<T> ? VariablesOf<T>[VarName] : VariablesOf<T>,
26
+ ) => void;
24
27
  }
25
28
 
26
29
  /**
@@ -35,13 +38,19 @@ export function useGeneratedForm<
35
38
  VarName extends keyof VariablesOf<T> | undefined,
36
39
  E extends Record<string, any> = Record<string, any>,
37
40
  >(options: GeneratedFormOptions<T, VarName, E>) {
38
- const { document, entity, setValues, onSubmit, varName } = options;
41
+ const { document, entity, setValues, onSubmit, varName, customFieldConfig } = options;
39
42
  const { activeChannel } = useChannel();
40
- const availableLanguages = useServerConfig()?.availableLanguages || [];
43
+ const serverConfig = useServerConfig();
44
+ const availableLanguages = serverConfig?.availableLanguages || [];
41
45
  const updateFields = document ? getOperationVariablesFields(document, varName) : [];
42
- const schema = createFormSchemaFromFields(updateFields);
46
+
47
+ const schema = createFormSchemaFromFields(updateFields, customFieldConfig);
43
48
  const defaultValues = getDefaultValuesFromFields(updateFields, activeChannel?.defaultLanguageCode);
44
- const processedEntity = ensureTranslationsForAllLanguages(entity, availableLanguages);
49
+ const processedEntity = ensureTranslationsForAllLanguages(entity, availableLanguages, defaultValues);
50
+
51
+ const values = processedEntity
52
+ ? transformRelationFields(updateFields, setValues(processedEntity))
53
+ : defaultValues;
45
54
 
46
55
  const form = useForm({
47
56
  resolver: async (values, context, options) => {
@@ -53,14 +62,28 @@ export function useGeneratedForm<
53
62
  },
54
63
  mode: 'onChange',
55
64
  defaultValues,
56
- values: processedEntity ? setValues(processedEntity) : defaultValues,
65
+ values,
57
66
  });
58
- let submitHandler = (event: FormEvent) => {
67
+ let submitHandler = (event: FormEvent): any => {
59
68
  event.preventDefault();
60
69
  };
61
70
  if (onSubmit) {
62
- submitHandler = (event: FormEvent) => {
63
- form.handleSubmit(onSubmit as any)(event);
71
+ submitHandler = async (event: FormEvent) => {
72
+ event.preventDefault();
73
+
74
+ // Trigger validation on ALL fields, not just dirty ones
75
+ const isValid = await form.trigger();
76
+
77
+ if (!isValid) {
78
+ console.log(`Form invalid!`);
79
+ event.stopPropagation();
80
+ return;
81
+ }
82
+
83
+ const onSubmitWrapper = (values: any) => {
84
+ onSubmit(removeEmptyIdFields(values, updateFields));
85
+ };
86
+ form.handleSubmit(onSubmitWrapper)(event);
64
87
  };
65
88
  }
66
89
 
@@ -69,11 +92,13 @@ export function useGeneratedForm<
69
92
 
70
93
  /**
71
94
  * Ensures that an entity with translations has entries for all available languages.
72
- * If a language is missing, it creates an empty translation based on the structure of existing translations.
95
+ * If a language is missing, it creates an empty translation based on the structure of existing translations
96
+ * and the expected form structure from defaultValues.
73
97
  */
74
98
  function ensureTranslationsForAllLanguages<E extends Record<string, any>>(
75
99
  entity: E | null | undefined,
76
100
  availableLanguages: string[] = [],
101
+ expectedStructure?: Record<string, any>,
77
102
  ): E | null | undefined {
78
103
  if (
79
104
  !entity ||
@@ -91,23 +116,56 @@ function ensureTranslationsForAllLanguages<E extends Record<string, any>>(
91
116
  // Get existing language codes
92
117
  const existingLanguageCodes = new Set(translations.map((t: any) => t.languageCode));
93
118
 
119
+ // Get the expected translation structure from defaultValues or existing translations
120
+ const existingTemplate = translations[0] || {};
121
+ const expectedTranslationStructure = expectedStructure?.translations?.[0] || {};
122
+
123
+ // Merge the structures to ensure we have all expected fields
124
+ const templateStructure = {
125
+ ...expectedTranslationStructure,
126
+ ...existingTemplate,
127
+ };
128
+
94
129
  // Add missing language translations
95
130
  for (const langCode of availableLanguages) {
96
131
  if (!existingLanguageCodes.has(langCode)) {
97
- // Find a translation to use as template for field structure
98
- const template = translations[0] || {};
99
132
  const emptyTranslation: Record<string, any> = {
100
133
  languageCode: langCode,
101
134
  };
102
135
 
103
- // Add empty fields based on template (excluding languageCode)
104
- Object.keys(template).forEach(key => {
136
+ // Add empty fields based on merged template structure (excluding languageCode)
137
+ Object.keys(templateStructure).forEach(key => {
105
138
  if (key !== 'languageCode') {
106
- emptyTranslation[key] = '';
139
+ if (typeof templateStructure[key] === 'object' && templateStructure[key] !== null) {
140
+ // For nested objects like customFields, create an empty object
141
+ emptyTranslation[key] = Array.isArray(templateStructure[key]) ? [] : {};
142
+ } else {
143
+ // For primitive values, use empty string as default
144
+ emptyTranslation[key] = '';
145
+ }
107
146
  }
108
147
  });
109
148
 
110
149
  translations.push(emptyTranslation);
150
+ } else {
151
+ // For existing translations, ensure they have all expected fields
152
+ const existingTranslation = translations.find((t: any) => t.languageCode === langCode);
153
+ if (existingTranslation) {
154
+ Object.keys(expectedTranslationStructure).forEach(key => {
155
+ if (key !== 'languageCode' && !(key in existingTranslation)) {
156
+ if (
157
+ typeof expectedTranslationStructure[key] === 'object' &&
158
+ expectedTranslationStructure[key] !== null
159
+ ) {
160
+ existingTranslation[key] = Array.isArray(expectedTranslationStructure[key])
161
+ ? []
162
+ : {};
163
+ } else {
164
+ existingTranslation[key] = '';
165
+ }
166
+ }
167
+ });
168
+ }
111
169
  }
112
170
  }
113
171
 
@@ -0,0 +1,37 @@
1
+ import { graphql, VariablesOf } from 'gql.tada';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { getOperationVariablesFields } from '../document-introspection/get-document-structure.js';
5
+
6
+ import { removeEmptyIdFields } from './utils.js';
7
+
8
+ const createProductDocument = graphql(`
9
+ mutation CreateProduct($input: CreateProductInput!) {
10
+ createProduct(input: $input) {
11
+ id
12
+ }
13
+ }
14
+ `);
15
+
16
+ type CreateProductInput = VariablesOf<typeof createProductDocument>;
17
+
18
+ describe('removeEmptyIdFields', () => {
19
+ it('should remove empty translation id field', () => {
20
+ const values: CreateProductInput = {
21
+ input: { translations: [{ id: '', languageCode: 'en' }] },
22
+ };
23
+ const fields = getOperationVariablesFields(createProductDocument);
24
+ const result = removeEmptyIdFields(values, fields);
25
+
26
+ expect(result).toEqual({ input: { translations: [{ languageCode: 'en' }] } });
27
+ });
28
+
29
+ it('should remove empty featuredAsset id field', () => {
30
+ const values: CreateProductInput = {
31
+ input: { featuredAssetId: '', translations: [] },
32
+ };
33
+ const fields = getOperationVariablesFields(createProductDocument);
34
+ const result = removeEmptyIdFields(values, fields);
35
+ expect(result).toEqual({ input: { translations: [] } });
36
+ });
37
+ });