@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
@@ -11,6 +11,12 @@ import {
11
11
  import { usePageRecordLabel } from "#dms-core/app/composables/page/usePageRecordLabel";
12
12
  import type { CategoryInfo } from "../../../types/page";
13
13
  import { resolveModuleLandingSlug } from "../../../utils/moduleLanding";
14
+ import {
15
+ foldBreadcrumb,
16
+ isFoldedCrumbs,
17
+ withParentCrumb,
18
+ } from "../../../utils/breadcrumb";
19
+ import { fillRouteParams } from "../../../utils/pageQuery";
14
20
 
15
21
  const HOME_ICON = "i-ph-house-light";
16
22
  /** Breadcrumb slot of the folded middle crumbs on small screens. */
@@ -130,6 +136,26 @@ function moduleLandingSlug(
130
136
  );
131
137
  }
132
138
 
139
+ /**
140
+ * The crumb of the page the current one is a detail of (`parentSlug`), its
141
+ * route parameters filled from the current page's: none when it is missing,
142
+ * out of reach, or names a parameter the current page lacks.
143
+ */
144
+ const parentCrumb = computed((): BreadcrumbItem | null => {
145
+ const current = siteLayout.findMatchingRoute(route.path);
146
+ const parentSlug = current?.metadata.parentSlug;
147
+ if (!parentSlug) return null;
148
+ const path = fillRouteParams(parentSlug, current.params);
149
+ if (path.includes(":")) return null;
150
+ const parent = siteLayout.findMatchingRoute(path)?.metadata;
151
+ if (!parent || parent.hasAccess === false) return null;
152
+ const target = buildBreadcrumbTarget(path, parent, route.query);
153
+ return {
154
+ label: processI18n(parent.displayName),
155
+ ...(target ? { to: target } : {}),
156
+ };
157
+ });
158
+
133
159
  const breadcrumb = computed((): BreadcrumbItem[] => {
134
160
  const base: BreadcrumbItem[] = [
135
161
  { icon: HOME_ICON, to: homepage || "/", "aria-label": t("header.home") },
@@ -143,7 +169,7 @@ const breadcrumb = computed((): BreadcrumbItem[] => {
143
169
 
144
170
  const paths = route.path.split("/").filter((path) => path);
145
171
 
146
- const crumbs = base.concat(
172
+ const pathCrumbs = base.concat(
147
173
  paths
148
174
  .map((path, index): BreadcrumbItem | null => {
149
175
  const to = `/${paths.slice(0, index + 1).join("/")}`;
@@ -170,6 +196,7 @@ const breadcrumb = computed((): BreadcrumbItem[] => {
170
196
  })
171
197
  .filter((item) => item !== null),
172
198
  );
199
+ const crumbs = withParentCrumb(pathCrumbs, parentCrumb.value);
173
200
 
174
201
  // Written as the row has it: an email address keeps its lowercase.
175
202
  const last = crumbs.at(-1);
@@ -183,32 +210,25 @@ const breadcrumb = computed((): BreadcrumbItem[] => {
183
210
  return crumbs;
184
211
  });
185
212
 
213
+ // The middle crumbs fold into a "…" menu (see the `#ellipsis` slot) so a
214
+ // deep page still fits a phone without losing the way back up.
186
215
  const mobileBreadcrumb = computed(
187
- (): (BreadcrumbItem & { children?: DropdownMenuItem[] })[] => {
188
- if (breadcrumb.value.length <= 2) {
189
- return breadcrumb.value;
190
- }
191
-
192
- // The middle crumbs fold into a "…" menu (see the `#ellipsis` slot) so a
193
- // deep page still fits a phone without losing the way back up.
194
- return [
195
- breadcrumb.value[0]!,
196
- {
197
- slot: MOBILE_ELLIPSIS_SLOT,
198
- children: breadcrumb.value.slice(1, -1).map((x) => ({
199
- label: x.label,
200
- to: x.to,
201
- })),
202
- },
203
- breadcrumb.value.slice(-1)[0]!,
204
- ];
205
- },
216
+ (): (BreadcrumbItem & { children?: DropdownMenuItem[] })[] =>
217
+ foldBreadcrumb(breadcrumb.value, MOBILE_ELLIPSIS_SLOT).map((crumb) => {
218
+ if (!isFoldedCrumbs(crumb)) return crumb;
219
+ return {
220
+ slot: crumb.slot,
221
+ children: crumb.children.map((x) => ({ label: x.label, to: x.to })),
222
+ };
223
+ }),
206
224
  );
207
225
  </script>
208
226
 
209
227
  <template>
210
228
  <div data-dms-persistent-header>
211
- <UDashboardNavbar>
229
+ <!-- The phone toggle below is the only one: the navbar's own would sit
230
+ next to it. -->
231
+ <UDashboardNavbar :toggle="false">
212
232
  <template #leading>
213
233
  <UButton
214
234
  class="lg:hidden"
@@ -5,6 +5,7 @@ import PageHeaderActionsOutlet from "./PageHeaderActionsOutlet.vue";
5
5
  import PageSkeleton from "./PageSkeleton.vue";
6
6
  import type { LayoutHeaderAction } from "./PageHeaderActionBar.vue";
7
7
  import { providePageHeaderActions } from "../../../composables/layout/usePageHeaderActions";
8
+ import type { PageRecordSource } from "../../../composables/layout/usePageRecord";
8
9
 
9
10
  interface Props {
10
11
  hideHeader?: boolean;
@@ -18,6 +19,10 @@ interface Props {
18
19
  * when one declares an `availability` (`headerActionsQuery`).
19
20
  */
20
21
  headerActionsQuery?: Record<string, string>;
22
+ /** The route parameters (`:id`) they were resolved for, likewise. */
23
+ headerActionsParams?: Record<string, string>;
24
+ /** The record the page shows, which header buttons may act on (`record`). */
25
+ record?: PageRecordSource;
21
26
  /** Space between the page header and the page. */
22
27
  headerClass?: string;
23
28
  }
@@ -29,6 +34,8 @@ const props = withDefaults(defineProps<Props>(), {
29
34
  description: undefined,
30
35
  headerActions: () => [],
31
36
  headerActionsQuery: undefined,
37
+ headerActionsParams: undefined,
38
+ record: undefined,
32
39
  headerClass: "pb-6",
33
40
  });
34
41
 
@@ -67,6 +74,8 @@ const PAGE_SLOT_CLASS =
67
74
  <PageHeaderActionsOutlet
68
75
  :actions="props.headerActions"
69
76
  :served-query="props.headerActionsQuery"
77
+ :served-params="props.headerActionsParams"
78
+ :record="props.record"
70
79
  />
71
80
  </template>
72
81
  </PageHeader>
@@ -14,13 +14,10 @@ import { useKeyboardPlatform } from "#dms-ui/app/composables/global/keyboardPlat
14
14
  import { useMediaQuery } from "@vueuse/core";
15
15
  import DmsSidebarUserMenu from "./SidebarUserMenu.vue";
16
16
 
17
- const SETTINGS_PATH = "/settings";
18
17
  const MODULES_PATH = "/modules";
19
- // Registered ids of the footer entries, so "Preview as role" can lock them.
20
- const SETTINGS_FULL_ID = "settings";
18
+ // Registered id of the Modules entry, so "Preview as role" can lock it.
21
19
  const MODULES_FULL_ID = "modules";
22
20
  const MODULES_TREE_KEY = "modules";
23
- const SETTINGS_ICON = "i-ph-gear-six-light";
24
21
  const MODULES_ICON = "i-ph-squares-four-light";
25
22
  const BACK_ICON = "i-ph-arrow-left-light";
26
23
  const ESCAPE_KEY = "Escape";
@@ -254,21 +251,9 @@ const footerItems = computed((): NavigationMenuItem[] => {
254
251
  });
255
252
  }
256
253
 
257
- // Settings highlights its sub-pages; Back and Modules only highlight their
258
- // destinations, regardless of the router's default prefix matching.
259
- result.push(
260
- addActiveStateToMenuItem(
261
- {
262
- fullId: SETTINGS_FULL_ID,
263
- label: t("menu.section.settings"),
264
- to: SETTINGS_PATH,
265
- icon: SETTINGS_ICON,
266
- } as DmsMenuItem,
267
- currentFullId.value,
268
- currentRoute.value,
269
- ),
270
- );
271
-
254
+ // The settings are reached from the account menu, not from here. Back and
255
+ // Modules only highlight their destinations, regardless of the router's
256
+ // default prefix matching.
272
257
  if (isOwner.value) {
273
258
  result.push({
274
259
  fullId: MODULES_FULL_ID,
@@ -14,6 +14,8 @@ const GITHUB_STARS_CACHE_KEY = "dms-github-stars";
14
14
  const GITHUB_STARS_CACHE_TTL_MS = 60 * 60 * 1000;
15
15
 
16
16
  const { locale, setLocale, t } = useI18n();
17
+ const { loggedIn: isLoggedIn } = useDmsUserSession();
18
+ const { changeLanguage } = useUserLanguage();
17
19
  const { uniqueLocales } = useUniqueLocales();
18
20
  const { links } = useFooterLinks();
19
21
  const { processI18n } = useTranslation();
@@ -49,9 +51,7 @@ const githubStarsSettled = ref(false);
49
51
  const formattedGithubStars = computed(() =>
50
52
  githubStars.value === null
51
53
  ? ""
52
- : new Intl.NumberFormat(locale.value, { notation: "compact" }).format(
53
- githubStars.value,
54
- ),
54
+ : formatNumber(githubStars.value, locale.value, { notation: "compact" }),
55
55
  );
56
56
 
57
57
  function readCachedStars(): CachedStars | null {
@@ -104,7 +104,15 @@ onMounted(async () => {
104
104
  githubStarsSettled.value = true;
105
105
  });
106
106
 
107
+ // A signed-in user on a page of this layout (the account switcher) switches
108
+ // their profile's language too: the session applies that one on every load,
109
+ // and the server writes in it, so switching the screen alone would leave the
110
+ // two apart. A visitor's choice lives in the locale cookie.
107
111
  async function onLocaleChange(code: unknown) {
112
+ if (isLoggedIn.value) {
113
+ await changeLanguage(code as string);
114
+ return;
115
+ }
108
116
  await setLocale(code as typeof locale.value);
109
117
  }
110
118
  </script>
@@ -1,7 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from "vue";
3
+ import { useOpenDialog } from "#dms-ui/app/build/composables/containers/closingLayers";
3
4
  import { isFloatingSaveBarShown } from "#dms-ui/app/build/composables/form/floatingSaveBars";
4
5
  import { useGithubStarPrompt } from "../../composables/general/useGithubStarPrompt";
6
+ import { useActiveSidePanel } from "../../composables/layout/sidePanelState";
7
+ import { useSidebarState } from "./sidebarState";
5
8
 
6
9
  const TITLE_ID = "dms-github-star-prompt-title";
7
10
  const DESCRIPTION_ID = "dms-github-star-prompt-description";
@@ -9,6 +12,9 @@ const DESCRIPTION_ID = "dms-github-star-prompt-description";
9
12
  const { t } = useI18n();
10
13
  const { user } = useCurrentUser();
11
14
  const toast = useToast();
15
+ const { open: isSidebarOpen } = useSidebarState();
16
+ const { panel: sidePanel } = useActiveSidePanel();
17
+ const isDialogOpen = useOpenDialog();
12
18
 
13
19
  // Developers only: a production build, a client's dashboard included, never
14
20
  // counts, stores or shows anything. The same flag gates the dev reload.
@@ -16,15 +22,21 @@ const { isDue, snooze, star, dismiss } = useGithubStarPrompt(
16
22
  import.meta.env.DEV,
17
23
  );
18
24
 
19
- // The toaster stacks in the same corner, and a floating save bar sticks to
20
- // the bottom of the panel: the prompt steps aside while either is up rather
21
- // than cover it.
25
+ // The toaster stacks in the same corner, a floating save bar sticks to the
26
+ // bottom of the panel, and the phone sidebar keeps its account menu trigger
27
+ // at the bottom of the screen: the prompt steps aside while any is up rather
28
+ // than cover it. The sidebar only opens as a drawer, below `lg`. It waits
29
+ // as well while a drawer, a modal or a side panel is open, whose footer or
30
+ // content it would float over, and comes back once they close.
22
31
  const isShown = computed(
23
32
  () =>
24
33
  isDue.value &&
25
34
  !!user.value &&
26
35
  toast.toasts.value.length === 0 &&
27
- !isFloatingSaveBarShown.value,
36
+ !isFloatingSaveBarShown.value &&
37
+ !isSidebarOpen.value &&
38
+ !sidePanel.value &&
39
+ !isDialogOpen.value,
28
40
  );
29
41
  </script>
30
42
 
@@ -34,14 +46,13 @@ const isShown = computed(
34
46
  width, rising into place like a toast. The buttons take the card's
35
47
  full width, so the longer French label still fits beside "Later".
36
48
  On a phone it tightens and drops the icon, so the text runs the full
37
- width and covers less of the page. From `lg` it stays clear of an
38
- open side panel, whose width the dashboard frame sets. -->
49
+ width and covers less of the page. -->
39
50
  <div
40
51
  v-if="isShown"
41
52
  role="dialog"
42
53
  :aria-labelledby="TITLE_ID"
43
54
  :aria-describedby="DESCRIPTION_ID"
44
- class="bg-default ring-accented fixed inset-x-3 bottom-3 z-50 rounded-(--dms-radius-card) p-3 shadow-(--dms-shadow-pop) ring transition duration-300 ease-out motion-reduce:transition-none sm:inset-x-auto sm:right-5 sm:bottom-5 sm:w-[380px] sm:p-3.5 sm:ps-4 lg:right-[calc(min(var(--dms-side-panel-width,0px),60vw)+1.25rem)] starting:translate-y-4 starting:opacity-0"
55
+ class="bg-default ring-accented fixed inset-x-3 bottom-3 z-50 rounded-(--dms-radius-card) p-3 shadow-(--dms-shadow-pop) ring transition duration-300 ease-out motion-reduce:transition-none sm:inset-x-auto sm:right-5 sm:bottom-5 sm:w-[380px] sm:p-3.5 sm:ps-4 starting:translate-y-4 starting:opacity-0"
45
56
  >
46
57
  <div class="flex items-start gap-3 pe-6">
47
58
  <DmsIconWell icon="i-ph-star" size="sm" class="max-sm:hidden" />
@@ -24,6 +24,7 @@ import {
24
24
  findServedQuickAction,
25
25
  } from "#dms-ui/app/build/utils/dispatchQuickActionTarget";
26
26
  import { DEFAULT_PAGE_HEADER_ACTIONS_ORDER } from "../../../composables/layout/usePageHeaderActions";
27
+ import { type PageRecord, withPageRecord } from "../../../utils/pageRecord";
27
28
 
28
29
  /**
29
30
  * A button of the page header as the server serves it (`DefaultLayout({
@@ -43,6 +44,8 @@ export interface LayoutHeaderAction
43
44
  buttonId?: string;
44
45
  /** Place among the header's buttons, smallest first; `0` by default. */
45
46
  order?: number;
47
+ /** Acts on the page's record: disabled while it loads, gone without it. */
48
+ requiresRecord?: boolean;
46
49
  }
47
50
 
48
51
  interface PageHeaderActionBarProps {
@@ -52,6 +55,8 @@ interface PageHeaderActionBarProps {
52
55
  * among the buttons: after every button of an order up to this one.
53
56
  */
54
57
  slotOrder?: number;
58
+ /** The record the page shows, for the buttons acting on it. */
59
+ record?: PageRecord;
55
60
  }
56
61
 
57
62
  interface ResolvedHeaderAction {
@@ -66,6 +71,7 @@ interface ResolvedHeaderAction {
66
71
 
67
72
  const props = withDefaults(defineProps<PageHeaderActionBarProps>(), {
68
73
  slotOrder: DEFAULT_PAGE_HEADER_ACTIONS_ORDER,
74
+ record: undefined,
69
75
  });
70
76
 
71
77
  const { processI18n } = useTranslation();
@@ -273,6 +279,8 @@ function lockForPreview(action: ResolvedHeaderAction): ResolvedHeaderAction {
273
279
 
274
280
  const resolved = computed(() =>
275
281
  props.actions
282
+ .map((action) => withPageRecord(action, props.record))
283
+ .filter((action): action is LayoutHeaderAction => action !== null)
276
284
  .map(resolveAction)
277
285
  .filter((action): action is ResolvedHeaderAction => action !== null)
278
286
  .map(lockForPreview)
@@ -5,21 +5,34 @@ import PageHeaderActionBar, {
5
5
  } from "./PageHeaderActionBar.vue";
6
6
  import { injectPageHeaderActionsHost } from "../../../composables/layout/usePageHeaderActions";
7
7
  import { useServedHeaderActions } from "../../../composables/layout/useServedHeaderActions";
8
+ import {
9
+ type PageRecordSource,
10
+ usePageRecord,
11
+ } from "../../../composables/layout/usePageRecord";
8
12
 
9
13
  interface PageHeaderActionsOutletProps {
10
14
  /** Header buttons declared by the backend page (`headerActions`). */
11
15
  actions: LayoutHeaderAction[];
12
16
  /** The page query the server resolved their availability for. */
13
17
  servedQuery?: Record<string, string>;
18
+ /** The route parameters the server resolved their availability for. */
19
+ servedParams?: Record<string, string>;
20
+ /** The record the page shows (`record` of the layout). */
21
+ record?: PageRecordSource;
14
22
  }
15
23
 
16
24
  const props = withDefaults(defineProps<PageHeaderActionsOutletProps>(), {
17
25
  servedQuery: undefined,
26
+ servedParams: undefined,
27
+ record: undefined,
18
28
  });
19
29
 
30
+ const pageRecord = usePageRecord(() => props.record);
31
+
20
32
  const actions = useServedHeaderActions({
21
33
  actions: () => props.actions,
22
34
  servedQuery: () => props.servedQuery,
35
+ servedParams: () => props.servedParams,
23
36
  });
24
37
 
25
38
  const ROOT_SELECTOR = "[data-dms-page-header-actions]";
@@ -69,6 +82,7 @@ const showsServerMarkup = computed(
69
82
  <PageHeaderActionBar
70
83
  :actions="actions"
71
84
  :slot-order="host?.actionsOrder.value"
85
+ :record="pageRecord"
72
86
  >
73
87
  <component :is="pageActions" v-if="pageActions" />
74
88
  </PageHeaderActionBar>
@@ -1,47 +1,52 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
3
  * The body of the side panel a `sidePanel` action target opens on a row (see
4
- * `rowSidePanel.ts`). The DMS draws the header: the title, the row's position
5
- * among the rows the table shows, previous and next (J and K too) and close
6
- * (Escape too). Below it, the module's component receives the row as a
7
- * drawer's component does, mounted anew on each row. The position follows the
8
- * table: a refresh that moves or drops the row updates it. A row the link
9
- * names but its route answered 404 for shows the "not found" state instead,
10
- * with a close button.
4
+ * `rowSidePanel.ts`). The DMS draws the header (`RowHeader`, shared with a
5
+ * row drawer): the title, the row's position among the rows the table shows,
6
+ * previous and next (J and K too) and close (Escape too). Below it, the
7
+ * module's component receives the row as a drawer's component does, mounted
8
+ * anew on each row. The position follows the table: a refresh or live rows
9
+ * that move it update it, and a row past the rows loaded (a deep link) is
10
+ * located in the table's whole result. A row the link names but its route answered 404
11
+ * for shows the "not found" state instead, with a close button.
11
12
  */
12
- import { computed, onBeforeUnmount, onMounted } from "vue";
13
- import DmsEmptyState from "#dms-ui/app/components/empty-state/EmptyState.vue";
13
+ import { computed, onBeforeUnmount, onMounted, shallowRef, watch } from "vue";
14
+ import DmsRowHeader from "#dms-ui/app/build/components/containers/row/RowHeader.vue";
15
+ import DmsRowNotFound from "#dms-ui/app/build/components/containers/row/RowNotFound.vue";
14
16
  import {
15
17
  listenRowPanelClose,
16
18
  listenRowSteps,
17
- rowNavigation,
19
+ trackRowNavigation,
18
20
  } from "#dms-ui/app/build/composables/actions/rowNavigation";
19
21
  import {
20
22
  closeRowSidePanel,
21
23
  useRowSidePanelContent,
22
24
  } from "../../composables/layout/rowSidePanel";
23
25
 
24
- const DEFAULT_POSITION_KEY = "side_panel.position";
25
-
26
26
  const { t } = useI18n();
27
- const { processI18n } = useTranslation();
28
27
  const content = useRowSidePanelContent();
29
28
 
30
- const navigation = computed(() => {
31
- const current = content.value;
32
- if (!current?.row || !current.source) return undefined;
33
- return rowNavigation(current.source, current.row, current.showRow);
34
- });
35
-
36
- const position = computed(() => {
37
- if (!navigation.value) return undefined;
38
- const params = {
39
- index: navigation.value.index + 1,
40
- total: navigation.value.total,
41
- };
42
- const label = content.value?.positionLabel;
43
- return label ? processI18n(label, params) : t(DEFAULT_POSITION_KEY, params);
44
- });
29
+ const navigation = shallowRef<RowNavigation>();
30
+ let stopNavigation: (() => void) | undefined;
31
+ watch(
32
+ content,
33
+ (current) => {
34
+ stopNavigation?.();
35
+ stopNavigation = undefined;
36
+ navigation.value = undefined;
37
+ if (!current?.row || !current.source) return;
38
+ stopNavigation = trackRowNavigation(
39
+ current.source,
40
+ current.row,
41
+ current.showRow,
42
+ (next) => {
43
+ navigation.value = next;
44
+ },
45
+ );
46
+ },
47
+ // At once: a second J right after the first steps from the row it opened.
48
+ { immediate: true, flush: "sync" },
49
+ );
45
50
 
46
51
  const componentProps = computed(() => ({
47
52
  ...content.value?.componentOptions,
@@ -53,16 +58,6 @@ const heading = computed(() => content.value?.rowTitle || content.value?.title);
53
58
 
54
59
  const close = () => closeRowSidePanel();
55
60
 
56
- const notFoundActions = computed(() => [
57
- {
58
- label: t("side_panel.close"),
59
- icon: "i-ph-x",
60
- color: "neutral" as const,
61
- variant: "outline" as const,
62
- onClick: close,
63
- },
64
- ]);
65
-
66
61
  let stopKeys: Array<() => void> = [];
67
62
 
68
63
  onMounted(() => {
@@ -76,6 +71,7 @@ onMounted(() => {
76
71
  // longer open.
77
72
  onBeforeUnmount(() => {
78
73
  stopKeys.forEach((stop) => stop());
74
+ stopNavigation?.();
79
75
  close();
80
76
  });
81
77
  </script>
@@ -85,45 +81,12 @@ onBeforeUnmount(() => {
85
81
  <header
86
82
  class="border-default flex shrink-0 items-start gap-2 border-b py-3 ps-4 pe-3"
87
83
  >
88
- <div class="min-w-0 flex-1 self-center">
89
- <h2
90
- class="text-highlighted truncate text-[15px] leading-[1.3] font-[650] tracking-[-0.015em]"
91
- >
92
- {{ heading }}
93
- </h2>
94
- <p
95
- v-if="position"
96
- data-dms-row-side-panel-position
97
- class="text-muted mt-0.5 text-[12.5px] tabular-nums"
98
- >
99
- {{ position }}
100
- </p>
101
- </div>
102
- <div class="flex shrink-0 items-center gap-0.5">
103
- <template v-if="navigation">
104
- <UButton
105
- data-dms-row-side-panel-prev
106
- icon="i-ph-caret-up"
107
- color="neutral"
108
- variant="ghost"
109
- size="sm"
110
- square
111
- :aria-label="t('side_panel.previous')"
112
- :disabled="!navigation.hasPrev"
113
- @click="navigation.prev()"
114
- />
115
- <UButton
116
- data-dms-row-side-panel-next
117
- icon="i-ph-caret-down"
118
- color="neutral"
119
- variant="ghost"
120
- size="sm"
121
- square
122
- :aria-label="t('side_panel.next')"
123
- :disabled="!navigation.hasNext"
124
- @click="navigation.next()"
125
- />
126
- </template>
84
+ <DmsRowHeader
85
+ part="dms-row-side-panel"
86
+ :heading="heading"
87
+ :position-label="content.positionLabel"
88
+ :navigation="navigation"
89
+ >
127
90
  <UButton
128
91
  data-dms-row-side-panel-close
129
92
  icon="i-ph-x"
@@ -134,17 +97,15 @@ onBeforeUnmount(() => {
134
97
  :aria-label="t('side_panel.close')"
135
98
  @click="close"
136
99
  />
137
- </div>
100
+ </DmsRowHeader>
138
101
  </header>
139
102
  <div class="min-h-0 flex-1 overflow-y-auto p-4">
140
- <DmsEmptyState
103
+ <DmsRowNotFound
141
104
  v-if="content.notFound"
142
105
  data-dms-row-side-panel-not-found
143
- variant="no-result"
144
106
  :title="content.notFound.title"
145
107
  :description="content.notFound.description"
146
- :actions="notFoundActions"
147
- class="py-10"
108
+ @close="close"
148
109
  />
149
110
  <Suspense v-else>
150
111
  <component
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import UserIdentity from "./UserIdentity.vue";
3
+ import { useSidebarState } from "./sidebarState";
3
4
  import type { DropdownMenuItem } from "@nuxt/ui";
4
5
 
5
6
  interface Props {
@@ -27,11 +28,32 @@ const COLOR_MODE_ENTRIES: ColorModeEntry[] = [
27
28
  ];
28
29
 
29
30
  const { t, locale } = useI18n();
31
+ const route = useDmsRoute();
30
32
  const { user } = useCurrentUser();
31
33
  const { logout } = useLogout();
32
34
  const colorModePreference = useColorModePreference();
33
35
  const { uniqueLocales } = useUniqueLocales();
34
36
  const { changeLanguage } = useUserLanguage();
37
+ const { open: sidebarOpen } = useSidebarState();
38
+
39
+ // The picture the profile saved, read from the session user: a profile save
40
+ // refreshes that user, so a new picture shows here at once.
41
+ const avatarUrls = useFileReadUrls();
42
+ const avatarKey = computed(() => user.value?.avatar?.key);
43
+ const avatarSrc = computed(() =>
44
+ avatarKey.value ? avatarUrls.getUrl(avatarKey.value) : undefined,
45
+ );
46
+ watch(avatarKey, (key) => key && void avatarUrls.resolve(key), {
47
+ immediate: true,
48
+ });
49
+
50
+ // The settings have no sidebar entry: their only way in is this menu, so its
51
+ // trigger is drawn active while a settings page is open.
52
+ const isOnSettings = computed(
53
+ () =>
54
+ route.path === SETTINGS_ROUTE ||
55
+ route.path.startsWith(`${SETTINGS_ROUTE}/`),
56
+ );
35
57
 
36
58
  // v2 user menu: account links, then the per-user preferences as submenus,
37
59
  // then logging out.
@@ -67,6 +89,11 @@ const menuItems = computed<DropdownMenuItem[][]>(() => [
67
89
  label: t(entry.labelKey),
68
90
  icon: entry.icon,
69
91
  to: entry.to,
92
+ // The menu renders in a portal, outside the sidebar whose link clicks
93
+ // close the phone drawer: its own entries close it themselves.
94
+ onSelect: () => {
95
+ sidebarOpen.value = false;
96
+ },
70
97
  })),
71
98
  preferenceItems.value,
72
99
  [
@@ -91,12 +118,25 @@ const menuItems = computed<DropdownMenuItem[][]>(() => [
91
118
  <button
92
119
  type="button"
93
120
  class="hover:bg-elevated data-[state=open]:bg-elevated flex w-full items-center gap-2.5 rounded-md p-1.5 text-start transition-colors"
94
- :class="{ 'justify-center': props.collapsed }"
121
+ :class="{
122
+ 'justify-center': props.collapsed,
123
+ 'bg-elevated': isOnSettings,
124
+ }"
95
125
  :aria-label="user?.name"
126
+ :data-active="isOnSettings || undefined"
96
127
  >
97
- <UAvatar v-if="props.collapsed" :alt="user?.name" size="sm" />
128
+ <UAvatar
129
+ v-if="props.collapsed"
130
+ :src="avatarSrc"
131
+ :alt="user?.name"
132
+ size="sm"
133
+ />
98
134
  <template v-else>
99
- <UserIdentity :name="user?.name" :email="user?.email" />
135
+ <UserIdentity
136
+ :name="user?.name"
137
+ :email="user?.email"
138
+ :avatar-src="avatarSrc"
139
+ />
100
140
  <UIcon :name="TRIGGER_ICON" class="text-dimmed size-4 shrink-0" />
101
141
  </template>
102
142
  </button>
@@ -105,6 +145,7 @@ const menuItems = computed<DropdownMenuItem[][]>(() => [
105
145
  <UserIdentity
106
146
  :name="user?.name"
107
147
  :email="user?.email"
148
+ :avatar-src="avatarSrc"
108
149
  avatar-size="md"
109
150
  class="border-default border-b px-2.5 py-2.5"
110
151
  />
@@ -1,25 +1,33 @@
1
1
  <script setup lang="ts">
2
2
  import type { AvatarProps } from "@nuxt/ui";
3
3
 
4
- // The signed-in user as the shell shows them: initials avatar, name, then
5
- // the e-mail in mono (the sidebar menu's trigger and its header).
4
+ // The signed-in user as the shell shows them: their picture (initials
5
+ // without one), name, then the e-mail in mono (the sidebar menu's trigger and
6
+ // its header).
6
7
 
7
8
  interface UserIdentityProps {
8
9
  name?: string;
9
10
  email?: string;
11
+ avatarSrc?: string;
10
12
  avatarSize?: AvatarProps["size"];
11
13
  }
12
14
 
13
15
  const props = withDefaults(defineProps<UserIdentityProps>(), {
14
16
  name: undefined,
15
17
  email: undefined,
18
+ avatarSrc: undefined,
16
19
  avatarSize: "sm",
17
20
  });
18
21
  </script>
19
22
 
20
23
  <template>
21
24
  <span class="flex min-w-0 flex-1 items-center gap-2.5">
22
- <UAvatar :alt="props.name" :size="props.avatarSize" class="shrink-0" />
25
+ <UAvatar
26
+ :src="props.avatarSrc"
27
+ :alt="props.name"
28
+ :size="props.avatarSize"
29
+ class="shrink-0"
30
+ />
23
31
  <span class="flex min-w-0 flex-1 flex-col">
24
32
  <span
25
33
  class="text-highlighted truncate text-[13px] leading-tight font-semibold"