@antelopejs/dms 0.9.8 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/README.md +1 -1
  3. package/data/module-store/catalog.json +2 -2
  4. package/dist/attachments/table-view.js +2 -2
  5. package/dist/attachments/table-view.js.map +1 -1
  6. package/dist/automation/actions.d.ts +2 -0
  7. package/dist/automation/actions.js +116 -71
  8. package/dist/automation/actions.js.map +1 -1
  9. package/dist/automation/i18n.d.ts +4 -0
  10. package/dist/automation/i18n.js +19 -0
  11. package/dist/automation/i18n.js.map +1 -0
  12. package/dist/automation/send-notification.d.ts +21 -0
  13. package/dist/automation/send-notification.js +163 -0
  14. package/dist/automation/send-notification.js.map +1 -0
  15. package/dist/automation/triggers.js +19 -25
  16. package/dist/automation/triggers.js.map +1 -1
  17. package/dist/client-origin-cors.d.ts +1 -0
  18. package/dist/client-origin-cors.js +19 -0
  19. package/dist/client-origin-cors.js.map +1 -0
  20. package/dist/config.d.ts +3 -2
  21. package/dist/config.js +5 -7
  22. package/dist/config.js.map +1 -1
  23. package/dist/frontend-module.d.ts +4 -0
  24. package/dist/frontend-module.js +37 -0
  25. package/dist/frontend-module.js.map +1 -0
  26. package/dist/implementations/dms/client-ip.js +1 -1
  27. package/dist/implementations/dms/client-ip.js.map +1 -1
  28. package/dist/implementations/dms/dev-reload.d.ts +5 -0
  29. package/dist/implementations/dms/dev-reload.js +23 -3
  30. package/dist/implementations/dms/dev-reload.js.map +1 -1
  31. package/dist/implementations/dms/frontend-modules.d.ts +2 -4
  32. package/dist/implementations/dms/frontend-modules.js +38 -94
  33. package/dist/implementations/dms/frontend-modules.js.map +1 -1
  34. package/dist/implementations/dms/frontend-ready.d.ts +12 -2
  35. package/dist/implementations/dms/frontend-ready.js +34 -4
  36. package/dist/implementations/dms/frontend-ready.js.map +1 -1
  37. package/dist/implementations/dms/layout-banners.d.ts +2 -1
  38. package/dist/implementations/dms/layout-banners.js +9 -0
  39. package/dist/implementations/dms/layout-banners.js.map +1 -1
  40. package/dist/implementations/dms/manifest.d.ts +7 -2
  41. package/dist/implementations/dms/manifest.js +12 -7
  42. package/dist/implementations/dms/manifest.js.map +1 -1
  43. package/dist/implementations/dms/page.d.ts +4 -3
  44. package/dist/implementations/dms/page.js +49 -18
  45. package/dist/implementations/dms/page.js.map +1 -1
  46. package/dist/implementations/dms/permissions-resolver.d.ts +2 -2
  47. package/dist/implementations/dms/permissions-resolver.js +2 -2
  48. package/dist/implementations/dms/permissions-resolver.js.map +1 -1
  49. package/dist/implementations/dms/realtime.js +1 -0
  50. package/dist/implementations/dms/realtime.js.map +1 -1
  51. package/dist/implementations/dms-base/bulk-selection.d.ts +1 -1
  52. package/dist/implementations/dms-base/bulk-selection.js +23 -1
  53. package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
  54. package/dist/implementations/dms-base/filter-tokens.js +5 -3
  55. package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
  56. package/dist/implementations/dms-base/search-route.d.ts +1 -0
  57. package/dist/implementations/dms-base/search-route.js +35 -13
  58. package/dist/implementations/dms-base/search-route.js.map +1 -1
  59. package/dist/implementations/dms-base/table-view.js +11 -6
  60. package/dist/implementations/dms-base/table-view.js.map +1 -1
  61. package/dist/implementations/dms-notifications/index.d.ts +2 -2
  62. package/dist/implementations/dms-notifications/index.js +5 -5
  63. package/dist/implementations/dms-notifications/index.js.map +1 -1
  64. package/dist/index.js +32 -55
  65. package/dist/index.js.map +1 -1
  66. package/dist/pages/modules/index.d.ts +1 -0
  67. package/dist/pages/modules/index.js +1 -0
  68. package/dist/pages/modules/index.js.map +1 -1
  69. package/dist/pages/modules/modules-store.d.ts +9 -0
  70. package/dist/pages/modules/modules-store.js +30 -0
  71. package/dist/pages/modules/modules-store.js.map +1 -0
  72. package/dist/pages/settings/users/account-credentials.js +2 -2
  73. package/dist/pages/settings/users/account-credentials.js.map +1 -1
  74. package/dist/pages/settings/users/account-data-store.js +25 -8
  75. package/dist/pages/settings/users/account-data-store.js.map +1 -1
  76. package/dist/pages/settings/users/invite-extension-routes.js +9 -3
  77. package/dist/pages/settings/users/invite-extension-routes.js.map +1 -1
  78. package/dist/pages/settings/users/member-management.js +13 -14
  79. package/dist/pages/settings/users/member-management.js.map +1 -1
  80. package/dist/pages/settings/users/profile.d.ts +1 -4
  81. package/dist/pages/settings/users/profile.js +0 -35
  82. package/dist/pages/settings/users/profile.js.map +1 -1
  83. package/dist/pages/settings/users/role-editor-store.js +26 -18
  84. package/dist/pages/settings/users/role-editor-store.js.map +1 -1
  85. package/dist/pages/settings/users/role-editor.d.ts +0 -1
  86. package/dist/pages/settings/users/role-editor.js +1 -2
  87. package/dist/pages/settings/users/role-editor.js.map +1 -1
  88. package/dist/pages/settings/users/role-preview.js +1 -2
  89. package/dist/pages/settings/users/role-preview.js.map +1 -1
  90. package/dist/pages/settings/users/roles.js +2 -2
  91. package/dist/pages/settings/users/roles.js.map +1 -1
  92. package/dist/pages/settings/users/two-factor-operations.js.map +1 -1
  93. package/dist/realtime/broker.d.ts +6 -0
  94. package/dist/realtime/broker.js +8 -1
  95. package/dist/realtime/broker.js.map +1 -1
  96. package/dist/realtime/presence.d.ts +8 -3
  97. package/dist/realtime/presence.js +8 -4
  98. package/dist/realtime/presence.js.map +1 -1
  99. package/dist/realtime/sessions.d.ts +11 -1
  100. package/dist/realtime/sessions.js +21 -3
  101. package/dist/realtime/sessions.js.map +1 -1
  102. package/dist/realtime/table-view.d.ts +2 -2
  103. package/dist/realtime/table-view.js +4 -3
  104. package/dist/realtime/table-view.js.map +1 -1
  105. package/dist/routes/auth/client-ip.d.ts +1 -0
  106. package/dist/routes/auth/client-ip.js +18 -0
  107. package/dist/routes/auth/client-ip.js.map +1 -1
  108. package/dist/routes/auth/oauth/config.d.ts +2 -1
  109. package/dist/routes/auth/oauth/config.js +5 -1
  110. package/dist/routes/auth/oauth/config.js.map +1 -1
  111. package/dist/routes/auth/session-handoff.d.ts +0 -1
  112. package/dist/routes/auth/session-handoff.js +0 -2
  113. package/dist/routes/auth/session-handoff.js.map +1 -1
  114. package/dist/routes/auth.js +3 -2
  115. package/dist/routes/auth.js.map +1 -1
  116. package/dist/routes/index.d.ts +0 -1
  117. package/dist/routes/index.js +0 -1
  118. package/dist/routes/index.js.map +1 -1
  119. package/dist/routes/realtime.d.ts +13 -1
  120. package/dist/routes/realtime.js +114 -29
  121. package/dist/routes/realtime.js.map +1 -1
  122. package/dist/startup-config.d.ts +2 -0
  123. package/dist/startup-config.js +62 -0
  124. package/dist/startup-config.js.map +1 -0
  125. package/dist/store/interfaces.d.ts +3 -2
  126. package/dist/store/interfaces.js +16 -6
  127. package/dist/store/interfaces.js.map +1 -1
  128. package/dist/store/listing.js +25 -15
  129. package/dist/store/listing.js.map +1 -1
  130. package/dist/store/operations.js +5 -2
  131. package/dist/store/operations.js.map +1 -1
  132. package/dist/store/requirements.js +3 -2
  133. package/dist/store/requirements.js.map +1 -1
  134. package/dist/store/runtime.d.ts +10 -1
  135. package/dist/store/runtime.js +33 -6
  136. package/dist/store/runtime.js.map +1 -1
  137. package/dist/store/types.d.ts +10 -0
  138. package/dist/utils/ensure-default-tenant.d.ts +2 -1
  139. package/dist/utils/ensure-default-tenant.js +13 -1
  140. package/dist/utils/ensure-default-tenant.js.map +1 -1
  141. package/dist/utils/export-jobs.js +2 -1
  142. package/dist/utils/export-jobs.js.map +1 -1
  143. package/dist/validation/auth.schema.d.ts +2 -2
  144. package/dist/validation/member-invite.schema.d.ts +2 -10
  145. package/dist/validation/member-invite.schema.js +2 -15
  146. package/dist/validation/member-invite.schema.js.map +1 -1
  147. package/frontend-vue/dms.frontend.ts +24 -5
  148. package/frontend-vue/dms.server.ts +17 -0
  149. package/frontend-vue/layers/dms-auth/server/accounts.ts +3 -4
  150. package/frontend-vue/layers/dms-core/app/build/composables/renderNow.ts +33 -0
  151. package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +5 -1
  152. package/frontend-vue/layers/dms-core/app/build/utils/crossTab.ts +199 -0
  153. package/frontend-vue/layers/dms-core/app/build/utils/sharedRealtimeConnection.ts +410 -0
  154. package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +77 -12
  155. package/frontend-vue/layers/dms-core/app/composables/data-types/useDataType.ts +26 -0
  156. package/frontend-vue/layers/dms-core/app/composables/live/livePauseSource.ts +29 -0
  157. package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +150 -9
  158. package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +6 -4
  159. package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +27 -1
  160. package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +256 -119
  161. package/frontend-vue/layers/dms-core/app/composables/translation/useComposedText.ts +21 -8
  162. package/frontend-vue/layers/dms-core/app/composables/translation/useTranslation.ts +25 -5
  163. package/frontend-vue/layers/dms-core/app/composables/useApiError.ts +3 -5
  164. package/frontend-vue/layers/dms-core/app/composables/useFieldErrors.ts +61 -26
  165. package/frontend-vue/layers/dms-core/app/plugins/dev-error-log.ts +16 -0
  166. package/frontend-vue/layers/dms-core/app/types/user.ts +6 -0
  167. package/frontend-vue/layers/dms-core/app/utils/composedText.ts +7 -2
  168. package/frontend-vue/layers/dms-core/app/utils/formatter.ts +134 -35
  169. package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +18 -1
  170. package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +136 -2
  171. package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +136 -2
  172. package/frontend-vue/layers/dms-core/shared/types/app-config.ts +0 -2
  173. package/frontend-vue/layers/dms-layout/app/app.config.ts +0 -1
  174. package/frontend-vue/layers/dms-layout/app/assets/css/main.css +11 -1
  175. package/frontend-vue/layers/dms-layout/app/assets/fonts/Geist-Italic-Variable.woff2 +0 -0
  176. package/frontend-vue/layers/dms-layout/app/assets/fonts/Geist-Variable.woff2 +0 -0
  177. package/frontend-vue/layers/dms-layout/app/assets/fonts/GeistMono-Italic-Variable.woff2 +0 -0
  178. package/frontend-vue/layers/dms-layout/app/assets/fonts/GeistMono-Variable.woff2 +0 -0
  179. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +3 -2
  180. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +41 -21
  181. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +6 -2
  182. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +4 -19
  183. package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +11 -107
  184. package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
  185. package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +4 -0
  186. package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +43 -82
  187. package/frontend-vue/layers/dms-layout/app/build/components/layout/SidebarUserMenu.vue +44 -3
  188. package/frontend-vue/layers/dms-layout/app/build/components/layout/UserIdentity.vue +11 -3
  189. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleCatalogTile.vue +132 -35
  190. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleManageMenu.vue +59 -0
  191. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleSourceLabel.vue +25 -0
  192. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreBanners.vue +73 -0
  193. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreOperation.vue +116 -0
  194. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +8 -3
  195. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +85 -207
  196. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +10 -3
  197. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTileSkeleton.vue +68 -0
  198. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +24 -8
  199. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxItem.vue +8 -5
  200. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/region/RegionTimeZoneInput.vue +1 -0
  201. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-permissions.ts +19 -0
  202. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/useRoleEditor.ts +2 -1
  203. package/frontend-vue/layers/dms-layout/app/build/composables/layout/useSidePanelWidth.ts +31 -70
  204. package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationGroupsOpen.ts +11 -1
  205. package/frontend-vue/layers/dms-layout/app/build/utils/dev-reload-stream.ts +81 -0
  206. package/frontend-vue/layers/dms-layout/app/build/utils/github-repository.ts +2 -2
  207. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +232 -36
  208. package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +122 -14
  209. package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +32 -3
  210. package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +49 -11
  211. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
  212. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +59 -4
  213. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +194 -0
  214. package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +23 -3
  215. package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +4 -2
  216. package/frontend-vue/layers/dms-layout/app/composables/page/usePageRecordDeletion.ts +162 -0
  217. package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
  218. package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +6 -2
  219. package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +6 -2
  220. package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +291 -0
  221. package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +87 -474
  222. package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +3 -0
  223. package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +13 -22
  224. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +11 -10
  225. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +22 -52
  226. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-quick-actions.client.ts +7 -7
  227. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-session.client.ts +11 -10
  228. package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
  229. package/frontend-vue/layers/dms-layout/app/theme/actions.ts +5 -4
  230. package/frontend-vue/layers/dms-layout/app/theme/form-controls.ts +9 -4
  231. package/frontend-vue/layers/dms-layout/app/theme/surfaces.ts +22 -6
  232. package/frontend-vue/layers/dms-layout/app/types/module-store.ts +9 -0
  233. package/frontend-vue/layers/dms-layout/app/types/page.ts +15 -1
  234. package/frontend-vue/layers/dms-layout/app/utils/account-menu.ts +11 -0
  235. package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +57 -0
  236. package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +26 -0
  237. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +45 -18
  238. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +44 -17
  239. package/frontend-vue/layers/dms-ui/app/build/components/containers/TargetComponentTree.vue +31 -0
  240. package/frontend-vue/layers/dms-ui/app/build/components/containers/constants.ts +25 -0
  241. package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +244 -15
  242. package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +42 -25
  243. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowHeader.vue +153 -0
  244. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowNotFound.vue +44 -0
  245. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/drawerRowHeader.ts +35 -0
  246. package/frontend-vue/layers/dms-ui/app/build/components/form/FormEntries.vue +44 -9
  247. package/frontend-vue/layers/dms-ui/app/build/components/form/FormErrorSummary.vue +11 -4
  248. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +38 -5
  249. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldNotes.vue +29 -5
  250. package/frontend-vue/layers/dms-ui/app/build/components/form/FormLoadError.vue +5 -1
  251. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +3 -1
  252. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSections.vue +106 -27
  253. package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +12 -4
  254. package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +4 -2
  255. package/frontend-vue/layers/dms-ui/app/build/components/table/FiltersRow.vue +48 -1
  256. package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +55 -54
  257. package/frontend-vue/layers/dms-ui/app/build/components/table/RowSelection.vue +2 -1
  258. package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +396 -288
  259. package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +2 -1
  260. package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +2 -1
  261. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardAvatar.vue +59 -0
  262. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +19 -2
  263. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardRowActions.vue +38 -0
  264. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +148 -104
  265. package/frontend-vue/layers/dms-ui/app/build/components/table-view/LiveHoldBanner.vue +17 -2
  266. package/frontend-vue/layers/dms-ui/app/build/composables/actions/bulkSelection.ts +43 -9
  267. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowComponentOptions.ts +76 -0
  268. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +172 -27
  269. package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +270 -46
  270. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/actionError.ts +6 -1
  271. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +2 -0
  272. package/frontend-vue/layers/dms-ui/app/build/composables/containers/closingLayers.ts +186 -0
  273. package/frontend-vue/layers/dms-ui/app/build/composables/containers/context.ts +14 -0
  274. package/frontend-vue/layers/dms-ui/app/build/composables/containers/drawerSize.ts +229 -0
  275. package/frontend-vue/layers/dms-ui/app/build/composables/containers/useResizableSize.ts +175 -0
  276. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +18 -35
  277. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/numberSuffix.ts +44 -0
  278. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +123 -27
  279. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerFieldTypes.ts +2 -1
  280. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationFilter.ts +75 -0
  281. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationRows.ts +92 -0
  282. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationValue.ts +39 -0
  283. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/twoLineCell.ts +3 -3
  284. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/useColumnValueRenderer.ts +6 -2
  285. package/frontend-vue/layers/dms-ui/app/build/composables/form/createFormFields.ts +15 -0
  286. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldSaved.ts +40 -0
  287. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +62 -0
  288. package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryContext.ts +5 -0
  289. package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +42 -7
  290. package/frontend-vue/layers/dms-ui/app/build/composables/form/formLayout.ts +106 -4
  291. package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +11 -5
  292. package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
  293. package/frontend-vue/layers/dms-ui/app/build/composables/form/formValidation.ts +36 -4
  294. package/frontend-vue/layers/dms-ui/app/build/composables/form/readonlyValue.ts +19 -0
  295. package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +27 -0
  296. package/frontend-vue/layers/dms-ui/app/build/composables/form/repeaterTable.ts +78 -0
  297. package/frontend-vue/layers/dms-ui/app/build/composables/form/serverFieldErrors.ts +49 -0
  298. package/frontend-vue/layers/dms-ui/app/build/composables/form/toasterClearance.ts +118 -0
  299. package/frontend-vue/layers/dms-ui/app/build/composables/form/useInstantForm.ts +12 -1
  300. package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +41 -3
  301. package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +10 -2
  302. package/frontend-vue/layers/dms-ui/app/build/composables/live/pageReadClock.ts +28 -0
  303. package/frontend-vue/layers/dms-ui/app/build/composables/live/shortcuts.ts +1 -1
  304. package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +17 -3
  305. package/frontend-vue/layers/dms-ui/app/build/composables/live/useUpdatedLabel.ts +38 -0
  306. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/pageToolbarContext.ts +75 -0
  307. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/toolbarControlHeight.ts +29 -0
  308. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparison.ts +52 -0
  309. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +21 -0
  310. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodShortLabels.ts +28 -0
  311. package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +1 -0
  312. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRelationFilterLabels.ts +97 -0
  313. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRowScrollAnchor.ts +106 -0
  314. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +9 -0
  315. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +1 -2
  316. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/archivedCount.ts +31 -0
  317. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/filterEditorValue.ts +38 -0
  318. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/formatFilterValue.ts +33 -2
  319. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/scrollEdges.ts +31 -0
  320. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useCardFields.ts +33 -0
  321. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useMobileDisplay.ts +48 -0
  322. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +41 -18
  323. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +3 -2
  324. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLinkedVisit.ts +78 -0
  325. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +135 -22
  326. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +56 -25
  327. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +90 -35
  328. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +86 -10
  329. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/bulkActions.ts +14 -4
  330. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/card.ts +127 -2
  331. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/compareValues.ts +13 -0
  332. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/expandedRows.ts +28 -0
  333. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/groupedRows.ts +114 -1
  334. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/linkedArrival.ts +308 -0
  335. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/locateRow.ts +106 -0
  336. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pageArrival.ts +34 -0
  337. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +17 -0
  338. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/quickFilters.ts +41 -33
  339. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +24 -8
  340. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableCounts.ts +113 -0
  341. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +27 -4
  342. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +22 -6
  343. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/writeError.ts +21 -0
  344. package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
  345. package/frontend-vue/layers/dms-ui/app/build/utils/codeHighlight.ts +67 -29
  346. package/frontend-vue/layers/dms-ui/app/build/utils/eyebrow.ts +12 -0
  347. package/frontend-vue/layers/dms-ui/app/build/utils/fieldOptionsI18n.ts +51 -11
  348. package/frontend-vue/layers/dms-ui/app/build/utils/htmlGrammar.ts +79 -0
  349. package/frontend-vue/layers/dms-ui/app/build/utils/inputWidth.ts +53 -0
  350. package/frontend-vue/layers/dms-ui/app/build/utils/numberAdjustment.ts +6 -3
  351. package/frontend-vue/layers/dms-ui/app/build/utils/relativeDate.ts +19 -5
  352. package/frontend-vue/layers/dms-ui/app/build/utils/scrollWithin.ts +99 -0
  353. package/frontend-vue/layers/dms-ui/app/build/utils/shellParser.ts +1 -1
  354. package/frontend-vue/layers/dms-ui/app/build/utils/streamHighlight.ts +90 -0
  355. package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +36 -14
  356. package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
  357. package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +9 -1
  358. package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +80 -32
  359. package/frontend-vue/layers/dms-ui/app/components/blocks/PageToolbarBlock.vue +137 -0
  360. package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +5 -0
  361. package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +16 -2
  362. package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +24 -5
  363. package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +41 -23
  364. package/frontend-vue/layers/dms-ui/app/components/code-snippet/CodeSnippet.vue +23 -12
  365. package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +1 -1
  366. package/frontend-vue/layers/dms-ui/app/components/container-footer/ContainerFooter.vue +34 -0
  367. package/frontend-vue/layers/dms-ui/app/components/field-row/FieldRow.vue +25 -2
  368. package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +2 -2
  369. package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +119 -36
  370. package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +53 -22
  371. package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +20 -1
  372. package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +13 -1
  373. package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +8 -3
  374. package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +53 -5
  375. package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +29 -5
  376. package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +45 -8
  377. package/frontend-vue/layers/dms-ui/app/components/form/components/InputPercentage.vue +28 -5
  378. package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +5 -0
  379. package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +19 -2
  380. package/frontend-vue/layers/dms-ui/app/components/form/components/InputTime.vue +2 -1
  381. package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +226 -65
  382. package/frontend-vue/layers/dms-ui/app/components/form/components/Repeater.vue +80 -30
  383. package/frontend-vue/layers/dms-ui/app/components/form/components/RepeaterTable.vue +210 -0
  384. package/frontend-vue/layers/dms-ui/app/components/form/components/Select.vue +4 -2
  385. package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +11 -2
  386. package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +79 -70
  387. package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +117 -21
  388. package/frontend-vue/layers/dms-ui/app/components/meter/Meter.vue +2 -1
  389. package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +113 -34
  390. package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +14 -2
  391. package/frontend-vue/layers/dms-ui/app/components/section-header/Eyebrow.vue +6 -1
  392. package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +13 -0
  393. package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +52 -3
  394. package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +2 -1
  395. package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +74 -25
  396. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +48 -44
  397. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanDisplay.vue +4 -1
  398. package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +426 -97
  399. package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +33 -5
  400. package/frontend-vue/layers/dms-ui/app/composables/chart/apexCategoryLabels.ts +68 -0
  401. package/frontend-vue/layers/dms-ui/app/composables/chart/apexDatetimeBuckets.ts +306 -0
  402. package/frontend-vue/layers/dms-ui/app/composables/chart/formatValue.ts +4 -3
  403. package/frontend-vue/layers/dms-ui/app/composables/chart/heatmapColors.ts +93 -0
  404. package/frontend-vue/layers/dms-ui/app/composables/chart/localizeSeries.ts +62 -0
  405. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +116 -8
  406. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.types.ts +7 -0
  407. package/frontend-vue/layers/dms-ui/app/composables/containers/types.ts +84 -1
  408. package/frontend-vue/layers/dms-ui/app/composables/containers/useDrawer.ts +69 -8
  409. package/frontend-vue/layers/dms-ui/app/composables/form/types/field-saved.ts +11 -0
  410. package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +16 -0
  411. package/frontend-vue/layers/dms-ui/app/composables/form/types/functions.ts +1 -0
  412. package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +189 -28
  413. package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +29 -12
  414. package/frontend-vue/layers/dms-ui/app/composables/page-toolbar/usePageToolbarSize.ts +30 -0
  415. package/frontend-vue/layers/dms-ui/app/composables/table-view/kanban.ts +4 -2
  416. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +22 -3
  417. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +4 -0
  418. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +24 -1
  419. package/frontend-vue/layers/dms-ui/app/plugins/closing-layers.client.ts +10 -0
  420. package/frontend-vue/layers/dms-ui/app/plugins/live-pause-source.ts +15 -0
  421. package/frontend-vue/layers/dms-ui/app/plugins/register.ts +2 -0
  422. package/frontend-vue/layers/dms-ui/app/types/input-number.ts +3 -2
  423. package/frontend-vue/layers/dms-ui/app/types/page-toolbar.ts +7 -0
  424. package/frontend-vue/layers/dms-ui/app/types/row-action.ts +0 -2
  425. package/frontend-vue/layers/dms-ui/app/types/row-navigation.ts +17 -0
  426. package/frontend-vue/layers/dms-ui/app/types/table/query.ts +2 -1
  427. package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +3 -1
  428. package/frontend-vue/layers/dms-ui/app/utils/cascader.ts +94 -3
  429. package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
  430. package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +20 -5
  431. package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +20 -5
  432. package/frontend-vue/package.json +6 -2
  433. package/package.json +11 -10
  434. package/skills/dms/SKILL.md +1 -1
  435. package/skills/dms-auth/SKILL.md +5 -3
  436. package/skills/dms-best-practices/REFERENCE.md +2 -0
  437. package/skills/dms-best-practices/SKILL.md +4 -0
  438. package/skills/dms-module/SKILL.md +3 -2
  439. package/skills/dms-pages/REFERENCE.md +129 -16
  440. package/skills/dms-pages/SKILL.md +1 -1
  441. package/skills/dms-project/DEV-SERVERS.md +23 -1
  442. package/skills/dms-project/REFERENCE.md +1 -1
  443. package/skills/dms-project/SKILL.md +4 -1
  444. package/skills/dms-project/scripts/dms-dev.sh +205 -17
  445. package/dist/hooks/tenant-instances.d.ts +0 -1
  446. package/dist/hooks/tenant-instances.js +0 -13
  447. package/dist/hooks/tenant-instances.js.map +0 -1
  448. package/dist/routes/startup-gate.d.ts +0 -7
  449. package/dist/routes/startup-gate.js +0 -39
  450. package/dist/routes/startup-gate.js.map +0 -1
  451. package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
