@antelopejs/dms 0.9.8 → 0.11.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 (451) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/README.md +1 -1
  3. package/data/module-store/catalog.json +2 -2
  4. package/dist/attachments/table-view.js +2 -2
  5. package/dist/attachments/table-view.js.map +1 -1
  6. package/dist/automation/actions.d.ts +2 -0
  7. package/dist/automation/actions.js +116 -71
  8. package/dist/automation/actions.js.map +1 -1
  9. package/dist/automation/i18n.d.ts +4 -0
  10. package/dist/automation/i18n.js +19 -0
  11. package/dist/automation/i18n.js.map +1 -0
  12. package/dist/automation/send-notification.d.ts +21 -0
  13. package/dist/automation/send-notification.js +163 -0
  14. package/dist/automation/send-notification.js.map +1 -0
  15. package/dist/automation/triggers.js +19 -25
  16. package/dist/automation/triggers.js.map +1 -1
  17. package/dist/client-origin-cors.d.ts +1 -0
  18. package/dist/client-origin-cors.js +19 -0
  19. package/dist/client-origin-cors.js.map +1 -0
  20. package/dist/config.d.ts +3 -2
  21. package/dist/config.js +5 -7
  22. package/dist/config.js.map +1 -1
  23. package/dist/frontend-module.d.ts +4 -0
  24. package/dist/frontend-module.js +37 -0
  25. package/dist/frontend-module.js.map +1 -0
  26. package/dist/implementations/dms/client-ip.js +1 -1
  27. package/dist/implementations/dms/client-ip.js.map +1 -1
  28. package/dist/implementations/dms/dev-reload.d.ts +5 -0
  29. package/dist/implementations/dms/dev-reload.js +23 -3
  30. package/dist/implementations/dms/dev-reload.js.map +1 -1
  31. package/dist/implementations/dms/frontend-modules.d.ts +2 -4
  32. package/dist/implementations/dms/frontend-modules.js +38 -94
  33. package/dist/implementations/dms/frontend-modules.js.map +1 -1
  34. package/dist/implementations/dms/frontend-ready.d.ts +12 -2
  35. package/dist/implementations/dms/frontend-ready.js +34 -4
  36. package/dist/implementations/dms/frontend-ready.js.map +1 -1
  37. package/dist/implementations/dms/layout-banners.d.ts +2 -1
  38. package/dist/implementations/dms/layout-banners.js +9 -0
  39. package/dist/implementations/dms/layout-banners.js.map +1 -1
  40. package/dist/implementations/dms/manifest.d.ts +7 -2
  41. package/dist/implementations/dms/manifest.js +12 -7
  42. package/dist/implementations/dms/manifest.js.map +1 -1
  43. package/dist/implementations/dms/page.d.ts +4 -3
  44. package/dist/implementations/dms/page.js +49 -18
  45. package/dist/implementations/dms/page.js.map +1 -1
  46. package/dist/implementations/dms/permissions-resolver.d.ts +2 -2
  47. package/dist/implementations/dms/permissions-resolver.js +2 -2
  48. package/dist/implementations/dms/permissions-resolver.js.map +1 -1
  49. package/dist/implementations/dms/realtime.js +1 -0
  50. package/dist/implementations/dms/realtime.js.map +1 -1
  51. package/dist/implementations/dms-base/bulk-selection.d.ts +1 -1
  52. package/dist/implementations/dms-base/bulk-selection.js +23 -1
  53. package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
  54. package/dist/implementations/dms-base/filter-tokens.js +5 -3
  55. package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
  56. package/dist/implementations/dms-base/search-route.d.ts +1 -0
  57. package/dist/implementations/dms-base/search-route.js +35 -13
  58. package/dist/implementations/dms-base/search-route.js.map +1 -1
  59. package/dist/implementations/dms-base/table-view.js +11 -6
  60. package/dist/implementations/dms-base/table-view.js.map +1 -1
  61. package/dist/implementations/dms-notifications/index.d.ts +2 -2
  62. package/dist/implementations/dms-notifications/index.js +5 -5
  63. package/dist/implementations/dms-notifications/index.js.map +1 -1
  64. package/dist/index.js +32 -55
  65. package/dist/index.js.map +1 -1
  66. package/dist/pages/modules/index.d.ts +1 -0
  67. package/dist/pages/modules/index.js +1 -0
  68. package/dist/pages/modules/index.js.map +1 -1
  69. package/dist/pages/modules/modules-store.d.ts +9 -0
  70. package/dist/pages/modules/modules-store.js +30 -0
  71. package/dist/pages/modules/modules-store.js.map +1 -0
  72. package/dist/pages/settings/users/account-credentials.js +2 -2
  73. package/dist/pages/settings/users/account-credentials.js.map +1 -1
  74. package/dist/pages/settings/users/account-data-store.js +25 -8
  75. package/dist/pages/settings/users/account-data-store.js.map +1 -1
  76. package/dist/pages/settings/users/invite-extension-routes.js +9 -3
  77. package/dist/pages/settings/users/invite-extension-routes.js.map +1 -1
  78. package/dist/pages/settings/users/member-management.js +13 -14
  79. package/dist/pages/settings/users/member-management.js.map +1 -1
  80. package/dist/pages/settings/users/profile.d.ts +1 -4
  81. package/dist/pages/settings/users/profile.js +0 -35
  82. package/dist/pages/settings/users/profile.js.map +1 -1
  83. package/dist/pages/settings/users/role-editor-store.js +26 -18
  84. package/dist/pages/settings/users/role-editor-store.js.map +1 -1
  85. package/dist/pages/settings/users/role-editor.d.ts +0 -1
  86. package/dist/pages/settings/users/role-editor.js +1 -2
  87. package/dist/pages/settings/users/role-editor.js.map +1 -1
  88. package/dist/pages/settings/users/role-preview.js +1 -2
  89. package/dist/pages/settings/users/role-preview.js.map +1 -1
  90. package/dist/pages/settings/users/roles.js +2 -2
  91. package/dist/pages/settings/users/roles.js.map +1 -1
  92. package/dist/pages/settings/users/two-factor-operations.js.map +1 -1
  93. package/dist/realtime/broker.d.ts +6 -0
  94. package/dist/realtime/broker.js +8 -1
  95. package/dist/realtime/broker.js.map +1 -1
  96. package/dist/realtime/presence.d.ts +8 -3
  97. package/dist/realtime/presence.js +8 -4
  98. package/dist/realtime/presence.js.map +1 -1
  99. package/dist/realtime/sessions.d.ts +11 -1
  100. package/dist/realtime/sessions.js +21 -3
  101. package/dist/realtime/sessions.js.map +1 -1
  102. package/dist/realtime/table-view.d.ts +2 -2
  103. package/dist/realtime/table-view.js +4 -3
  104. package/dist/realtime/table-view.js.map +1 -1
  105. package/dist/routes/auth/client-ip.d.ts +1 -0
  106. package/dist/routes/auth/client-ip.js +18 -0
  107. package/dist/routes/auth/client-ip.js.map +1 -1
  108. package/dist/routes/auth/oauth/config.d.ts +2 -1
  109. package/dist/routes/auth/oauth/config.js +5 -1
  110. package/dist/routes/auth/oauth/config.js.map +1 -1
  111. package/dist/routes/auth/session-handoff.d.ts +0 -1
  112. package/dist/routes/auth/session-handoff.js +0 -2
  113. package/dist/routes/auth/session-handoff.js.map +1 -1
  114. package/dist/routes/auth.js +3 -2
  115. package/dist/routes/auth.js.map +1 -1
  116. package/dist/routes/index.d.ts +0 -1
  117. package/dist/routes/index.js +0 -1
  118. package/dist/routes/index.js.map +1 -1
  119. package/dist/routes/realtime.d.ts +13 -1
  120. package/dist/routes/realtime.js +114 -29
  121. package/dist/routes/realtime.js.map +1 -1
  122. package/dist/startup-config.d.ts +2 -0
  123. package/dist/startup-config.js +62 -0
  124. package/dist/startup-config.js.map +1 -0
  125. package/dist/store/interfaces.d.ts +3 -2
  126. package/dist/store/interfaces.js +16 -6
  127. package/dist/store/interfaces.js.map +1 -1
  128. package/dist/store/listing.js +25 -15
  129. package/dist/store/listing.js.map +1 -1
  130. package/dist/store/operations.js +5 -2
  131. package/dist/store/operations.js.map +1 -1
  132. package/dist/store/requirements.js +3 -2
  133. package/dist/store/requirements.js.map +1 -1
  134. package/dist/store/runtime.d.ts +10 -1
  135. package/dist/store/runtime.js +33 -6
  136. package/dist/store/runtime.js.map +1 -1
  137. package/dist/store/types.d.ts +10 -0
  138. package/dist/utils/ensure-default-tenant.d.ts +2 -1
  139. package/dist/utils/ensure-default-tenant.js +13 -1
  140. package/dist/utils/ensure-default-tenant.js.map +1 -1
  141. package/dist/utils/export-jobs.js +2 -1
  142. package/dist/utils/export-jobs.js.map +1 -1
  143. package/dist/validation/auth.schema.d.ts +2 -2
  144. package/dist/validation/member-invite.schema.d.ts +2 -10
  145. package/dist/validation/member-invite.schema.js +2 -15
  146. package/dist/validation/member-invite.schema.js.map +1 -1
  147. package/frontend-vue/dms.frontend.ts +24 -5
  148. package/frontend-vue/dms.server.ts +17 -0
  149. package/frontend-vue/layers/dms-auth/server/accounts.ts +3 -4
  150. package/frontend-vue/layers/dms-core/app/build/composables/renderNow.ts +33 -0
  151. package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +5 -1
  152. package/frontend-vue/layers/dms-core/app/build/utils/crossTab.ts +199 -0
  153. package/frontend-vue/layers/dms-core/app/build/utils/sharedRealtimeConnection.ts +410 -0
  154. package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +77 -12
  155. package/frontend-vue/layers/dms-core/app/composables/data-types/useDataType.ts +26 -0
  156. package/frontend-vue/layers/dms-core/app/composables/live/livePauseSource.ts +29 -0
  157. package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +150 -9
  158. package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +6 -4
  159. package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +27 -1
  160. package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +256 -119
  161. package/frontend-vue/layers/dms-core/app/composables/translation/useComposedText.ts +21 -8
  162. package/frontend-vue/layers/dms-core/app/composables/translation/useTranslation.ts +25 -5
  163. package/frontend-vue/layers/dms-core/app/composables/useApiError.ts +3 -5
  164. package/frontend-vue/layers/dms-core/app/composables/useFieldErrors.ts +61 -26
  165. package/frontend-vue/layers/dms-core/app/plugins/dev-error-log.ts +16 -0
  166. package/frontend-vue/layers/dms-core/app/types/user.ts +6 -0
  167. package/frontend-vue/layers/dms-core/app/utils/composedText.ts +7 -2
  168. package/frontend-vue/layers/dms-core/app/utils/formatter.ts +134 -35
  169. package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +18 -1
  170. package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +136 -2
  171. package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +136 -2
  172. package/frontend-vue/layers/dms-core/shared/types/app-config.ts +0 -2
  173. package/frontend-vue/layers/dms-layout/app/app.config.ts +0 -1
  174. package/frontend-vue/layers/dms-layout/app/assets/css/main.css +11 -1
  175. package/frontend-vue/layers/dms-layout/app/assets/fonts/Geist-Italic-Variable.woff2 +0 -0
  176. package/frontend-vue/layers/dms-layout/app/assets/fonts/Geist-Variable.woff2 +0 -0
  177. package/frontend-vue/layers/dms-layout/app/assets/fonts/GeistMono-Italic-Variable.woff2 +0 -0
  178. package/frontend-vue/layers/dms-layout/app/assets/fonts/GeistMono-Variable.woff2 +0 -0
  179. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +3 -2
  180. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +41 -21
  181. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +6 -2
  182. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +4 -19
  183. package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +11 -107
  184. package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
  185. package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +4 -0
  186. package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +43 -82
  187. package/frontend-vue/layers/dms-layout/app/build/components/layout/SidebarUserMenu.vue +44 -3
  188. package/frontend-vue/layers/dms-layout/app/build/components/layout/UserIdentity.vue +11 -3
  189. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleCatalogTile.vue +132 -35
  190. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleManageMenu.vue +59 -0
  191. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleSourceLabel.vue +25 -0
  192. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreBanners.vue +73 -0
  193. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreOperation.vue +116 -0
  194. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +8 -3
  195. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +85 -207
  196. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +10 -3
  197. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTileSkeleton.vue +68 -0
  198. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +24 -8
  199. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxItem.vue +8 -5
  200. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/region/RegionTimeZoneInput.vue +1 -0
  201. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-permissions.ts +19 -0
  202. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/useRoleEditor.ts +2 -1
  203. package/frontend-vue/layers/dms-layout/app/build/composables/layout/useSidePanelWidth.ts +31 -70
  204. package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationGroupsOpen.ts +11 -1
  205. package/frontend-vue/layers/dms-layout/app/build/utils/dev-reload-stream.ts +81 -0
  206. package/frontend-vue/layers/dms-layout/app/build/utils/github-repository.ts +2 -2
  207. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +232 -36
  208. package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +122 -14
  209. package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +32 -3
  210. package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +49 -11
  211. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
  212. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +59 -4
  213. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +194 -0
  214. package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +23 -3
  215. package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +4 -2
  216. package/frontend-vue/layers/dms-layout/app/composables/page/usePageRecordDeletion.ts +162 -0
  217. package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
  218. package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +6 -2
  219. package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +6 -2
  220. package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +291 -0
  221. package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +87 -474
  222. package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +3 -0
  223. package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +13 -22
  224. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +11 -10
  225. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +22 -52
  226. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-quick-actions.client.ts +7 -7
  227. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-session.client.ts +11 -10
  228. package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
  229. package/frontend-vue/layers/dms-layout/app/theme/actions.ts +5 -4
  230. package/frontend-vue/layers/dms-layout/app/theme/form-controls.ts +9 -4
  231. package/frontend-vue/layers/dms-layout/app/theme/surfaces.ts +22 -6
  232. package/frontend-vue/layers/dms-layout/app/types/module-store.ts +9 -0
  233. package/frontend-vue/layers/dms-layout/app/types/page.ts +15 -1
  234. package/frontend-vue/layers/dms-layout/app/utils/account-menu.ts +11 -0
  235. package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +57 -0
  236. package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +26 -0
  237. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +45 -18
  238. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +44 -17
  239. package/frontend-vue/layers/dms-ui/app/build/components/containers/TargetComponentTree.vue +31 -0
  240. package/frontend-vue/layers/dms-ui/app/build/components/containers/constants.ts +25 -0
  241. package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +244 -15
  242. package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +42 -25
  243. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowHeader.vue +153 -0
  244. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowNotFound.vue +44 -0
  245. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/drawerRowHeader.ts +35 -0
  246. package/frontend-vue/layers/dms-ui/app/build/components/form/FormEntries.vue +44 -9
  247. package/frontend-vue/layers/dms-ui/app/build/components/form/FormErrorSummary.vue +11 -4
  248. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +38 -5
  249. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldNotes.vue +29 -5
  250. package/frontend-vue/layers/dms-ui/app/build/components/form/FormLoadError.vue +5 -1
  251. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +3 -1
  252. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSections.vue +106 -27
  253. package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +12 -4
  254. package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +4 -2
  255. package/frontend-vue/layers/dms-ui/app/build/components/table/FiltersRow.vue +48 -1
  256. package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +55 -54
  257. package/frontend-vue/layers/dms-ui/app/build/components/table/RowSelection.vue +2 -1
  258. package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +396 -288
  259. package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +2 -1
  260. package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +2 -1
  261. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardAvatar.vue +59 -0
  262. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +19 -2
  263. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardRowActions.vue +38 -0
  264. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +148 -104
  265. package/frontend-vue/layers/dms-ui/app/build/components/table-view/LiveHoldBanner.vue +17 -2
  266. package/frontend-vue/layers/dms-ui/app/build/composables/actions/bulkSelection.ts +43 -9
  267. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowComponentOptions.ts +76 -0
  268. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +172 -27
  269. package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +270 -46
  270. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/actionError.ts +6 -1
  271. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +2 -0
  272. package/frontend-vue/layers/dms-ui/app/build/composables/containers/closingLayers.ts +186 -0
  273. package/frontend-vue/layers/dms-ui/app/build/composables/containers/context.ts +14 -0
  274. package/frontend-vue/layers/dms-ui/app/build/composables/containers/drawerSize.ts +229 -0
  275. package/frontend-vue/layers/dms-ui/app/build/composables/containers/useResizableSize.ts +175 -0
  276. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +18 -35
  277. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/numberSuffix.ts +44 -0
  278. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +123 -27
  279. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerFieldTypes.ts +2 -1
  280. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationFilter.ts +75 -0
  281. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationRows.ts +92 -0
  282. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationValue.ts +39 -0
  283. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/twoLineCell.ts +3 -3
  284. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/useColumnValueRenderer.ts +6 -2
  285. package/frontend-vue/layers/dms-ui/app/build/composables/form/createFormFields.ts +15 -0
  286. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldSaved.ts +40 -0
  287. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +62 -0
  288. package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryContext.ts +5 -0
  289. package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +42 -7
  290. package/frontend-vue/layers/dms-ui/app/build/composables/form/formLayout.ts +106 -4
  291. package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +11 -5
  292. package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
  293. package/frontend-vue/layers/dms-ui/app/build/composables/form/formValidation.ts +36 -4
  294. package/frontend-vue/layers/dms-ui/app/build/composables/form/readonlyValue.ts +19 -0
  295. package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +27 -0
  296. package/frontend-vue/layers/dms-ui/app/build/composables/form/repeaterTable.ts +78 -0
  297. package/frontend-vue/layers/dms-ui/app/build/composables/form/serverFieldErrors.ts +49 -0
  298. package/frontend-vue/layers/dms-ui/app/build/composables/form/toasterClearance.ts +118 -0
  299. package/frontend-vue/layers/dms-ui/app/build/composables/form/useInstantForm.ts +12 -1
  300. package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +41 -3
  301. package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +10 -2
  302. package/frontend-vue/layers/dms-ui/app/build/composables/live/pageReadClock.ts +28 -0
  303. package/frontend-vue/layers/dms-ui/app/build/composables/live/shortcuts.ts +1 -1
  304. package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +17 -3
  305. package/frontend-vue/layers/dms-ui/app/build/composables/live/useUpdatedLabel.ts +38 -0
  306. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/pageToolbarContext.ts +75 -0
  307. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/toolbarControlHeight.ts +29 -0
  308. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparison.ts +52 -0
  309. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +21 -0
  310. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodShortLabels.ts +28 -0
  311. package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +1 -0
  312. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRelationFilterLabels.ts +97 -0
  313. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRowScrollAnchor.ts +106 -0
  314. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +9 -0
  315. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +1 -2
  316. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/archivedCount.ts +31 -0
  317. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/filterEditorValue.ts +38 -0
  318. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/formatFilterValue.ts +33 -2
  319. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/scrollEdges.ts +31 -0
  320. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useCardFields.ts +33 -0
  321. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useMobileDisplay.ts +48 -0
  322. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +41 -18
  323. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +3 -2
  324. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLinkedVisit.ts +78 -0
  325. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +135 -22
  326. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +56 -25
  327. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +90 -35
  328. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +86 -10
  329. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/bulkActions.ts +14 -4
  330. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/card.ts +127 -2
  331. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/compareValues.ts +13 -0
  332. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/expandedRows.ts +28 -0
  333. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/groupedRows.ts +114 -1
  334. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/linkedArrival.ts +308 -0
  335. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/locateRow.ts +106 -0
  336. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pageArrival.ts +34 -0
  337. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +17 -0
  338. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/quickFilters.ts +41 -33
  339. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +24 -8
  340. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableCounts.ts +113 -0
  341. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +27 -4
  342. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +22 -6
  343. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/writeError.ts +21 -0
  344. package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
  345. package/frontend-vue/layers/dms-ui/app/build/utils/codeHighlight.ts +67 -29
  346. package/frontend-vue/layers/dms-ui/app/build/utils/eyebrow.ts +12 -0
  347. package/frontend-vue/layers/dms-ui/app/build/utils/fieldOptionsI18n.ts +51 -11
  348. package/frontend-vue/layers/dms-ui/app/build/utils/htmlGrammar.ts +79 -0
  349. package/frontend-vue/layers/dms-ui/app/build/utils/inputWidth.ts +53 -0
  350. package/frontend-vue/layers/dms-ui/app/build/utils/numberAdjustment.ts +6 -3
  351. package/frontend-vue/layers/dms-ui/app/build/utils/relativeDate.ts +19 -5
  352. package/frontend-vue/layers/dms-ui/app/build/utils/scrollWithin.ts +99 -0
  353. package/frontend-vue/layers/dms-ui/app/build/utils/shellParser.ts +1 -1
  354. package/frontend-vue/layers/dms-ui/app/build/utils/streamHighlight.ts +90 -0
  355. package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +36 -14
  356. package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
  357. package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +9 -1
  358. package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +80 -32
  359. package/frontend-vue/layers/dms-ui/app/components/blocks/PageToolbarBlock.vue +137 -0
  360. package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +5 -0
  361. package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +16 -2
  362. package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +24 -5
  363. package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +41 -23
  364. package/frontend-vue/layers/dms-ui/app/components/code-snippet/CodeSnippet.vue +23 -12
  365. package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +1 -1
  366. package/frontend-vue/layers/dms-ui/app/components/container-footer/ContainerFooter.vue +34 -0
  367. package/frontend-vue/layers/dms-ui/app/components/field-row/FieldRow.vue +25 -2
  368. package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +2 -2
  369. package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +119 -36
  370. package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +53 -22
  371. package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +20 -1
  372. package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +13 -1
  373. package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +8 -3
  374. package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +53 -5
  375. package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +29 -5
  376. package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +45 -8
  377. package/frontend-vue/layers/dms-ui/app/components/form/components/InputPercentage.vue +28 -5
  378. package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +5 -0
  379. package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +19 -2
  380. package/frontend-vue/layers/dms-ui/app/components/form/components/InputTime.vue +2 -1
  381. package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +226 -65
  382. package/frontend-vue/layers/dms-ui/app/components/form/components/Repeater.vue +80 -30
  383. package/frontend-vue/layers/dms-ui/app/components/form/components/RepeaterTable.vue +210 -0
  384. package/frontend-vue/layers/dms-ui/app/components/form/components/Select.vue +4 -2
  385. package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +11 -2
  386. package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +79 -70
  387. package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +117 -21
  388. package/frontend-vue/layers/dms-ui/app/components/meter/Meter.vue +2 -1
  389. package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +113 -34
  390. package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +14 -2
  391. package/frontend-vue/layers/dms-ui/app/components/section-header/Eyebrow.vue +6 -1
  392. package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +13 -0
  393. package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +52 -3
  394. package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +2 -1
  395. package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +74 -25
  396. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +48 -44
  397. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanDisplay.vue +4 -1
  398. package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +426 -97
  399. package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +33 -5
  400. package/frontend-vue/layers/dms-ui/app/composables/chart/apexCategoryLabels.ts +68 -0
  401. package/frontend-vue/layers/dms-ui/app/composables/chart/apexDatetimeBuckets.ts +306 -0
  402. package/frontend-vue/layers/dms-ui/app/composables/chart/formatValue.ts +4 -3
  403. package/frontend-vue/layers/dms-ui/app/composables/chart/heatmapColors.ts +93 -0
  404. package/frontend-vue/layers/dms-ui/app/composables/chart/localizeSeries.ts +62 -0
  405. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +116 -8
  406. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.types.ts +7 -0
  407. package/frontend-vue/layers/dms-ui/app/composables/containers/types.ts +84 -1
  408. package/frontend-vue/layers/dms-ui/app/composables/containers/useDrawer.ts +69 -8
  409. package/frontend-vue/layers/dms-ui/app/composables/form/types/field-saved.ts +11 -0
  410. package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +16 -0
  411. package/frontend-vue/layers/dms-ui/app/composables/form/types/functions.ts +1 -0
  412. package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +189 -28
  413. package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +29 -12
  414. package/frontend-vue/layers/dms-ui/app/composables/page-toolbar/usePageToolbarSize.ts +30 -0
  415. package/frontend-vue/layers/dms-ui/app/composables/table-view/kanban.ts +4 -2
  416. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +22 -3
  417. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +4 -0
  418. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +24 -1
  419. package/frontend-vue/layers/dms-ui/app/plugins/closing-layers.client.ts +10 -0
  420. package/frontend-vue/layers/dms-ui/app/plugins/live-pause-source.ts +15 -0
  421. package/frontend-vue/layers/dms-ui/app/plugins/register.ts +2 -0
  422. package/frontend-vue/layers/dms-ui/app/types/input-number.ts +3 -2
  423. package/frontend-vue/layers/dms-ui/app/types/page-toolbar.ts +7 -0
  424. package/frontend-vue/layers/dms-ui/app/types/row-action.ts +0 -2
  425. package/frontend-vue/layers/dms-ui/app/types/row-navigation.ts +17 -0
  426. package/frontend-vue/layers/dms-ui/app/types/table/query.ts +2 -1
  427. package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +3 -1
  428. package/frontend-vue/layers/dms-ui/app/utils/cascader.ts +94 -3
  429. package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
  430. package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +20 -5
  431. package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +20 -5
  432. package/frontend-vue/package.json +6 -2
  433. package/package.json +11 -10
  434. package/skills/dms/SKILL.md +1 -1
  435. package/skills/dms-auth/SKILL.md +5 -3
  436. package/skills/dms-best-practices/REFERENCE.md +2 -0
  437. package/skills/dms-best-practices/SKILL.md +4 -0
  438. package/skills/dms-module/SKILL.md +3 -2
  439. package/skills/dms-pages/REFERENCE.md +129 -16
  440. package/skills/dms-pages/SKILL.md +1 -1
  441. package/skills/dms-project/DEV-SERVERS.md +23 -1
  442. package/skills/dms-project/REFERENCE.md +1 -1
  443. package/skills/dms-project/SKILL.md +4 -1
  444. package/skills/dms-project/scripts/dms-dev.sh +205 -17
  445. package/dist/hooks/tenant-instances.d.ts +0 -1
  446. package/dist/hooks/tenant-instances.js +0 -13
  447. package/dist/hooks/tenant-instances.js.map +0 -1
  448. package/dist/routes/startup-gate.d.ts +0 -7
  449. package/dist/routes/startup-gate.js +0 -39
  450. package/dist/routes/startup-gate.js.map +0 -1
  451. package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
