@antelopejs/dms 0.9.8 → 0.10.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 (420) hide show
  1. package/CHANGELOG.md +102 -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 -0
  21. package/dist/config.js +5 -3
  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 +22 -89
  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 +1 -2
  41. package/dist/implementations/dms/manifest.js +2 -0
  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 +28 -48
  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 +24 -13
  129. package/dist/store/listing.js.map +1 -1
  130. package/dist/store/operations.js +4 -0
  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/validation/auth.schema.d.ts +2 -2
  139. package/dist/validation/member-invite.schema.d.ts +2 -10
  140. package/dist/validation/member-invite.schema.js +2 -15
  141. package/dist/validation/member-invite.schema.js.map +1 -1
  142. package/frontend-vue/dms.frontend.ts +24 -5
  143. package/frontend-vue/dms.server.ts +17 -0
  144. package/frontend-vue/layers/dms-auth/server/accounts.ts +3 -4
  145. package/frontend-vue/layers/dms-core/app/build/composables/renderNow.ts +34 -0
  146. package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +5 -1
  147. package/frontend-vue/layers/dms-core/app/build/utils/crossTab.ts +199 -0
  148. package/frontend-vue/layers/dms-core/app/build/utils/sharedRealtimeConnection.ts +410 -0
  149. package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +77 -12
  150. package/frontend-vue/layers/dms-core/app/composables/data-types/useDataType.ts +26 -0
  151. package/frontend-vue/layers/dms-core/app/composables/hydration/isHydratingComponent.ts +29 -0
  152. package/frontend-vue/layers/dms-core/app/composables/live/livePauseSource.ts +29 -0
  153. package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +150 -9
  154. package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +6 -4
  155. package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +27 -1
  156. package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +256 -119
  157. package/frontend-vue/layers/dms-core/app/composables/translation/useComposedText.ts +21 -8
  158. package/frontend-vue/layers/dms-core/app/composables/translation/useTranslation.ts +22 -5
  159. package/frontend-vue/layers/dms-core/app/plugins/dev-error-log.ts +16 -0
  160. package/frontend-vue/layers/dms-core/app/types/user.ts +6 -0
  161. package/frontend-vue/layers/dms-core/app/utils/composedText.ts +7 -2
  162. package/frontend-vue/layers/dms-core/app/utils/formatter.ts +166 -35
  163. package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +18 -1
  164. package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +134 -2
  165. package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +134 -2
  166. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +3 -2
  167. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +41 -21
  168. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +9 -0
  169. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +4 -19
  170. package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +11 -3
  171. package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
  172. package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionBar.vue +8 -0
  173. package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +14 -0
  174. package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +43 -82
  175. package/frontend-vue/layers/dms-layout/app/build/components/layout/SidebarUserMenu.vue +44 -3
  176. package/frontend-vue/layers/dms-layout/app/build/components/layout/UserIdentity.vue +11 -3
  177. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleCatalogTile.vue +132 -35
  178. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleManageMenu.vue +59 -0
  179. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleSourceLabel.vue +25 -0
  180. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreBanners.vue +73 -0
  181. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreOperation.vue +116 -0
  182. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +8 -3
  183. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +85 -207
  184. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +10 -3
  185. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTileSkeleton.vue +68 -0
  186. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +24 -8
  187. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxItem.vue +8 -5
  188. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/region/RegionTimeZoneInput.vue +1 -0
  189. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-permissions.ts +19 -0
  190. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/useRoleEditor.ts +2 -1
  191. package/frontend-vue/layers/dms-layout/app/build/composables/layout/useSidePanelWidth.ts +31 -70
  192. package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationGroupsOpen.ts +11 -1
  193. package/frontend-vue/layers/dms-layout/app/build/utils/dev-reload-stream.ts +81 -0
  194. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +232 -36
  195. package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +122 -14
  196. package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +32 -3
  197. package/frontend-vue/layers/dms-layout/app/composables/layout/usePageRecord.ts +50 -0
  198. package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +25 -7
  199. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
  200. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +51 -4
  201. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +194 -0
  202. package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +23 -3
  203. package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +4 -2
  204. package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
  205. package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +9 -0
  206. package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +9 -0
  207. package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +291 -0
  208. package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +87 -474
  209. package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +3 -0
  210. package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +6 -22
  211. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +2 -1
  212. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +14 -44
  213. package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
  214. package/frontend-vue/layers/dms-layout/app/theme/form-controls.ts +9 -4
  215. package/frontend-vue/layers/dms-layout/app/theme/surfaces.ts +22 -6
  216. package/frontend-vue/layers/dms-layout/app/types/module-store.ts +9 -0
  217. package/frontend-vue/layers/dms-layout/app/types/page.ts +15 -1
  218. package/frontend-vue/layers/dms-layout/app/utils/account-menu.ts +11 -0
  219. package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +57 -0
  220. package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +26 -0
  221. package/frontend-vue/layers/dms-layout/app/utils/pageRecord.ts +71 -0
  222. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +39 -14
  223. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +38 -13
  224. package/frontend-vue/layers/dms-ui/app/build/components/containers/TargetComponentTree.vue +31 -0
  225. package/frontend-vue/layers/dms-ui/app/build/components/containers/constants.ts +25 -0
  226. package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +244 -15
  227. package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +42 -25
  228. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowHeader.vue +153 -0
  229. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowNotFound.vue +44 -0
  230. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/drawerRowHeader.ts +35 -0
  231. package/frontend-vue/layers/dms-ui/app/build/components/form/FormEntries.vue +44 -9
  232. package/frontend-vue/layers/dms-ui/app/build/components/form/FormErrorSummary.vue +11 -4
  233. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +38 -5
  234. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldNotes.vue +29 -5
  235. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +3 -1
  236. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSections.vue +106 -27
  237. package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +12 -4
  238. package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +4 -2
  239. package/frontend-vue/layers/dms-ui/app/build/components/table/FiltersRow.vue +38 -1
  240. package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +55 -54
  241. package/frontend-vue/layers/dms-ui/app/build/components/table/RowSelection.vue +2 -1
  242. package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +396 -288
  243. package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +2 -1
  244. package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +2 -1
  245. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardAvatar.vue +59 -0
  246. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +19 -2
  247. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardRowActions.vue +38 -0
  248. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +148 -104
  249. package/frontend-vue/layers/dms-ui/app/build/components/table-view/LiveHoldBanner.vue +17 -2
  250. package/frontend-vue/layers/dms-ui/app/build/composables/actions/bulkSelection.ts +43 -9
  251. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowComponentOptions.ts +76 -0
  252. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +172 -27
  253. package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +270 -46
  254. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +2 -0
  255. package/frontend-vue/layers/dms-ui/app/build/composables/containers/closingLayers.ts +186 -0
  256. package/frontend-vue/layers/dms-ui/app/build/composables/containers/context.ts +14 -0
  257. package/frontend-vue/layers/dms-ui/app/build/composables/containers/drawerSize.ts +229 -0
  258. package/frontend-vue/layers/dms-ui/app/build/composables/containers/useResizableSize.ts +175 -0
  259. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +18 -35
  260. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/numberSuffix.ts +47 -0
  261. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +99 -24
  262. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerFieldTypes.ts +2 -1
  263. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationFilter.ts +75 -0
  264. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationRows.ts +71 -0
  265. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationValue.ts +39 -0
  266. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/twoLineCell.ts +3 -3
  267. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/useColumnValueRenderer.ts +6 -2
  268. package/frontend-vue/layers/dms-ui/app/build/composables/form/createFormFields.ts +15 -0
  269. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldSaved.ts +40 -0
  270. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +62 -0
  271. package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryContext.ts +5 -0
  272. package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +42 -7
  273. package/frontend-vue/layers/dms-ui/app/build/composables/form/formLayout.ts +106 -4
  274. package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
  275. package/frontend-vue/layers/dms-ui/app/build/composables/form/readonlyValue.ts +19 -0
  276. package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +27 -0
  277. package/frontend-vue/layers/dms-ui/app/build/composables/form/repeaterTable.ts +65 -0
  278. package/frontend-vue/layers/dms-ui/app/build/composables/form/serverFieldErrors.ts +49 -0
  279. package/frontend-vue/layers/dms-ui/app/build/composables/form/toasterClearance.ts +118 -0
  280. package/frontend-vue/layers/dms-ui/app/build/composables/form/useInstantForm.ts +12 -1
  281. package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +41 -3
  282. package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +10 -2
  283. package/frontend-vue/layers/dms-ui/app/build/composables/live/pageReadClock.ts +28 -0
  284. package/frontend-vue/layers/dms-ui/app/build/composables/live/shortcuts.ts +1 -1
  285. package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +17 -3
  286. package/frontend-vue/layers/dms-ui/app/build/composables/live/useUpdatedLabel.ts +38 -0
  287. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/pageToolbarContext.ts +75 -0
  288. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/toolbarControlHeight.ts +29 -0
  289. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparison.ts +52 -0
  290. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +21 -0
  291. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodShortLabels.ts +28 -0
  292. package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +1 -0
  293. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRelationFilterLabels.ts +73 -0
  294. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRowScrollAnchor.ts +106 -0
  295. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +9 -0
  296. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +1 -2
  297. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/archivedCount.ts +31 -0
  298. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/filterEditorValue.ts +38 -0
  299. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/formatFilterValue.ts +33 -2
  300. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/scrollEdges.ts +31 -0
  301. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useCardFields.ts +33 -0
  302. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useMobileDisplay.ts +48 -0
  303. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +41 -18
  304. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +3 -2
  305. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLinkedVisit.ts +78 -0
  306. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +135 -22
  307. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +56 -25
  308. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +77 -32
  309. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +86 -10
  310. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/bulkActions.ts +14 -4
  311. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/card.ts +127 -2
  312. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/compareValues.ts +13 -0
  313. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/expandedRows.ts +28 -0
  314. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/groupedRows.ts +114 -1
  315. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/linkedArrival.ts +308 -0
  316. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/locateRow.ts +106 -0
  317. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pageArrival.ts +34 -0
  318. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +17 -0
  319. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/quickFilters.ts +41 -33
  320. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +24 -8
  321. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableCounts.ts +113 -0
  322. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +27 -4
  323. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +22 -6
  324. package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
  325. package/frontend-vue/layers/dms-ui/app/build/utils/eyebrow.ts +12 -0
  326. package/frontend-vue/layers/dms-ui/app/build/utils/fieldOptionsI18n.ts +51 -11
  327. package/frontend-vue/layers/dms-ui/app/build/utils/inputWidth.ts +53 -0
  328. package/frontend-vue/layers/dms-ui/app/build/utils/numberAdjustment.ts +6 -3
  329. package/frontend-vue/layers/dms-ui/app/build/utils/relativeDate.ts +19 -5
  330. package/frontend-vue/layers/dms-ui/app/build/utils/scrollWithin.ts +99 -0
  331. package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +36 -14
  332. package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
  333. package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +9 -1
  334. package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +80 -32
  335. package/frontend-vue/layers/dms-ui/app/components/blocks/PageToolbarBlock.vue +137 -0
  336. package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +5 -0
  337. package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +16 -2
  338. package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +24 -5
  339. package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +41 -23
  340. package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +1 -1
  341. package/frontend-vue/layers/dms-ui/app/components/container-footer/ContainerFooter.vue +34 -0
  342. package/frontend-vue/layers/dms-ui/app/components/field-row/FieldRow.vue +25 -2
  343. package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +2 -2
  344. package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +118 -36
  345. package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +20 -1
  346. package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +13 -1
  347. package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +8 -3
  348. package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +53 -5
  349. package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +29 -5
  350. package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +45 -8
  351. package/frontend-vue/layers/dms-ui/app/components/form/components/InputPercentage.vue +28 -5
  352. package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +5 -0
  353. package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +19 -2
  354. package/frontend-vue/layers/dms-ui/app/components/form/components/InputTime.vue +2 -1
  355. package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +205 -65
  356. package/frontend-vue/layers/dms-ui/app/components/form/components/Repeater.vue +80 -30
  357. package/frontend-vue/layers/dms-ui/app/components/form/components/RepeaterTable.vue +203 -0
  358. package/frontend-vue/layers/dms-ui/app/components/form/components/Select.vue +4 -2
  359. package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +11 -2
  360. package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +79 -70
  361. package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +117 -21
  362. package/frontend-vue/layers/dms-ui/app/components/meter/Meter.vue +2 -1
  363. package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +113 -34
  364. package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +14 -2
  365. package/frontend-vue/layers/dms-ui/app/components/section-header/Eyebrow.vue +6 -1
  366. package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +13 -0
  367. package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +52 -3
  368. package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +2 -1
  369. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +45 -35
  370. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanDisplay.vue +4 -1
  371. package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +410 -96
  372. package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +33 -5
  373. package/frontend-vue/layers/dms-ui/app/composables/chart/apexCategoryLabels.ts +68 -0
  374. package/frontend-vue/layers/dms-ui/app/composables/chart/apexDatetimeBuckets.ts +306 -0
  375. package/frontend-vue/layers/dms-ui/app/composables/chart/formatValue.ts +4 -3
  376. package/frontend-vue/layers/dms-ui/app/composables/chart/heatmapColors.ts +93 -0
  377. package/frontend-vue/layers/dms-ui/app/composables/chart/localizeSeries.ts +62 -0
  378. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +116 -8
  379. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.types.ts +7 -0
  380. package/frontend-vue/layers/dms-ui/app/composables/containers/types.ts +84 -1
  381. package/frontend-vue/layers/dms-ui/app/composables/containers/useDrawer.ts +69 -8
  382. package/frontend-vue/layers/dms-ui/app/composables/form/types/field-saved.ts +11 -0
  383. package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +4 -0
  384. package/frontend-vue/layers/dms-ui/app/composables/form/types/functions.ts +1 -0
  385. package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +142 -21
  386. package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +11 -4
  387. package/frontend-vue/layers/dms-ui/app/composables/page-toolbar/usePageToolbarSize.ts +30 -0
  388. package/frontend-vue/layers/dms-ui/app/composables/table-view/kanban.ts +4 -2
  389. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +22 -3
  390. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +4 -0
  391. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +24 -1
  392. package/frontend-vue/layers/dms-ui/app/plugins/closing-layers.client.ts +10 -0
  393. package/frontend-vue/layers/dms-ui/app/plugins/live-pause-source.ts +15 -0
  394. package/frontend-vue/layers/dms-ui/app/plugins/register.ts +2 -0
  395. package/frontend-vue/layers/dms-ui/app/types/input-number.ts +3 -2
  396. package/frontend-vue/layers/dms-ui/app/types/page-toolbar.ts +7 -0
  397. package/frontend-vue/layers/dms-ui/app/types/row-action.ts +0 -2
  398. package/frontend-vue/layers/dms-ui/app/types/row-navigation.ts +17 -0
  399. package/frontend-vue/layers/dms-ui/app/types/table/query.ts +2 -1
  400. package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +3 -1
  401. package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
  402. package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +16 -4
  403. package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +16 -4
  404. package/frontend-vue/package.json +2 -2
  405. package/package.json +7 -6
  406. package/skills/dms/SKILL.md +1 -1
  407. package/skills/dms-auth/SKILL.md +5 -3
  408. package/skills/dms-best-practices/REFERENCE.md +2 -0
  409. package/skills/dms-best-practices/SKILL.md +4 -0
  410. package/skills/dms-module/SKILL.md +3 -2
  411. package/skills/dms-pages/REFERENCE.md +125 -16
  412. package/skills/dms-pages/SKILL.md +1 -1
  413. package/skills/dms-project/DEV-SERVERS.md +23 -1
  414. package/skills/dms-project/REFERENCE.md +1 -1
  415. package/skills/dms-project/SKILL.md +4 -1
  416. package/skills/dms-project/scripts/dms-dev.sh +205 -17
  417. package/dist/routes/startup-gate.d.ts +0 -7
  418. package/dist/routes/startup-gate.js +0 -39
  419. package/dist/routes/startup-gate.js.map +0 -1
  420. package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