@@ -1,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"
@@ -1,6 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import ModuleTile from "./ModuleTile.vue";
3
- import type { Tone } from "#dms-ui/app/types/tone";
3
+ import ModuleManageMenu from "./ModuleManageMenu.vue";
4
+ import ModuleSourceLabel from "./ModuleSourceLabel.vue";
5
+ import ModuleStoreOperation from "./ModuleStoreOperation.vue";
6
+ import ModuleStoreRequirements from "./ModuleStoreRequirements.vue";
4
7
  import type {
5
8
  CheckListItem,
6
9
  CheckListState,
@@ -8,27 +11,52 @@ import type {
8
11
  import type {
9
12
  ModuleCatalogEntry,
10
13
  ModuleReadoutTone,
11
- ModuleStatus,
12
14
  } from "../../../../types/page";
13
- import { moduleCategory, moduleStatus } from "../../../utils/modules-catalog";
15
+ import type { StoreOperationKind } from "../../../../types/module-store";
16
+ import { moduleCategory } from "../../../utils/modules-catalog";
17
+ import {
18
+ type ModuleStoreEntry,
19
+ type StoreAction,
20
+ installedTileStatus,
21
+ storeActions,
22
+ storeBlockMessage,
23
+ } from "../../../utils/module-store-catalog";
24
+
25
+ // An installed module: its state, a live readout, and the one place it is
26
+ // managed. Its button opens it on its landing page, or resumes it while it
27
+ // is paused; the store actions it offers are in its Manage menu.
14
28
 
15
29
  interface ModuleCatalogTileProps {
16
30
  entry: ModuleCatalogEntry;
17
- /** Where the tile leads: the last page visited in the module, or its landing page. */
18
- to: string;
31
+ /** Where Open leads: the module's landing page, when it has pages. */
32
+ to?: string;
33
+ /** What the module store says of it, when the store lists it. */
34
+ storeEntry?: ModuleStoreEntry;
35
+ /** Another module's operation runs, or the store is read-only. */
36
+ isLocked?: boolean;
37
+ }
38
+
39
+ interface ModuleCatalogTileEmits {
40
+ action: [kind: StoreOperationKind];
19
41
  }
20
42
 
21
- const props = defineProps<ModuleCatalogTileProps>();
43
+ const props = withDefaults(defineProps<ModuleCatalogTileProps>(), {
44
+ to: undefined,
45
+ storeEntry: undefined,
46
+ isLocked: false,
47
+ });
48
+ const emit = defineEmits<ModuleCatalogTileEmits>();
22
49
  const { t } = useI18n();
23
50
  const { processI18n } = useTranslation();
24
51
 
25
- // v2 .module-tile__state: a mono uppercase pill with a leading dot.
26
- const STATUS_TONES: Record<ModuleStatus, Tone> = {
27
- live: "success",
28
- beta: "secondary",
29
- update: "warning",
30
- attention: "error",
31
- };
52
+ const TOOLTIP_CONTENT = { side: "top", sideOffset: 6 } as const;
53
+ // Takes what the actions leave rather than wrapping them under it: the band
54
+ // stays one line, and only the category is cut when even that is too short.
55
+ // The facts never shrink below the version and the source (the category's
56
+ // zero width keeps its text out of that floor): a tile too narrow for those
57
+ // and the actions drops the actions to a second line rather than run the
58
+ // source under them.
59
+ const FOOTER_FACTS_CLASS = "flex grow basis-0 items-center gap-2";
32
60
 
33
61
  // v2 .module-tile__viz: each readout line opens with a tone glyph.
34
62
  const READOUT_STATES: Record<ModuleReadoutTone, CheckListState> = {
@@ -38,7 +66,9 @@ const READOUT_STATES: Record<ModuleReadoutTone, CheckListState> = {
38
66
  error: "error",
39
67
  };
40
68
 
41
- const status = computed(() => moduleStatus(props.entry));
69
+ const status = computed(() =>
70
+ installedTileStatus(props.entry, props.storeEntry),
71
+ );
42
72
  const readout = computed<CheckListItem[]>(() =>
43
73
  (props.entry.readout ?? []).map((line, index) => ({
44
74
  id: String(index),
@@ -47,18 +77,41 @@ const readout = computed<CheckListItem[]>(() =>
47
77
  })),
48
78
  );
49
79
  const title = computed(() => processI18n(props.entry.title));
80
+ const category = computed(() => processI18n(moduleCategory(props.entry)));
81
+
82
+ const actions = computed<StoreAction[]>(() =>
83
+ props.storeEntry ? storeActions(props.storeEntry) : [],
84
+ );
85
+ /** The store action the button runs when there is nothing to open. */
86
+ const primary = computed<StoreAction | undefined>(() =>
87
+ props.to ? undefined : actions.value.find((action) => !action.isMenuOnly),
88
+ );
89
+ const menuActions = computed(() =>
90
+ actions.value.filter((action) => action !== primary.value),
91
+ );
92
+ const isOperating = computed(() => props.storeEntry?.operation !== undefined);
93
+
94
+ /** Why the button waits, when it does. */
95
+ const blockedReason = computed(() => {
96
+ if (!primary.value || !props.storeEntry) return undefined;
97
+ if (props.isLocked) return t("modules.store.locked");
98
+ return storeBlockMessage(props.storeEntry, primary.value.kind, t);
99
+ });
100
+ const isBlocked = computed(() => blockedReason.value !== undefined);
50
101
  </script>
51
102
 
52
103
  <template>
53
104
  <ModuleTile
54
- :to="props.to"
105
+ :id="`module-${props.entry.id}`"
55
106
  :icon="props.entry.icon"
56
107
  :title="title"
57
108
  :description="processI18n(props.entry.description)"
58
109
  :status="{
59
- tone: STATUS_TONES[status],
60
- label: t(`modules.status.${status}`),
110
+ tone: status.tone,
111
+ dot: status.dot,
112
+ label: t(status.labelKey),
61
113
  }"
114
+ is-solid
62
115
  >
63
116
  <template #readout>
64
117
  <DmsCheckList
@@ -72,26 +125,70 @@ const title = computed(() => processI18n(props.entry.title));
72
125
  />
73
126
  </template>
74
127
 
128
+ <template v-if="props.storeEntry">
129
+ <ModuleStoreOperation :entry="props.storeEntry" />
130
+ <p
131
+ v-if="!isOperating && props.storeEntry.state === 'pending'"
132
+ class="text-xs text-(--ui-warning)"
133
+ >
134
+ {{ t("modules.store.pending_hint") }}
135
+ </p>
136
+ <ModuleStoreRequirements v-if="!isOperating" :entry="props.storeEntry" />
137
+ </template>
138
+
75
139
  <template #footer>
76
- <span v-if="props.entry.version" class="whitespace-nowrap">
77
- v{{ props.entry.version }}
140
+ <!-- v0.5.0 · [icon] npm · Content, as the store tile says it: the
141
+ state is the pill's to say. Only the category gives way to Open
142
+ and Manage; the version and the source stay whole. -->
143
+ <span :class="FOOTER_FACTS_CLASS">
144
+ <span v-if="props.entry.version" class="shrink-0 whitespace-nowrap">
145
+ v{{ props.entry.version }}
146
+ </span>
147
+ <template v-if="props.entry.source">
148
+ <span v-if="props.entry.version" aria-hidden="true">·</span>
149
+ <ModuleSourceLabel :source="props.entry.source" />
150
+ </template>
151
+ <span class="w-0 min-w-0 grow truncate" :title="category">
152
+ <template v-if="props.entry.version || props.entry.source">
153
+ ·
154
+ </template>
155
+ {{ category }}
156
+ </span>
78
157
  </span>
79
- <span v-if="status === 'update'" class="text-warning truncate">
80
- <template v-if="props.entry.version">·</template>
81
- {{ t("modules.update_available") }}
82
- </span>
83
- <span v-else class="truncate">
84
- <template v-if="props.entry.version">·</template>
85
- {{ processI18n(moduleCategory(props.entry)) }}
86
- </span>
87
- <span
88
- class="text-primary ms-auto inline-flex shrink-0 items-center gap-1 font-sans text-xs font-semibold"
89
- >
90
- {{ t("modules.open") }}
91
- <UIcon
92
- name="i-ph-arrow-right"
93
- class="size-3.5 transition-transform group-hover:translate-x-0.5"
94
- :aria-hidden="true"
158
+ <span class="ms-auto flex shrink-0 items-center gap-1 font-sans">
159
+ <UButton
160
+ v-if="props.to"
161
+ :to="props.to"
162
+ size="xs"
163
+ color="primary"
164
+ variant="soft"
165
+ trailing-icon="i-ph-arrow-right"
166
+ :label="t('modules.open')"
167
+ />
168
+ <UTooltip
169
+ v-else-if="primary"
170
+ :text="blockedReason"
171
+ :disabled="!blockedReason"
172
+ :content="TOOLTIP_CONTENT"
173
+ >
174
+ <!-- aria-disabled rather than disabled: the button keeps its
175
+ tooltip, which says why it waits. -->
176
+ <UButton
177
+ size="xs"
178
+ :color="primary.kind === 'update' ? 'warning' : 'neutral'"
179
+ variant="outline"
180
+ :icon="primary.icon"
181
+ :label="t(`modules.store.actions.${primary.labelKey}`)"
182
+ :aria-disabled="isBlocked ? 'true' : undefined"
183
+ :class="{ 'cursor-not-allowed opacity-50': isBlocked }"
184
+ @click="!isBlocked && emit('action', primary.kind)"
185
+ />
186
+ </UTooltip>
187
+ <ModuleManageMenu
188
+ v-if="props.storeEntry && !props.isLocked"
189
+ :entry="props.storeEntry"
190
+ :actions="menuActions"
191
+ @action="emit('action', $event)"
95
192
  />
96
193
  </span>
97
194
  </template>
@@ -0,0 +1,59 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ type ModuleStoreEntry,
4
+ type StoreAction,
5
+ storeBlockMessage,
6
+ } from "../../../utils/module-store-catalog";
7
+ import type { StoreOperationKind } from "../../../../types/module-store";
8
+
9
+ // The Manage menu of an installed module: the store actions its tile does
10
+ // not show as its button. One that cannot run stays listed, disabled, with
11
+ // why under it.
12
+
13
+ interface ModuleManageMenuProps {
14
+ entry: ModuleStoreEntry;
15
+ actions: StoreAction[];
16
+ }
17
+
18
+ interface ModuleManageMenuEmits {
19
+ action: [kind: StoreOperationKind];
20
+ }
21
+
22
+ const props = defineProps<ModuleManageMenuProps>();
23
+ const emit = defineEmits<ModuleManageMenuEmits>();
24
+ const { t } = useI18n();
25
+
26
+ const items = computed(() =>
27
+ props.actions.map((action) => {
28
+ const blocked = storeBlockMessage(props.entry, action.kind, t);
29
+ return {
30
+ label: t(`modules.store.actions.${action.labelKey}`),
31
+ description: blocked,
32
+ icon: action.icon,
33
+ color: action.kind === "uninstall" ? ("error" as const) : undefined,
34
+ disabled: blocked !== undefined,
35
+ onSelect: () => emit("action", action.kind),
36
+ };
37
+ }),
38
+ );
39
+ </script>
40
+
41
+ <template>
42
+ <UDropdownMenu
43
+ v-if="items.length > 0"
44
+ :items="items"
45
+ :content="{ align: 'end' }"
46
+ :ui="{
47
+ content: 'max-w-72',
48
+ itemDescription: 'whitespace-normal text-muted text-xs',
49
+ }"
50
+ >
51
+ <UButton
52
+ size="xs"
53
+ color="neutral"
54
+ variant="outline"
55
+ trailing-icon="i-ph-caret-down"
56
+ :label="t('modules.store.actions.manage')"
57
+ />
58
+ </UDropdownMenu>
59
+ </template>
@@ -0,0 +1,25 @@
1
+ <script setup lang="ts">
2
+ import type { ModuleSourceKind } from "../../../../types/module-store";
3
+ import { MODULE_SOURCE_ICONS } from "../../../utils/module-store-catalog";
4
+
5
+ // Where a module's code comes from, as a tile's footer names it: an icon
6
+ // and a short name (npm, GitHub, GitLab, Git, Local).
7
+
8
+ interface ModuleSourceLabelProps {
9
+ source: ModuleSourceKind;
10
+ }
11
+
12
+ const props = defineProps<ModuleSourceLabelProps>();
13
+ const { t } = useI18n();
14
+ </script>
15
+
16
+ <template>
17
+ <span class="inline-flex shrink-0 items-center gap-1 whitespace-nowrap">
18
+ <UIcon
19
+ :name="MODULE_SOURCE_ICONS[props.source]"
20
+ class="size-3.5"
21
+ :aria-hidden="true"
22
+ />
23
+ {{ t(`modules.source.${props.source}`) }}
24
+ </span>
25
+ </template>