@@ -1,11 +1,15 @@
1
1
  import type { FormSubmitEvent } from "@nuxt/ui";
2
2
  import { z } from "zod";
3
- import { onMounted, unref } from "vue";
3
+ import { nextTick, onMounted, onScopeDispose, unref } from "vue";
4
4
  import type { FormProps, FormFetchResponse, FormSubmitResponse } from "./types";
5
5
  import { jsonSchemaToZod } from "json-schema-to-zod";
6
6
  import { FormEvents } from "./types/events";
7
7
  import type { FormData, FormFieldValue } from "./types/value";
8
- import type { FormField, FormFieldOrGroup } from "./types/field";
8
+ import type {
9
+ FieldCondition,
10
+ FormField,
11
+ FormFieldOrGroup,
12
+ } from "./types/field";
9
13
  import { isFieldGroup } from "./types/field";
10
14
  import { resolveResponseToast } from "../../build/utils/responseWarning";
11
15
  import { processFieldI18n } from "../../build/utils/fieldOptionsI18n";
@@ -35,14 +39,17 @@ interface FormSubmitNotice {
35
39
  import type { DataType } from "#dms-core/app/composables/data-types/useDataType";
36
40
  import {
37
41
  type ApiFieldError,
42
+ apiErrorStatus,
38
43
  apiErrorText,
39
44
  resolveFieldErrors,
40
45
  } from "#dms-core/app/composables/useFieldErrors";
46
+ import { HTTP_NOT_FOUND } from "#dms-core/app/utils/http-status";
41
47
  import { isBlankValue } from "#dms-core/app/composables/useFormValidation";
42
48
  import {
43
49
  isPlainObject,
44
50
  sameFormValue,
45
51
  } from "../../build/composables/unsaved-changes/formValue";
52
+ import { createStartingValueTracker } from "../../build/composables/form/fieldValueRule";
46
53
 
47
54
  /** A server error shown under its field, translated. */
48
55
  export interface FormServerFieldError {
@@ -361,6 +368,26 @@ export function resolveSubmitDefaults(
361
368
  return Object.keys(resolved).length > 0 ? resolved : undefined;
362
369
  }
363
370
 
371
+ /**
372
+ * The values a form's fields start from among its submit defaults: a field
373
+ * the form submits under anyway (the parent a table is filtered on by a route
374
+ * parameter) shows it from the start, and a mandatory one is filled. A
375
+ * default with no field of the form is only submitted.
376
+ */
377
+ export function fieldValuesOfSubmitDefaults(
378
+ fields: ReadonlyArray<{ id: string }>,
379
+ submitDefaults: Record<string, unknown> | undefined,
380
+ ): FormData {
381
+ const values: FormData = {};
382
+ if (!submitDefaults) return values;
383
+ for (const { id } of fields) {
384
+ if (id in submitDefaults) {
385
+ values[id] = submitDefaults[id] as FormFieldValue;
386
+ }
387
+ }
388
+ return values;
389
+ }
390
+
364
391
  export function makeFieldSchemaRequired(schema: z.ZodTypeAny): z.ZodTypeAny {
365
392
  if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {
366
393
  return makeFieldSchemaRequired(schema.unwrap());
@@ -451,6 +478,34 @@ export function isFieldMarkedRequired(
451
478
  return !!field.required || (required?.has(field.id) ?? false);
452
479
  }
453
480
 
481
+ const meetsCondition = (
482
+ condition: FieldCondition,
483
+ values: Record<string, unknown>,
484
+ ): boolean => condition.in.includes(values[condition.field] ?? null);
485
+
486
+ /**
487
+ * The fields the values hide (`visibleWhen` not met) or disable
488
+ * (`disabledWhen` met), added to those the watches hide or disable: the
489
+ * conditions hold from the values the form opens with, without a watch.
490
+ */
491
+ export function withConditionFields(
492
+ fields: ReadonlyArray<Pick<FormField, "id" | "visibleWhen" | "disabledWhen">>,
493
+ values: Record<string, unknown>,
494
+ watched: { disabled?: Set<string>; hidden?: Set<string> },
495
+ ): { disabled: Set<string>; hidden: Set<string> } {
496
+ const disabled = new Set(watched.disabled);
497
+ const hidden = new Set(watched.hidden);
498
+ for (const field of fields) {
499
+ if (field.visibleWhen && !meetsCondition(field.visibleWhen, values)) {
500
+ hidden.add(field.id);
501
+ }
502
+ if (field.disabledWhen && meetsCondition(field.disabledWhen, values)) {
503
+ disabled.add(field.id);
504
+ }
505
+ }
506
+ return { disabled, hidden };
507
+ }
508
+
454
509
  function watchFieldChanges(
455
510
  state: Ref<Record<string, unknown>>,
456
511
  componentId: string | undefined,
@@ -459,10 +514,14 @@ function watchFieldChanges(
459
514
  componentId: string,
460
515
  payload?: unknown,
461
516
  ) => void,
517
+ isStartingValue: (fieldId: string, value: unknown) => boolean,
462
518
  ): void {
463
519
  if (!componentId) return;
464
520
  const id = componentId;
465
521
  const previousValuesJson: Record<string, string> = {};
522
+ // `initial` on each change: a watch writing values (SetFieldValue) leaves
523
+ // the values the form started from to the record it loaded.
524
+ const isInitial = createStartingValueTracker(isStartingValue);
466
525
 
467
526
  // From mount, once `useWatch` listens: the values the form starts from
468
527
  // (defaults, loaded row) reach the watch rules too, not only later edits.
@@ -485,6 +544,7 @@ function watchFieldChanges(
485
544
  fieldId,
486
545
  value: newState[fieldId],
487
546
  formValues: newState,
547
+ initial: isInitial(fieldId, newState[fieldId]),
488
548
  });
489
549
  previousValuesJson[fieldId] = newJson;
490
550
  }
@@ -544,15 +604,41 @@ export const useForm = (props: FormProps, options: UseFormOptions = {}) => {
544
604
  const initialValues = ref<FormData>({});
545
605
  const submitSucceeded = ref(false);
546
606
 
547
- watchFieldChanges(state, props.componentId, sendComponentEvent);
607
+ const allFields = computed(() => flattenFields(props.fields));
608
+
609
+ // Whether a field holds the value the form started from: its default or
610
+ // the loaded record's, compared as its data type does (a joined row is
611
+ // the same as the id the picker holds).
612
+ const isStartingValue = (fieldId: string, value: unknown): boolean => {
613
+ const field = allFields.value.find((entry) => entry.id === fieldId);
614
+ const initial = initialValues.value[fieldId];
615
+ return field
616
+ ? isSameFieldValue(field, value, initial)
617
+ : sameFormValue(value, initial);
618
+ };
619
+
620
+ watchFieldChanges(
621
+ state,
622
+ props.componentId,
623
+ sendComponentEvent,
624
+ isStartingValue,
625
+ );
548
626
 
549
627
  // A successful submit leaves the form at its saved state; a later edit makes
550
628
  // it dirty again, so clear `submitSucceeded` to re-arm the unsaved-changes
551
629
  // guard (and the realtime release-on-unmount) for the new edits. Without this
552
630
  // the guard would stay permanently disabled after the first save.
631
+ // Set while the record a save stored reaches the state: what the server
632
+ // stored is no edit made since.
633
+ let isApplyingSavedRecord = false;
634
+ let isDisposed = false;
635
+ onScopeDispose(() => {
636
+ isDisposed = true;
637
+ });
553
638
  watch(
554
639
  () => state.value,
555
640
  () => {
641
+ if (isApplyingSavedRecord) return;
556
642
  if (submitSucceeded.value) {
557
643
  submitSucceeded.value = false;
558
644
  }
@@ -560,15 +646,16 @@ export const useForm = (props: FormProps, options: UseFormOptions = {}) => {
560
646
  { deep: true },
561
647
  );
562
648
 
563
- const allFields = computed(() => flattenFields(props.fields));
564
-
565
- const disabledFields = computed(
566
- () => state.value.disabledFields as Set<string> | undefined,
649
+ const inactiveFields = computed(() =>
650
+ withConditionFields(allFields.value, state.value, {
651
+ disabled: state.value.disabledFields as Set<string> | undefined,
652
+ hidden: state.value.hiddenFields as Set<string> | undefined,
653
+ }),
567
654
  );
568
655
 
569
- const hiddenFields = computed(
570
- () => state.value.hiddenFields as Set<string> | undefined,
571
- );
656
+ const disabledFields = computed(() => inactiveFields.value.disabled);
657
+
658
+ const hiddenFields = computed(() => inactiveFields.value.hidden);
572
659
 
573
660
  const requiredFields = computed(
574
661
  () => state.value.requiredFields as Set<string> | undefined,
@@ -607,32 +694,39 @@ export const useForm = (props: FormProps, options: UseFormOptions = {}) => {
607
694
  const isRecordUpdate = computed(
608
695
  () => !!resolvedFetchUrl.value && props.submitUrlMethod !== HttpMethod.post,
609
696
  );
697
+ // A create form (a table view's add form, the duplicate it opens) makes a
698
+ // new row: what it holds is new as a whole, nothing "changed" in it.
699
+ const createsRecord = props.kind === "create";
610
700
 
611
- const fetchData = async () => {
701
+ /** Reads the record at `fetchUrl`: its values, mapped for the fields. */
702
+ const loadRecord = async () => {
612
703
  if (!props.fetchUrl) return undefined;
704
+ const processedUrl = resolveUrlVariables(props.fetchUrl, buildUrlContext());
705
+ if (processedUrl === undefined) return undefined;
706
+
707
+ const fetchedData = await $authFetch<FormFetchResponse>(processedUrl, {
708
+ method: props.fetchUrlMethod || "GET",
709
+ headers: { [CONTENT_LANGUAGE_HEADER]: "*" },
710
+ });
711
+
712
+ initialValues.value = {
713
+ ...initialValues.value,
714
+ ...JSON.parse(JSON.stringify(fetchedData)),
715
+ };
716
+ return processBeforeStateMappers(fetchedData, props.fields, getDataType);
717
+ };
613
718
 
719
+ const fetchData = async () => {
614
720
  try {
615
- const processedUrl = resolveUrlVariables(
616
- props.fetchUrl,
617
- buildUrlContext(),
618
- );
619
- if (processedUrl === undefined) return undefined;
620
-
621
- const fetchedData = await $authFetch<FormFetchResponse>(processedUrl, {
622
- method: props.fetchUrlMethod || "GET",
623
- headers: { [CONTENT_LANGUAGE_HEADER]: "*" },
624
- });
625
-
626
- initialValues.value = {
627
- ...initialValues.value,
628
- ...JSON.parse(JSON.stringify(fetchedData)),
629
- };
630
- return processBeforeStateMappers(fetchedData, props.fields, getDataType);
721
+ return await loadRecord();
631
722
  } catch (error) {
632
723
  // A server render shows the failure in the form (FormLoadError) and
633
724
  // hands it to the browser; a toast it added would show in its HTML
634
725
  // alone, which the browser's toasts never hold.
635
- if (!import.meta.env.SSR) showFetchErrorToast(error);
726
+ // A 404 is no error either: the form says the record is not found.
727
+ if (!import.meta.env.SSR && apiErrorStatus(error) !== HTTP_NOT_FOUND) {
728
+ showFetchErrorToast(error);
729
+ }
636
730
  throw error;
637
731
  }
638
732
  };
@@ -767,6 +861,63 @@ export const useForm = (props: FormProps, options: UseFormOptions = {}) => {
767
861
  options.onSaved?.();
768
862
  };
769
863
 
864
+ /** Puts the record a save stored in the state: no edit made since. */
865
+ const applyStoredRecord = async (stored: FormData): Promise<void> => {
866
+ isApplyingSavedRecord = true;
867
+ Object.assign(state.value, stored);
868
+ options.onSaved?.();
869
+ await nextTick();
870
+ isApplyingSavedRecord = false;
871
+ };
872
+
873
+ /**
874
+ * Reads the record back from `fetchUrl` once its update went through, for
875
+ * a route that does not answer it. An edit made meanwhile is kept, and a
876
+ * failed read leaves the values as they were sent.
877
+ */
878
+ const refreshSavedRecord = async (): Promise<void> => {
879
+ let stored: FormData | undefined;
880
+ try {
881
+ stored = (await loadRecord()) as FormData | undefined;
882
+ } catch {
883
+ return;
884
+ }
885
+ if (isDisposed || !submitSucceeded.value) return;
886
+ if (!stored || Object.keys(stored).length === 0) return;
887
+ await applyStoredRecord(stored);
888
+ };
889
+
890
+ /**
891
+ * Shows the record its update stored rather than what was typed: the
892
+ * server may store what it was sent normalized (a VAT number stripped of
893
+ * its spaces, the empty rows of a list dropped). A route that answers the
894
+ * record (a table view's edit route, interface-data-api 0.3) is read
895
+ * without a second request: its answer holds the record when it carries
896
+ * one of the form's fields, and only those are taken. Any other answer
897
+ * (`{ success: true }`, nothing) has the form read `fetchUrl` again. The
898
+ * form stays saved.
899
+ */
900
+ const showSavedRecord = async (
901
+ response: FormSubmitResponse | undefined,
902
+ ): Promise<void> => {
903
+ if (!submitSucceeded.value) return;
904
+ const fieldIds = new Set(allFields.value.map((field) => field.id));
905
+ // The route answers JSON, as `fetchUrl` does: field values.
906
+ const record: FormData = isPlainObject(response)
907
+ ? (Object.fromEntries(
908
+ Object.entries(response).filter(([key]) => fieldIds.has(key)),
909
+ ) as FormData)
910
+ : {};
911
+ if (Object.keys(record).length === 0) return refreshSavedRecord();
912
+ initialValues.value = {
913
+ ...initialValues.value,
914
+ ...JSON.parse(JSON.stringify(record)),
915
+ };
916
+ await applyStoredRecord(
917
+ processBeforeStateMappers(record, props.fields, getDataType) as FormData,
918
+ );
919
+ };
920
+
770
921
  const handleSubmitSuccess = async (
771
922
  response: FormSubmitResponse | undefined,
772
923
  plainData: FormData,
@@ -854,6 +1005,14 @@ export const useForm = (props: FormProps, options: UseFormOptions = {}) => {
854
1005
  try {
855
1006
  const response = await sendSubmit(target.url, plainData, markSubmitSaved);
856
1007
  await handleSubmitSuccess(response, plainData);
1008
+ // A redirect leaves the form, an action form starts over.
1009
+ if (
1010
+ isRecordUpdate.value &&
1011
+ props.kind !== "action" &&
1012
+ !props.redirectOnSuccess
1013
+ ) {
1014
+ await showSavedRecord(response);
1015
+ }
857
1016
  } catch (error) {
858
1017
  reportSubmitError(error);
859
1018
  } finally {
@@ -914,5 +1073,7 @@ export const useForm = (props: FormProps, options: UseFormOptions = {}) => {
914
1073
  isFieldGroup,
915
1074
  submitSucceeded,
916
1075
  resolvedFetchUrl,
1076
+ createsRecord,
1077
+ effectiveSubmitDefaults,
917
1078
  };
918
1079
  };
@@ -1,10 +1,15 @@
1
1
  import {
2
2
  computed,
3
3
  getCurrentScope,
4
+ onMounted,
4
5
  onScopeDispose,
6
+ shallowRef,
5
7
  type ComputedRef,
6
8
  } from "vue";
7
- import { currentPageLive } from "../../build/composables/live/currentPageLive";
9
+ import {
10
+ currentPageLive,
11
+ isServerPageLive,
12
+ } from "../../build/composables/live/currentPageLive";
8
13
  import type { PageLiveController } from "../../build/composables/live/pageLiveController";
9
14
  import type { PageLiveNote, PageLiveNoteInput } from "../../types/live";
10
15
 
@@ -68,8 +73,16 @@ export interface PageLiveState {
68
73
  * paused, show what was missed, offer its own switch, hold the page while the
69
74
  * user types.
70
75
  *
71
- * On a page without `live`, or while server-rendering, `isEnabled` is false,
72
- * the page reads as live and never paused, and its actions do nothing.
76
+ * On a page without `live`, `isEnabled` is false, the page reads as live and
77
+ * never paused, and its actions do nothing. A server render reads the page as
78
+ * live and never paused too, but `isEnabled` says whether it declares `live`:
79
+ * the browser hydrates the page's blocks with its controller installed, so
80
+ * the server draws them as it will. The rest of the state is born in the
81
+ * browser (the realtime stream, the page's reads, a pause): a component
82
+ * hydrating the server's HTML reads it as the server did (live, not polling,
83
+ * never read, nothing missed) until its own `onMounted`, then follows the
84
+ * page. A component mounted afresh, after a client navigation, reads the page
85
+ * at once.
73
86
  *
74
87
  * ```ts
75
88
  * const { isPaused, missedCount, resume, hold, release } = usePageLive();
@@ -80,25 +93,29 @@ export interface PageLiveState {
80
93
  * `realtimeTopic` are.
81
94
  */
82
95
  export function usePageLive(): PageLiveState {
83
- const isPaused = computed(() => currentPageLive()?.isPaused.value ?? false);
96
+ // An async block hydrates after the page has installed its controller and
97
+ // read its data: until mounted, it draws the state the server drew.
98
+ const isHydrating = shallowRef(isDmsHydrating());
99
+ if (isHydrating.value) onMounted(() => (isHydrating.value = false));
100
+ const shown = () => (isHydrating.value ? null : currentPageLive());
101
+ const isPaused = computed(() => shown()?.isPaused.value ?? false);
84
102
  const hold = usePageLiveHold();
103
+ const isServerLive = isServerPageLive();
85
104
  return {
86
- isEnabled: computed(() => currentPageLive() !== null),
105
+ isEnabled: computed(() => currentPageLive() !== null || isServerLive),
87
106
  isLive: computed(() => !isPaused.value),
88
107
  isPaused,
89
- pauseReason: computed(() => currentPageLive()?.pauseReason.value ?? null),
108
+ pauseReason: computed(() => shown()?.pauseReason.value ?? null),
90
109
  pause: () => currentPageLive()?.pause(),
91
110
  resume: () => currentPageLive()?.resume(),
92
111
  toggle: () => currentPageLive()?.toggle(),
93
112
  hold: hold.hold,
94
113
  release: hold.release,
95
- lastUpdatedAt: computed(
96
- () => currentPageLive()?.lastUpdatedAt.value ?? null,
97
- ),
98
- missedCount: computed(() => currentPageLive()?.missedCount.value ?? 0),
99
- missedDetail: computed(() => currentPageLive()?.missedDetail.value ?? null),
114
+ lastUpdatedAt: computed(() => shown()?.lastUpdatedAt.value ?? null),
115
+ missedCount: computed(() => shown()?.missedCount.value ?? 0),
116
+ missedDetail: computed(() => shown()?.missedDetail.value ?? null),
100
117
  setMissedDetail: (detail) => currentPageLive()?.setMissedDetail(detail),
101
- isPolling: computed(() => currentPageLive()?.isPolling.value ?? false),
118
+ isPolling: computed(() => shown()?.isPolling.value ?? false),
102
119
  };
103
120
  }
104
121
 
@@ -0,0 +1,30 @@
1
+ import { computed, type ComputedRef } from "vue";
2
+ import { usePageToolbar } from "../../build/composables/page-toolbar/pageToolbarContext";
3
+ import type { PageToolbarSize } from "../../types/page-toolbar";
4
+ import { PAGE_TOOLBAR_CONTROL_HEIGHT_CLASSES } from "../../build/composables/page-toolbar/toolbarControlHeight";
5
+
6
+ const DEFAULT_SIZE: PageToolbarSize = "sm";
7
+
8
+ /** The size of the `PageToolbar` holding a component (see {@link usePageToolbarSize}). */
9
+ export interface PageToolbarSizeState {
10
+ /** The toolbar's size, `sm` outside one. */
11
+ size: ComputedRef<PageToolbarSize>;
12
+ /** The height class its controls share at that size (`h-7` at `sm`). */
13
+ heightClass: ComputedRef<string>;
14
+ }
15
+
16
+ /**
17
+ * The size of the `PageToolbar` a module's component sits in, for a leading
18
+ * block (a scope chip, a picker) to stand as tall as the toggle and the
19
+ * period beside it.
20
+ */
21
+ export function usePageToolbarSize(): PageToolbarSizeState {
22
+ const toolbar = usePageToolbar();
23
+ const size = computed(() => toolbar?.size.value ?? DEFAULT_SIZE);
24
+ return {
25
+ size,
26
+ heightClass: computed(
27
+ () => PAGE_TOOLBAR_CONTROL_HEIGHT_CLASSES[size.value],
28
+ ),
29
+ };
30
+ }
@@ -4,8 +4,8 @@ import type { TableViewColumn } from "./types";
4
4
  /**
5
5
  * The few inputs the (unchanged) KanbanBoard needs beyond the generic
6
6
  * {@link TableViewDisplayContext}: the two-way group-by field shared with the
7
- * options-menu selector, and the raw edit/delete action configs for per-row rule
8
- * evaluation. Everything else (query, componentId, pageId, labelKey) is read from
7
+ * options-menu selector, the raw edit/delete action configs for per-row rule
8
+ * evaluation, and the callback told of a saved move. Everything else (query, componentId, pageId, labelKey) is read from
9
9
  * the generic context. Provided by TableView and injected by the kanban adapter
10
10
  * — slotted content is a descendant of TableView, so a native inject reaches it.
11
11
  */
@@ -13,6 +13,8 @@ export interface KanbanDisplayBridge {
13
13
  groupByField: Ref<string>;
14
14
  editAction?: boolean | RowActionConfig;
15
15
  deleteAction?: boolean | RowActionConfig;
16
+ /** A card was moved to another column and saved: the counts read again. */
17
+ onCardMoved?: () => void;
16
18
  }
17
19
 
18
20
  export const KANBAN_DISPLAY_BRIDGE_KEY: InjectionKey<KanbanDisplayBridge> =
@@ -19,12 +19,31 @@ export interface SidePanelTargetOptions {
19
19
  * `{ index }` (from 1) and `{ total }`.
20
20
  */
21
21
  positionLabel?: string;
22
- /** The open row is kept in the URL (`?record=`): on unless `false`. */
22
+ /**
23
+ * The open row is kept in the URL (`?record=`): on unless `false` for a
24
+ * side panel, off unless `true` for a drawer.
25
+ */
23
26
  deepLink?: boolean;
24
27
  /** The state drawn on a row that no longer exists (a 404). */
25
28
  notFound?: SidePanelNotFoundTexts;
26
29
  }
27
30
 
31
+ /**
32
+ * What a `drawer` target sets besides its component (backend
33
+ * `DrawerTargetOptions`): the row options of a side panel, for a drawer
34
+ * opened on a row, and its size.
35
+ */
36
+ export interface DrawerTargetOptions
37
+ extends Omit<SidePanelTargetOptions, "title"> {
38
+ /** Width of a `left` / `right` drawer: a modal's at that size; 600px without one. */
39
+ size?: ModalSize;
40
+ /**
41
+ * The DMS draws the row header (title, position, previous / next): on when
42
+ * `rowTitle` or `positionLabel` is set, unless `false`.
43
+ */
44
+ rowHeader?: boolean;
45
+ }
46
+
28
47
  /**
29
48
  * Fetches a file with the user's session and saves it (backend
30
49
  * `DownloadActionTarget`).
@@ -38,14 +57,14 @@ export interface DownloadActionTarget {
38
57
  }
39
58
 
40
59
  export type ActionTarget =
41
- | {
60
+ | ({
42
61
  type: "drawer";
43
62
  component: ComponentInfo;
44
63
  title?: string;
45
64
  description?: string;
46
65
  /** Edge the drawer slides in from (`bottom` by default). */
47
66
  direction?: DrawerDirection;
48
- }
67
+ } & DrawerTargetOptions)
49
68
  | {
50
69
  type: "modal";
51
70
  size?: ModalSize;
@@ -274,6 +274,8 @@ export interface TableViewConfig<T extends Data>
274
274
  location: string;
275
275
  enableTableExport?: boolean;
276
276
  archiveMode?: boolean;
277
+ /** Caption count while the archived rows show; "Archived: {count}" else. */
278
+ archivedCountLabel?: string;
277
279
  defaultFilters?: TableFilter[];
278
280
  columns: TableViewColumn[];
279
281
  labelKey?: string;
@@ -298,6 +300,8 @@ export interface TableViewConfig<T extends Data>
298
300
  displays?: TableViewDisplayConfig[];
299
301
  /** Display shown by default when the user has no saved preference. Defaults to the first display. */
300
302
  defaultDisplay?: string;
303
+ /** Display a phone opens on when the user has no saved choice; one of `displays`. */
304
+ mobileDisplay?: string;
301
305
  /** Expandable rows: caret column + detail band. */
302
306
  expandable?: TableViewExpandableConfig;
303
307
  /** How much the table draws around its rows. */
@@ -87,7 +87,11 @@ export interface TableViewDisplayActions<T> {
87
87
  canDeleteRow: (item: T) => boolean;
88
88
  add: () => void;
89
89
  edit: (item: T) => void;
90
- delete: (ids: string[]) => void;
90
+ /**
91
+ * Deletes the rows once confirmed. A display drawing rows the grid does not
92
+ * list (a kanban card) hands them in `rows`, so a dialog can name them.
93
+ */
94
+ delete: (ids: string[], rows?: T[]) => void;
91
95
  duplicate: (id: string) => void;
92
96
  details: (item: T) => void;
93
97
  /** What a click on the row does in the grid: its default action. */
@@ -150,6 +154,10 @@ export interface TableViewCardReorder {
150
154
  export interface TableViewCardConfig {
151
155
  fields?: string[];
152
156
  component?: ComponentInfo;
157
+ /** `cards` only: the narrowest a card gets, a CSS length (`230px` left out). */
158
+ minWidth?: string;
159
+ /** `cards` only: the height of a placeholder card, a CSS length. */
160
+ skeletonHeight?: string;
153
161
  }
154
162
 
155
163
  /**
@@ -180,6 +188,13 @@ export interface TableViewCardProps<T = Record<string, unknown>> {
180
188
  * caller who may edit: the card's move handle, to draw somewhere on it.
181
189
  */
182
190
  reorder?: TableViewCardReorder;
191
+ /**
192
+ * `cards` only: the row's actions as the grid row offers them (its inline
193
+ * buttons and its … menu, with their rules and confirmations), to draw
194
+ * with `<component :is="rowActions" />`. It draws nothing for a table with
195
+ * no row action, and stops its clicks from reaching the card.
196
+ */
197
+ rowActions?: Component;
183
198
  }
184
199
 
185
200
  /**
@@ -239,6 +254,14 @@ export interface TableViewDisplayContext<T = Record<string, unknown>> {
239
254
  pageId?: string;
240
255
  /** Per-instance options declared for this display in the backend config. */
241
256
  options?: Record<string, unknown>;
257
+ /**
258
+ * A self-managed display hands the rows it shows, in the order the user
259
+ * reads them (a board: column by column), each time they change: a row
260
+ * opened from it steps through them in its drawer or side panel ("3 of
261
+ * 12", previous / next, J and K). Without it, a self-managed display's row
262
+ * opens without steps. The shared list's displays need not call it.
263
+ */
264
+ setShownRows?: (rows: T[]) => void;
242
265
  /** Re-fetch the table view (shared list + tab counts + active display). */
243
266
  refresh: () => Promise<void> | void;
244
267
  /**
@@ -0,0 +1,10 @@
1
+ import { watchClosingLayers } from "../build/composables/containers/closingLayers";
2
+
3
+ /**
4
+ * Every dialog the DMS or a module renders lets the page be clicked as soon
5
+ * as it starts closing: one place for UModal, USlideover, UDrawer, the
6
+ * confirm dialogs and DynamicDrawer / DynamicModal alike.
7
+ */
8
+ export default defineDmsPlugin(() => {
9
+ watchClosingLayers(document);
10
+ });
@@ -0,0 +1,15 @@
1
+ import { LIVE_PAUSE_SOURCE } from "#dms-core/app/composables/live/livePauseSource";
2
+ import { currentPageLive } from "../build/composables/live/currentPageLive";
3
+
4
+ /**
5
+ * Gives the core layer the pause of the page on screen (its live
6
+ * controller), so a component's own polling (`useApiData`'s
7
+ * `refreshInterval`) stops with the page's blocks. The server renders no
8
+ * poll and installs no controller: it reads as never paused.
9
+ */
10
+ export default defineDmsPlugin(({ vueApp }) => {
11
+ vueApp.provide(
12
+ LIVE_PAUSE_SOURCE,
13
+ () => currentPageLive()?.isPaused.value ?? false,
14
+ );
15
+ });
@@ -2,10 +2,12 @@ import { registerDefaultDataTypes } from "../build/composables/data-types/regist
2
2
  import { registerDefaultFunctions } from "../build/composables/table-view/registerDefaultFunctions";
3
3
  import { registerFormFunctions } from "../build/composables/form/registerFormFunctions";
4
4
  import { registerBlockFunctions } from "../build/composables/blocks/registerBlockFunctions";
5
+ import { trackPageArrivals } from "../build/composables/table-view/utils/pageArrival";
5
6
 
6
7
  export default defineDmsPlugin(() => {
7
8
  registerDefaultDataTypes();
8
9
  registerDefaultFunctions();
9
10
  registerFormFunctions();
10
11
  registerBlockFunctions();
12
+ trackPageArrivals();
11
13
  });
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Why a number field keeps another value than the one typed: above its `max`,
3
- * below its `min`, or off its `step`.
3
+ * below its `min`, or off its `step` (when it snaps to it: `stepSnapping`).
4
4
  */
5
5
  export type InputNumberAdjustmentReason = "min" | "max" | "step";
6
6
 
7
7
  /**
8
8
  * What `DmsInputNumber` emits as `adjust` when it settles a typed number on
9
9
  * another value (on leaving the field, or on Enter): 1020 typed in a field of
10
- * steps of 50 gives `{ typed: 1020, value: 1000, reason: "step" }`.
10
+ * steps of 50 that snaps to them gives
11
+ * `{ typed: 1020, value: 1000, reason: "step" }`.
11
12
  */
12
13
  export interface InputNumberAdjustment {
13
14
  /** The number as typed. */
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The sizes a `PageToolbar` draws its controls at (`xs` 24px, `sm` 28px,
3
+ * `md` 32px tall), as `usePageToolbarSize()` reports them to a module's
4
+ * leading block. The same values as `PageToolbarSize` of
5
+ * `@antelopejs/interface-dms/base/page-toolbar`.
6
+ */
7
+ export type PageToolbarSize = "xs" | "sm" | "md";
@@ -24,8 +24,6 @@ export interface CustomRowAction {
24
24
  rule?: RowActionRule;
25
25
  /** Inline button instead of a menu entry (the backend serializes `isVisible`). */
26
26
  isVisible?: boolean;
27
- /** Legacy spelling of `isVisible`, still honoured. */
28
- visible?: boolean;
29
27
  /**
30
28
  * Preferred row-click target when its rule accepts the row: a double-click,
31
29
  * or a single click on a table with `rowClick: "single"`.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * What a drawer or modal opened on a row receives to step through the rows
3
+ * of the table (`navigation` prop): J and K do the same. The steps follow the
4
+ * open row's neighbours in the table's current order, past the rows loaded:
5
+ * rows arriving or leaving meanwhile (a live table) never make a step skip
6
+ * or revisit one.
7
+ */
8
+ export interface RowNavigation {
9
+ /** Position of the row among all the rows the table lists, from 0. */
10
+ index: number;
11
+ /** The rows the table lists: its count, beyond the page loaded. */
12
+ total: number;
13
+ hasPrev: boolean;
14
+ hasNext: boolean;
15
+ prev: () => void;
16
+ next: () => void;
17
+ }