@@ -13,22 +13,32 @@ import type {
13
13
  ConfirmValues,
14
14
  } from "../../../composables/confirm/types";
15
15
  import { resolveResponseToast } from "../../utils/responseWarning";
16
- import type { ContainerInstance } from "../../../composables/containers/types";
16
+ import type {
17
+ ContainerInstance,
18
+ DrawerOptions,
19
+ } from "../../../composables/containers/types";
20
+ import DmsRowHeader from "../../components/containers/row/RowHeader.vue";
21
+ import { drawerRowHeaderProps } from "../../components/containers/row/drawerRowHeader";
22
+ import DmsRowNotFound from "../../components/containers/row/RowNotFound.vue";
17
23
  import {
18
24
  type BulkSelection,
19
25
  bulkSelectionQuery,
26
+ isBulkAction,
27
+ isUrlTarget,
28
+ targetForRow,
20
29
  targetWithSelection,
21
30
  withQuery,
22
31
  } from "./bulkSelection";
23
32
  import {
24
33
  listenRowSteps,
25
- type RowNavigation,
26
34
  type RowNavigationSource,
27
- rowNavigation,
28
35
  type ShowRow,
36
+ trackRowNavigation,
29
37
  } from "./rowNavigation";
38
+ import type { RowNavigation } from "../../../types/row-navigation";
30
39
  import {
31
40
  clearWrittenRecord,
41
+ hasMissingRowState,
32
42
  isDeepLinkAction,
33
43
  resolveDeepLinkedRow,
34
44
  writeRecordId,
@@ -57,8 +67,17 @@ import {
57
67
  resolveTextVariables,
58
68
  } from "../../utils/urlVariables";
59
69
  import { currentPageUrlValues } from "../page/pageUrlValues";
70
+ import { readBulkCount } from "../table-view/utils/bulkActions";
60
71
  import { type DownloadFetch, saveDownload } from "../../utils/fileDownload";
61
- import { interpolateRowText } from "#dms-core/app/utils/url-interpolation";
72
+ import {
73
+ interpolateRowText,
74
+ namesRowField,
75
+ } from "#dms-core/app/utils/url-interpolation";
76
+ import { dropRowTokens } from "../../utils/urlVariables";
77
+ import { fillRowChildren, fillRowOptions } from "./rowComponentOptions";
78
+ import { resolveComponentChildren } from "../../../utils/componentTree";
79
+ import TargetComponentTree from "../../components/containers/TargetComponentTree.vue";
80
+ import { dmsOwnDrawer } from "../containers/context";
62
81
 
63
82
  /** Runs a confirmed action from inside its dialog (see `onConfirm`). */
64
83
  type ConfirmedRun = NonNullable<ConfirmOptions["onConfirm"]>;
@@ -128,10 +147,17 @@ interface TargetRunExtras {
128
147
  selection?: BulkSelection;
129
148
  /** The open row is reflected in the URL. */
130
149
  deepLink?: boolean;
150
+ /** Fill-ins of the confirmation's texts besides the row's: `{ count }`. */
151
+ textParams?: Data;
152
+ /** A bulk action run from a row: its drawer, modal or panel gets the row too. */
153
+ keepsRow?: boolean;
131
154
  }
132
155
 
133
156
  type SidePanelTarget = ActionTarget & { type: "sidePanel" };
157
+ type DrawerTarget = ActionTarget & { type: "drawer" };
134
158
  type DownloadTarget = ActionTarget & { type: "download" };
159
+ /** A target opening a row with the DMS's row header and not-found state. */
160
+ type RowTarget = SidePanelTarget | DrawerTarget;
135
161
 
136
162
  /** A side panel to open, and on what. */
137
163
  interface SidePanelTargetRun extends TargetRunExtras {
@@ -165,6 +191,17 @@ interface ComponentTargetRun extends TargetRunExtras {
165
191
  description: string;
166
192
  containerId: string;
167
193
  rowData?: Data;
194
+ /** Id of a row the link names but the route answered 404 for (a drawer). */
195
+ missingId?: string;
196
+ }
197
+
198
+ /**
199
+ * Whether a drawer draws the DMS's row header (the title naming the row, its
200
+ * position, previous / next): asked for, or implied by the texts it words.
201
+ */
202
+ function hasRowHeader(target: ComponentTargetRun["target"]): boolean {
203
+ if (target.type !== "drawer") return false;
204
+ return target.rowHeader ?? !!(target.rowTitle || target.positionLabel);
168
205
  }
169
206
 
170
207
  /**
@@ -184,12 +221,17 @@ export function useActionTargets(config: ActionTargetsConfig) {
184
221
  const { handleApiError } = config;
185
222
  const fetchRaw = useAuthFetch().$authFetch.raw as unknown as DownloadFetch;
186
223
 
187
- /** Calls an `api` target and tells how it went (rejects on a failure). */
224
+ /**
225
+ * Calls an `api` target and tells how it went (rejects on a failure). On a
226
+ * selection, the toast counts the rows the route says it changed, or every
227
+ * selected row when it does not say.
228
+ */
188
229
  const requestApiTarget = async (
189
230
  target: ActionTarget & { type: "api" },
190
231
  url: string,
191
232
  rowData?: Data,
192
233
  values: ConfirmValues = {},
234
+ selection?: BulkSelection,
193
235
  ) => {
194
236
  // The confirmation's values join the declared body, the input winning.
195
237
  const body = { ...target.body, ...values };
@@ -203,6 +245,9 @@ export function useActionTargets(config: ActionTargetsConfig) {
203
245
  if (target.copy) {
204
246
  await copyToClipboard(String(response?.[target.copy] ?? ""));
205
247
  }
248
+ const toastParams = selection
249
+ ? { ...rowData, count: readBulkCount(response) ?? selection.count }
250
+ : (rowData ?? {});
206
251
  toast.add(
207
252
  resolveResponseToast(
208
253
  response,
@@ -211,7 +256,7 @@ export function useActionTargets(config: ActionTargetsConfig) {
211
256
  // Pluralized on a bulk action's `{ count }`, like its dialog.
212
257
  title: resolveConfirmText(
213
258
  target.successMessage,
214
- rowData ?? {},
259
+ toastParams,
215
260
  t as ConfirmTranslate,
216
261
  ),
217
262
  },
@@ -229,9 +274,10 @@ export function useActionTargets(config: ActionTargetsConfig) {
229
274
  url: string,
230
275
  label: string,
231
276
  rowData?: Data,
277
+ selection?: BulkSelection,
232
278
  ) => {
233
279
  try {
234
- await requestApiTarget(target, url, rowData);
280
+ await requestApiTarget(target, url, rowData, {}, selection);
235
281
  } catch (error: unknown) {
236
282
  handleApiError(error, label ? processI18n(label, rowData) : undefined);
237
283
  }
@@ -307,26 +353,51 @@ export function useActionTargets(config: ActionTargetsConfig) {
307
353
  }
308
354
  };
309
355
 
356
+ // A drawer remembers the size the user gives it per action: per table and
357
+ // component, whatever row it opens on.
310
358
  const openContainer = (
311
359
  run: ComponentTargetRun,
312
360
  component: Component,
313
- componentOptions: Record<string, unknown>,
361
+ options: Pick<
362
+ DrawerOptions,
363
+ "title" | "componentOptions" | "headerComponentOptions"
364
+ >,
314
365
  ): ContainerInstance => {
315
- const options = {
316
- title: run.title,
366
+ const containerOptions = {
367
+ ...options,
317
368
  description: run.description,
318
369
  containerId: run.containerId,
319
370
  component,
320
- componentOptions,
321
371
  };
322
- return run.target.type === "modal"
323
- ? openModal({ ...options, size: run.target.size })
324
- : openDrawer({ ...options, direction: run.target.direction });
372
+ if (run.target.type === "modal") {
373
+ return openModal({ ...containerOptions, size: run.target.size });
374
+ }
375
+ return openDrawer(
376
+ dmsOwnDrawer({
377
+ ...containerOptions,
378
+ direction: run.target.direction,
379
+ size: run.target.size,
380
+ sizeKey: `${config.pageId}-${config.componentId}-${run.target.component?.componentName}`,
381
+ ...(options.headerComponentOptions && {
382
+ headerComponent: DmsRowHeader,
383
+ }),
384
+ }),
385
+ );
325
386
  };
326
387
 
327
388
  const resolveTargetComponent = (componentName: string): Component =>
328
389
  (resolveDmsComponent(componentName) || componentName) as Component;
329
390
 
391
+ // A container block (a Card, a VStack, a Tab) mounts its children as a
392
+ // page does; a single block is mounted as it is.
393
+ const targetComponentOf = (
394
+ componentName: string,
395
+ info: ComponentInfo,
396
+ ): Component =>
397
+ info.children?.length
398
+ ? TargetComponentTree
399
+ : resolveTargetComponent(componentName);
400
+
330
401
  const rowIdOf = (row?: Data): string | undefined => {
331
402
  if (!row) return undefined;
332
403
  const idKey =
@@ -377,18 +448,41 @@ export function useActionTargets(config: ActionTargetsConfig) {
377
448
  };
378
449
  };
379
450
 
451
+ // The target's component options for the row shown, its `{{row.X}}`
452
+ // tokens filled (`{{row.id}}` being the row's id); without a row (a bulk
453
+ // selection, a toolbar button) they are handed as declared.
454
+ const targetOptionsFor = (target: ActionTarget, row?: Data): Data => {
455
+ const { component } = target as { component?: ComponentInfo<Data> };
456
+ const id = rowIdOf(row);
457
+ const tokens = row && id !== undefined ? { ...row, id } : row;
458
+ const declared = component?.options ?? {};
459
+ const options = tokens ? fillRowOptions(declared, tokens) : declared;
460
+ const children = component?.children;
461
+ if (!component?.componentName || !children?.length) return options;
462
+ // The container's children read the row through the same tokens.
463
+ const targetTree: ResolvedComponentInfo = {
464
+ id: component.componentName,
465
+ component: resolveTargetComponent(component.componentName),
466
+ componentName: component.componentName,
467
+ children: resolveComponentChildren(
468
+ tokens ? fillRowChildren(children, tokens) : children,
469
+ ),
470
+ };
471
+ return { ...options, targetTree };
472
+ };
473
+
380
474
  const rowRecorder = (deepLink?: boolean) => {
381
475
  const recordId = recordRecorder(deepLink);
382
476
  return (row?: Data): void => recordId(rowIdOf(row));
383
477
  };
384
478
 
385
479
  // The header names the open row when the target words it from its fields.
386
- const rowTitleOf = (target: SidePanelTarget, row?: Data) =>
480
+ const rowTitleOf = (target: RowTarget, row?: Data) =>
387
481
  row && target.rowTitle
388
482
  ? resolveConfirmText(target.rowTitle, row, t as ConfirmTranslate)
389
483
  : undefined;
390
484
 
391
- const notFoundOf = (target: SidePanelTarget) => ({
485
+ const notFoundOf = (target: RowTarget) => ({
392
486
  title: processI18n(target.notFound?.title || `$${NOT_FOUND_TITLE_KEY}`),
393
487
  description: processI18n(
394
488
  target.notFound?.description || `$${NOT_FOUND_DESCRIPTION_KEY}`,
@@ -407,15 +501,16 @@ export function useActionTargets(config: ActionTargetsConfig) {
407
501
  const componentName = run.target.component?.componentName;
408
502
  if (!componentName) return;
409
503
  const session = Symbol(componentName);
410
- const recordId = recordRecorder(run.deepLink && !run.selection);
504
+ const recordId = recordRecorder(
505
+ run.deepLink && (!run.selection || run.keepsRow),
506
+ );
411
507
  const panel = {
412
508
  session,
413
509
  ownerKey,
414
510
  title: run.title,
415
511
  positionLabel: run.target.positionLabel,
416
- component: resolveTargetComponent(componentName),
512
+ component: targetComponentOf(componentName, run.target.component),
417
513
  componentOptions: {
418
- ...run.target.component?.options,
419
514
  pageId: config.pageId,
420
515
  componentId: config.componentId,
421
516
  containerId: run.containerId,
@@ -434,16 +529,30 @@ export function useActionTargets(config: ActionTargetsConfig) {
434
529
  function showRow(row?: Data) {
435
530
  openRowSidePanel({
436
531
  ...panel,
532
+ componentOptions: {
533
+ ...targetOptionsFor(run.target, row),
534
+ ...panel.componentOptions,
535
+ },
437
536
  rowTitle: rowTitleOf(run.target, row),
438
537
  row,
439
538
  rowKey: rowIdOf(row),
440
539
  source: row ? config.rowNavigation : undefined,
441
- showRow,
540
+ // A step that loads a page first lands after it: not on a panel
541
+ // closed or replaced meanwhile.
542
+ showRow: whileShown(session, showRow),
442
543
  });
443
544
  recordId(rowIdOf(row));
444
545
  }
445
546
  function showMissing(id: string) {
446
- openRowSidePanel({ ...panel, notFound: notFoundOf(run.target), showRow });
547
+ openRowSidePanel({
548
+ ...panel,
549
+ componentOptions: {
550
+ ...targetOptionsFor(run.target),
551
+ ...panel.componentOptions,
552
+ },
553
+ notFound: notFoundOf(run.target),
554
+ showRow,
555
+ });
447
556
  recordId(id);
448
557
  }
449
558
  if (run.missingId) return showMissing(run.missingId);
@@ -456,21 +565,49 @@ export function useActionTargets(config: ActionTargetsConfig) {
456
565
  const handleComponentTarget = (run: ComponentTargetRun) => {
457
566
  const componentName = run.target.component?.componentName;
458
567
  if (!componentName) return;
459
- const component = resolveTargetComponent(componentName);
568
+ if (run.missingId) return showMissingInDrawer(run, run.missingId);
569
+ const component = targetComponentOf(componentName, run.target.component);
460
570
  const source = config.rowNavigation;
571
+ const drawerTarget = run.target.type === "drawer" ? run.target : undefined;
572
+ const withRowHeader = hasRowHeader(run.target) && !run.selection;
461
573
  let instance: ContainerInstance | undefined;
462
574
  let navigation: RowNavigation | undefined;
575
+ let stopNavigation: (() => void) | undefined;
463
576
  // Set once the container closes: a row read that resolves later neither
464
577
  // writes `?record=` back nor patches the closed container.
465
578
  let closed = false;
466
- const showRowById = rowShower((row) => {
579
+ const showRowOpen = (row: Data) => {
467
580
  if (!closed) showRow(row);
468
- });
581
+ };
582
+ const showRowById = rowShower(showRowOpen);
583
+ // The position and the steps follow the table (a reload, live rows, the
584
+ // row located past the page loaded): the open container is patched with
585
+ // them.
586
+ const trackNavigation = (row?: Data) => {
587
+ stopNavigation?.();
588
+ stopNavigation = undefined;
589
+ navigation = undefined;
590
+ if (!row || !source) return;
591
+ // The first navigation goes out with the row's own options.
592
+ let isShowing = true;
593
+ // A step that loads a page first lands after it, maybe closed by then.
594
+ stopNavigation = trackRowNavigation(source, row, showRowOpen, (next) => {
595
+ navigation = next;
596
+ if (isShowing || !instance || closed) return;
597
+ instance.patch({
598
+ componentOptions: optionsOf(row),
599
+ ...(withRowHeader && { headerComponentOptions: headerFor(row) }),
600
+ });
601
+ });
602
+ isShowing = false;
603
+ };
469
604
  const optionsFor = (row?: Data) => {
470
- navigation =
471
- row && source ? rowNavigation(source, row, showRow) : undefined;
605
+ trackNavigation(row);
606
+ return optionsOf(row);
607
+ };
608
+ const optionsOf = (row?: Data) => {
472
609
  return {
473
- ...run.target.component?.options,
610
+ ...targetOptionsFor(run.target, row),
474
611
  pageId: config.pageId,
475
612
  componentId: config.componentId,
476
613
  containerId: run.containerId,
@@ -484,26 +621,64 @@ export function useActionTargets(config: ActionTargetsConfig) {
484
621
  },
485
622
  };
486
623
  };
624
+ // A drawer words its title from the row (`rowTitle`); with the row
625
+ // header, the DMS draws it with the position and previous / next. Read
626
+ // after `optionsFor`, which sets the row's navigation.
627
+ const titleFor = (row?: Data) =>
628
+ (drawerTarget && rowTitleOf(drawerTarget, row)) || run.title;
629
+ const headerFor = (row?: Data) =>
630
+ withRowHeader
631
+ ? drawerRowHeaderProps(
632
+ { positionLabel: drawerTarget?.positionLabel, navigation },
633
+ titleFor(row),
634
+ )
635
+ : undefined;
487
636
  const recordRow = rowRecorder(run.deepLink);
488
637
  function showRow(row: Data) {
638
+ const componentOptions = optionsFor(row);
489
639
  instance?.patch({
490
- componentOptions: optionsFor(row),
640
+ title: titleFor(row),
641
+ componentOptions,
491
642
  componentKey: rowIdOf(row),
643
+ ...(withRowHeader && { headerComponentOptions: headerFor(row) }),
492
644
  });
493
645
  recordRow(row);
494
646
  }
495
- instance = openContainer(run, component, optionsFor(run.rowData));
647
+ const componentOptions = optionsFor(run.rowData);
648
+ instance = openContainer(run, component, {
649
+ title: titleFor(run.rowData),
650
+ componentOptions,
651
+ headerComponentOptions: headerFor(run.rowData),
652
+ });
496
653
  recordRow(run.rowData);
497
654
  // `showRow` may move an unlisted row's container onto a listed one.
498
655
  const stopSteps = source ? listenRowSteps(() => navigation) : undefined;
499
656
  const settle = () => {
500
657
  closed = true;
501
658
  stopSteps?.();
659
+ stopNavigation?.();
502
660
  recordRow(undefined);
503
661
  };
504
662
  instance.result.then(settle, settle);
505
663
  };
506
664
 
665
+ // A drawer opened on a row its link names but its route answered 404 for:
666
+ // under its title, the state in place of the component, a close button.
667
+ // Closing it removes the link, as closing it on a row does.
668
+ function showMissingInDrawer(run: ComponentTargetRun, id: string): void {
669
+ if (run.target.type !== "drawer") return;
670
+ const recordId = recordRecorder(run.deepLink);
671
+ let instance: ContainerInstance | undefined;
672
+ const notFound = notFoundOf(run.target);
673
+ instance = openContainer(run, DmsRowNotFound, {
674
+ title: run.title,
675
+ componentOptions: { ...notFound, onClose: () => instance?.close() },
676
+ });
677
+ recordId(id);
678
+ const settle = () => recordId(undefined);
679
+ instance.result.then(settle, settle);
680
+ }
681
+
507
682
  type TargetHandler = (
508
683
  target: ActionTarget,
509
684
  label: string,
@@ -520,7 +695,7 @@ export function useActionTargets(config: ActionTargetsConfig) {
520
695
  (target as { description?: string }).description || "",
521
696
  ),
522
697
  containerId: `${config.pageId}-${config.componentId}-${target.type}`,
523
- rowData: extras?.selection ? undefined : rowData,
698
+ rowData: extras?.selection && !extras.keepsRow ? undefined : rowData,
524
699
  ...extras,
525
700
  });
526
701
 
@@ -529,7 +704,7 @@ export function useActionTargets(config: ActionTargetsConfig) {
529
704
  target: target as SidePanelTarget,
530
705
  title: processI18n((target as SidePanelTarget).title || label),
531
706
  containerId: `${config.pageId}-${config.componentId}-${target.type}`,
532
- rowData: extras?.selection ? undefined : rowData,
707
+ rowData: extras?.selection && !extras.keepsRow ? undefined : rowData,
533
708
  ...extras,
534
709
  });
535
710
 
@@ -548,10 +723,10 @@ export function useActionTargets(config: ActionTargetsConfig) {
548
723
  }
549
724
  window.location.href = url;
550
725
  },
551
- api: async (target, label, rowData) => {
726
+ api: async (target, label, rowData, extras) => {
552
727
  const apiTarget = target as ActionTarget & { type: "api" };
553
728
  const url = targetUrl(apiTarget.url, rowData);
554
- await handleApiTarget(apiTarget, url, label, rowData);
729
+ await handleApiTarget(apiTarget, url, label, rowData, extras?.selection);
555
730
  },
556
731
  exportJob: async (target, _label, rowData) => {
557
732
  const jobTarget = target as ActionTarget & { type: "exportJob" };
@@ -581,16 +756,21 @@ export function useActionTargets(config: ActionTargetsConfig) {
581
756
  const confirmedRuns: Partial<
582
757
  Record<
583
758
  ActionTarget["type"],
584
- (target: ActionTarget, rowData?: Data) => ConfirmedRun
759
+ (
760
+ target: ActionTarget,
761
+ rowData?: Data,
762
+ extras?: TargetRunExtras,
763
+ ) => ConfirmedRun
585
764
  >
586
765
  > = {
587
- api: (target, rowData) => (values) => {
766
+ api: (target, rowData, extras) => (values) => {
588
767
  const apiTarget = target as ActionTarget & { type: "api" };
589
768
  return requestApiTarget(
590
769
  apiTarget,
591
770
  targetUrl(apiTarget.url, rowData),
592
771
  rowData,
593
772
  values,
773
+ extras?.selection,
594
774
  );
595
775
  },
596
776
  };
@@ -621,9 +801,9 @@ export function useActionTargets(config: ActionTargetsConfig) {
621
801
  await handler(target, label, rowData, extras);
622
802
  return;
623
803
  }
624
- const runInside = confirmedRuns[target.type]?.(target, rowData);
804
+ const runInside = confirmedRuns[target.type]?.(target, rowData, extras);
625
805
  const isConfirmed = await confirmAction(declared, {
626
- row: rowData,
806
+ row: extras?.textParams ? { ...rowData, ...extras.textParams } : rowData,
627
807
  urlParams: rowUrlParams(rowData),
628
808
  run: runInside,
629
809
  });
@@ -653,14 +833,45 @@ export function useActionTargets(config: ActionTargetsConfig) {
653
833
  );
654
834
  };
655
835
 
836
+ // A bulk action offered on a row runs on that row as the selection bar runs
837
+ // on several: a URL target, and a `from` confirmation, carry `?ids=<id>` —
838
+ // unless the URL names the row itself (`{{row._id}}`), which fills it.
839
+ const rowRun = (action: CustomRowAction, rowData?: Data) => {
840
+ const id = rowIdOf(rowData);
841
+ if (!isBulkAction(action) || id === undefined) {
842
+ return { target: action.target, confirm: action.confirm, row: rowData };
843
+ }
844
+ const selection: BulkSelection = { ids: [id], count: 1 };
845
+ const query = bulkSelectionQuery(selection);
846
+ const { confirm } = action;
847
+ // Its texts count one row, as the bar's count the selection ("Resolve
848
+ // this ticket?"): its dialog, and the toast of a URL target. A drawer,
849
+ // modal or side panel receives the row as it is, and the selection of
850
+ // that row as from the bar.
851
+ const textParams = { count: selection.count };
852
+ return {
853
+ target: targetForRow(action.target, query),
854
+ textParams,
855
+ selection: isUrlTarget(action.target) ? undefined : selection,
856
+ row: isUrlTarget(action.target) ? { ...rowData, ...textParams } : rowData,
857
+ confirm:
858
+ confirm && isConfirmFrom(confirm) && !namesRowField(confirm.from)
859
+ ? { from: withQuery(confirm.from, query) }
860
+ : confirm,
861
+ };
862
+ };
863
+
656
864
  const handleCustomRowAction = (action: CustomRowAction, rowData?: Data) => {
657
- void handleActionTarget(
658
- action.target,
659
- action.label,
865
+ const { target, confirm, row, textParams, selection } = rowRun(
866
+ action,
660
867
  rowData,
661
- action.confirm,
662
- { deepLink: isDeepLinkAction(action) },
663
868
  );
869
+ void handleActionTarget(target, action.label, row, confirm, {
870
+ deepLink: isDeepLinkAction(action),
871
+ textParams,
872
+ selection,
873
+ keepsRow: !!selection,
874
+ });
664
875
  };
665
876
 
666
877
  /**
@@ -675,7 +886,7 @@ export function useActionTargets(config: ActionTargetsConfig) {
675
886
  const query = bulkSelectionQuery(selection);
676
887
  const declared =
677
888
  action.confirm && isConfirmFrom(action.confirm)
678
- ? { from: withQuery(action.confirm.from, query) }
889
+ ? { from: withQuery(dropRowTokens(action.confirm.from), query) }
679
890
  : action.confirm;
680
891
  void handleActionTarget(
681
892
  targetWithSelection(action.target, query),
@@ -687,11 +898,24 @@ export function useActionTargets(config: ActionTargetsConfig) {
687
898
  };
688
899
 
689
900
  /**
690
- * Opens a side panel action on a row its link names but its route answered
691
- * 404 for: the panel says the row is gone, and closing it removes the link.
692
- * Another target opens nothing: no row, no drawer.
901
+ * Opens a side panel action, or a drawer wording its `notFound` state, on a
902
+ * row its link names but its route answered 404 for: the container says the
903
+ * row is gone, and closing it removes the link. Another target opens
904
+ * nothing: no row, no container.
693
905
  */
694
906
  const handleMissingRow = (action: CustomRowAction, id: string) => {
907
+ if (!hasMissingRowState(action)) return;
908
+ if (action.target.type === "drawer") {
909
+ handleComponentTarget({
910
+ target: action.target,
911
+ title: processI18n(action.target.title || action.label),
912
+ description: processI18n(action.target.description || ""),
913
+ containerId: `${config.pageId}-${config.componentId}-${action.target.type}`,
914
+ deepLink: isDeepLinkAction(action),
915
+ missingId: id,
916
+ });
917
+ return;
918
+ }
695
919
  if (action.target.type !== "sidePanel") return;
696
920
  handleSidePanelTarget({
697
921
  target: action.target,
@@ -125,6 +125,8 @@ export function resolveConfirmDialog(
125
125
  description: text(dialog.description),
126
126
  confirmLabel: text(dialog.confirmLabel),
127
127
  cancelLabel: text(dialog.cancelLabel),
128
+ // The text to type, the row's name more often than not ("{name}").
129
+ confirmText: text(dialog.confirmText),
128
130
  impact: dialog.impact?.map((entry) => ({
129
131
  ...entry,
130
132
  label: text(entry.label) ?? "",