@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,5 +1,4 @@
1
1
  <script setup lang="ts">
2
- import type { Ref, VNode } from "vue";
3
2
  import { MONO_CHIP_CLASS } from "#dms-ui/app/build/utils/monoChip";
4
3
  import UButton from "@nuxt/ui/components/Button.vue";
5
4
  import UIcon from "@nuxt/ui/runtime/vue/components/Icon.vue";
@@ -13,8 +12,8 @@ import type { Tone } from "#dms-ui/app/types/tone";
13
12
  import { useKeyboardPlatform } from "#dms-ui/app/composables/global/keyboardPlatform";
14
13
  import DmsChipGroup from "#dms-ui/app/build/components/form/ChipGroup.vue";
15
14
  import ModuleCatalogTile from "../build/components/pages/modules/ModuleCatalogTile.vue";
16
- import ModuleStoreTile from "../build/components/pages/modules/ModuleStoreTile.vue";
17
- import ModuleStoreDependencyPicker from "../build/components/pages/modules/ModuleStoreDependencyPicker.vue";
15
+ import ModuleStoreBanners from "../build/components/pages/modules/ModuleStoreBanners.vue";
16
+ import ModuleTileSkeleton from "../build/components/pages/modules/ModuleTileSkeleton.vue";
18
17
  import { usePageHeaderActions } from "../composables/layout/usePageHeaderActions";
19
18
  import {
20
19
  ENDPOINT_MODULES_LISTING,
@@ -24,7 +23,10 @@ import { useModuleHistory } from "../composables/page/useModuleHistory";
24
23
  import type { ModuleCatalogEntry } from "../types/page";
25
24
  import {
26
25
  MODULE_CATEGORY_ALL,
26
+ MODULE_STORE_PATH,
27
+ MODULE_TILE_GRID_CLASS,
27
28
  filterModules,
29
+ parseModuleSortMode,
28
30
  findModulePageMatch,
29
31
  moduleCategoryOptions,
30
32
  sortModules,
@@ -32,35 +34,19 @@ import {
32
34
  type ModuleSortMode,
33
35
  } from "../build/utils/modules-catalog";
34
36
  import {
35
- type ModuleStoreEntry,
36
- type StoreScope,
37
- FRONTEND_BUILD_COMMAND,
38
- filterByScope,
39
- isAwaitingFrontend,
40
- needsDependencyChoice,
41
37
  sortStoreModules,
38
+ storeOnlyModules,
39
+ storeOpenTarget,
42
40
  toModuleStoreEntry,
41
+ withStoreDetails,
43
42
  } from "../build/utils/module-store-catalog";
44
- import {
45
- type DependencyDraft,
46
- areDependencyDraftsValid,
47
- buildInstallBody,
48
- createDependencyDrafts,
49
- } from "../build/utils/module-store-install";
50
43
  import { useModuleStore } from "../composables/page/useModuleStore";
51
- import {
52
- ConfirmActionError,
53
- useConfirm,
54
- } from "#dms-ui/app/composables/confirm";
55
- import type {
56
- StoreActionBody,
57
- StoreOperationKind,
58
- } from "../types/module-store";
44
+ import { useModuleStoreActions } from "../composables/page/useModuleStoreActions";
59
45
  import DmsSearchInput from "#dms-ui/app/build/components/form/SearchInput.vue";
60
46
 
61
47
  const SKELETON_PLACEHOLDER_COUNT = 6;
62
48
 
63
- const { t } = useI18n();
49
+ const { t, locale } = useI18n();
64
50
  const { processI18n } = useTranslation();
65
51
  const { keyLabel } = useKeyboardPlatform();
66
52
  const isOwner = useIsOwner();
@@ -84,6 +70,15 @@ usePageHeaderActions(() => {
84
70
  t("modules.owner_badge"),
85
71
  ],
86
72
  ),
73
+ h(UButton, {
74
+ label: t("modules.browse_store"),
75
+ icon: "i-ph-storefront",
76
+ to: MODULE_STORE_PATH,
77
+ color: "primary",
78
+ variant: "soft",
79
+ ui: { label: "max-sm:sr-only" },
80
+ "aria-label": t("modules.browse_store"),
81
+ }),
87
82
  h(UButton, {
88
83
  label: t("modules.refresh"),
89
84
  icon: "i-ph-arrows-clockwise",
@@ -113,10 +108,14 @@ if (!isOwner.value) {
113
108
  });
114
109
  }
115
110
 
116
- // Read with the page, which the server renders and hands over: the
117
- // hydration reuses the listing the server rendered (read again, its live
118
- // readouts, an uptime a minute on, would no longer match the markup).
119
- await modulesListing.loadModulesListing();
111
+ // Declared before the installed modules, which take the version, category and
112
+ // source a registration leaves out from it, and the store actions.
113
+ const moduleStore = useModuleStore();
114
+
115
+ // Both listings are read with the page (`useApiData`): a server render waits
116
+ // for them and hands them to the browser, which hydrates on them without
117
+ // reading again. Nothing awaits them here: a client navigation shows the
118
+ // tile skeletons, sized like the tiles, until they answer.
120
119
 
121
120
  const isForbidden = computed(
122
121
  () =>
@@ -135,16 +134,32 @@ const isInitiallyLoading = computed(
135
134
  );
136
135
  const isRetrying = ref(false);
137
136
 
138
- const modules = computed<ModuleCatalogEntry[]>(() =>
139
- (modulesListing.modules.value ?? []).filter((entry) => entry.hasAccess),
137
+ const storeModules = computed(() =>
138
+ sortStoreModules(
139
+ (moduleStore.listing.value?.modules ?? []).map((entry) =>
140
+ toModuleStoreEntry(entry, modulesListing.modules.value ?? []),
141
+ ),
142
+ processI18n,
143
+ locale.value,
144
+ ),
145
+ );
146
+ const storeEntries = computed(
147
+ () => new Map(storeModules.value.map((entry) => [entry.id, entry])),
140
148
  );
141
- const summary = computed(() => summarizeModules(modules.value));
142
149
 
143
- // ── Navigation targets ─────────────────────────────────────────────
144
- function moduleTarget(entry: ModuleCatalogEntry): string {
145
- return moduleHistory.getLast(entry.id) ?? entry.landingSlug;
146
- }
150
+ // The modules that run, and the installed ones that register nothing (paused,
151
+ // not started, or without pages): antelope.config.ts declares them all.
152
+ const modules = computed<ModuleCatalogEntry[]>(() => {
153
+ const registered = withStoreDetails(
154
+ (modulesListing.modules.value ?? []).filter((entry) => entry.hasAccess),
155
+ storeModules.value,
156
+ );
157
+ return [...registered, ...storeOnlyModules(storeModules.value, registered)];
158
+ });
159
+ const summary = computed(() => summarizeModules(modules.value));
147
160
 
161
+ // The history only orders the grid by last use: a tile always opens the
162
+ // module on its landing page, never where it was left.
148
163
  const visitedAt = computed(
149
164
  () =>
150
165
  new Map(
@@ -155,16 +170,19 @@ const visitedAt = computed(
155
170
  ),
156
171
  );
157
172
 
158
- // The installed modules, their skeleton and the store share one grid and
159
- // one search toolbar.
160
- const TILE_GRID_CLASS =
161
- "grid grid-cols-[repeat(auto-fill,minmax(268px,1fr))] gap-4";
162
173
  const TOOLBAR_CLASS = "mb-[18px] flex flex-wrap items-center gap-2.5";
163
174
 
164
175
  // ── Search, category, sort ─────────────────────────────────────────
165
176
  const query = ref("");
166
177
  const category = ref<string>(MODULE_CATEGORY_ALL);
167
- const sortMode = ref<ModuleSortMode>("recent");
178
+ // Remembered per browser in the preferences cookie, so the server renders the
179
+ // grid in the order the browser hydrates it with.
180
+ const SORT_MODE_PREFERENCE_KEY = "modulesPage.sort";
181
+ const { getPreference, setPreference } = usePreferences();
182
+ const sortMode = computed<ModuleSortMode>({
183
+ get: () => parseModuleSortMode(getPreference(SORT_MODE_PREFERENCE_KEY)),
184
+ set: (mode) => setPreference(SORT_MODE_PREFERENCE_KEY, mode),
185
+ });
168
186
 
169
187
  const categoryOptions = computed(() => [
170
188
  {
@@ -183,8 +201,8 @@ watch(categoryOptions, (options) => {
183
201
  });
184
202
 
185
203
  const sortItems = computed(() => [
186
- { value: "recent", label: t("modules.sort.recent") },
187
204
  { value: "name", label: t("modules.sort.name") },
205
+ { value: "recent", label: t("modules.sort.recent") },
188
206
  ]);
189
207
 
190
208
  const visibleModules = computed(() =>
@@ -197,6 +215,7 @@ const visibleModules = computed(() =>
197
215
  sortMode.value,
198
216
  visitedAt.value,
199
217
  processI18n,
218
+ locale.value,
200
219
  ),
201
220
  );
202
221
 
@@ -221,217 +240,22 @@ function clearFilters(): void {
221
240
  }
222
241
 
223
242
  // ── Module store ───────────────────────────────────────────────────
224
- const moduleStore = useModuleStore();
225
- const toast = useToast();
226
- const { confirm } = useConfirm();
227
-
228
243
  onMounted(() => {
229
- if (isOwner.value) void moduleStore.load();
244
+ if (isOwner.value) moduleStore.start();
230
245
  });
231
246
  onBeforeUnmount(() => moduleStore.stop());
232
247
 
233
248
  const storeListing = computed(() => moduleStore.listing.value);
234
- const isStoreLoading = computed(
235
- () => storeListing.value === undefined && !moduleStore.loadingError.value,
236
- );
237
- const storeModules = computed(() =>
238
- sortStoreModules(
239
- (storeListing.value?.modules ?? []).map((entry) =>
240
- toModuleStoreEntry(entry, modulesListing.modules.value ?? []),
241
- ),
242
- processI18n,
243
- ),
244
- );
245
- const storeScope = ref<StoreScope>("all");
246
- const storeQuery = ref("");
247
- const storeCategory = ref<string>(MODULE_CATEGORY_ALL);
248
-
249
- const storeScopeItems = computed(() =>
250
- (["all", "available", "installed"] as const).map((scope) => ({
251
- value: scope,
252
- label: t(`modules.store.scope.${scope}`, {
253
- count: filterByScope(storeModules.value, scope).length,
254
- }),
255
- })),
256
- );
257
- const scopedStoreModules = computed(() =>
258
- filterByScope(storeModules.value, storeScope.value),
259
- );
260
-
261
- const storeCategoryOptions = computed(() => [
262
- {
263
- value: MODULE_CATEGORY_ALL,
264
- label: t("modules.category.all"),
265
- count: scopedStoreModules.value.length,
266
- },
267
- ...moduleCategoryOptions(scopedStoreModules.value, processI18n),
268
- ]);
269
-
270
- watch(storeCategoryOptions, (options) => {
271
- if (!options.some((option) => option.value === storeCategory.value)) {
272
- storeCategory.value = MODULE_CATEGORY_ALL;
273
- }
274
- });
275
-
276
- const visibleStoreModules = computed(() =>
277
- filterModules(scopedStoreModules.value, {
278
- query: storeQuery.value,
279
- category: storeCategory.value,
280
- resolve: processI18n,
281
- }),
282
- );
283
- const trimmedStoreQuery = computed(() => storeQuery.value.trim());
284
-
285
- function clearStoreFilters(): void {
286
- storeQuery.value = "";
287
- storeCategory.value = MODULE_CATEGORY_ALL;
288
- }
289
-
290
- /** Store modules whose interface the running frontend does not match yet. */
291
- const frontendLagging = computed(() =>
292
- storeModules.value.filter(
293
- (entry) =>
294
- isAwaitingFrontend(entry) ||
295
- (entry.frontend === "unknown" &&
296
- moduleStore.awaitingReload.value.has(entry.id)),
297
- ),
249
+ const { runStoreAction, frontendNotice } = useModuleStoreActions(
250
+ moduleStore,
251
+ storeModules,
298
252
  );
299
- const frontendNotice = computed(() => {
300
- const lagging = frontendLagging.value;
301
- if (lagging.length === 0) return null;
302
- const names = lagging.map((entry) => processI18n(entry.title)).join(", ");
303
- const hasReported = lagging.every((entry) => entry.frontend !== "unknown");
304
- const key = !hasReported
305
- ? "unknown"
306
- : storeListing.value?.dev
307
- ? "dev"
308
- : "production";
309
- return { key, names };
310
- });
311
-
312
- /**
313
- * Runs `kind` from a dialog: a refusal is worded as the toast words it and
314
- * shown in the dialog, which stays open.
315
- */
316
- async function runFromDialog(
317
- kind: StoreOperationKind,
318
- entry: ModuleStoreEntry,
319
- body?: StoreActionBody,
320
- ): Promise<void> {
321
- try {
322
- await moduleStore.run(kind, entry, body);
323
- } catch (error) {
324
- throw new ConfirmActionError({
325
- title: t("modules.store.action_failed", {
326
- module: processI18n(entry.title),
327
- }),
328
- description: resolveStoreError(error),
329
- });
330
- }
331
- }
332
-
333
- /** What the owner last picked in a module's install dialog, for a retry. */
334
- const lastDependencyDrafts = new Map<string, DependencyDraft[]>();
335
-
336
- function renderDependencyPicker(
337
- entry: ModuleStoreEntry,
338
- drafts: Ref<DependencyDraft[]>,
339
- showErrors: boolean,
340
- ): VNode {
341
- return h(ModuleStoreDependencyPicker, {
342
- missing: entry.missingInterfaces,
343
- drafts: drafts.value,
344
- showErrors,
345
- onUpdate: (index: number, patch: Partial<DependencyDraft>) => {
346
- drafts.value = drafts.value.map((draft, position) =>
347
- position === index ? { ...draft, ...patch } : draft,
348
- );
349
- },
350
- });
351
- }
352
-
353
- /**
354
- * Asks which modules implement the interfaces the release misses, then
355
- * installs them with it. A refusal shows in the dialog, kept open.
356
- */
357
- async function installWithDependencies(entry: ModuleStoreEntry): Promise<void> {
358
- const title = processI18n(entry.title);
359
- const drafts = ref(
360
- createDependencyDrafts(
361
- entry.missingInterfaces,
362
- lastDependencyDrafts.get(entry.id),
363
- ),
364
- );
365
- const showErrors = ref(false);
366
- await confirm({
367
- title: t("modules.store.install_dialog.title", { module: title }),
368
- description: t("modules.store.install_dialog.description", {
369
- module: title,
370
- }),
371
- confirmLabel: t("modules.store.actions.install"),
372
- confirmIcon: "i-ph-download-simple",
373
- icon: "i-ph-puzzle-piece",
374
- body: () => renderDependencyPicker(entry, drafts, showErrors.value),
375
- validate: () => {
376
- showErrors.value = true;
377
- return areDependencyDraftsValid(drafts.value);
378
- },
379
- onConfirm: async () => {
380
- lastDependencyDrafts.set(entry.id, drafts.value);
381
- await runFromDialog(
382
- "install",
383
- entry,
384
- buildInstallBody(entry.id, drafts.value),
385
- );
386
- },
387
- });
388
- }
389
-
390
- async function runStoreAction(
391
- kind: StoreOperationKind,
392
- entry: ModuleStoreEntry,
393
- ): Promise<void> {
394
- const title = processI18n(entry.title);
395
- if (needsDependencyChoice(entry, kind)) {
396
- await installWithDependencies(entry);
397
- return;
398
- }
399
- if (kind === "uninstall") {
400
- await confirm({
401
- title: t("modules.store.uninstall_confirm.title", { module: title }),
402
- description: t("modules.store.uninstall_confirm.description", {
403
- module: title,
404
- }),
405
- confirmLabel: t("modules.store.actions.uninstall"),
406
- color: "error",
407
- icon: "i-ph-trash",
408
- onConfirm: () => runFromDialog(kind, entry),
409
- });
410
- return;
411
- }
412
- try {
413
- await moduleStore.run(kind, entry);
414
- } catch (error) {
415
- toast.add({
416
- color: "error",
417
- icon: "i-ph-warning-circle",
418
- title: t("modules.store.action_failed", { module: title }),
419
- description: resolveStoreError(error),
420
- });
421
- }
422
- }
423
-
424
- /** What a refused store request carries: the server's error key, if any. */
425
- interface StoreRequestError {
426
- data?: unknown;
427
- message?: string;
428
- }
429
253
 
430
- function resolveStoreError(error: unknown): string {
431
- const details = error as StoreRequestError | undefined;
432
- const key = typeof details?.data === "string" ? details.data : undefined;
433
- if (!key) return details?.message ?? "";
434
- return t(`modules.store.errors.${key.replace("error.", "")}`);
254
+ /** Whether a tile's store actions wait: read-only, or another one runs. */
255
+ function isTileLocked(moduleId: string): boolean {
256
+ const listing = storeListing.value;
257
+ if (!listing?.enabled) return true;
258
+ return listing.busy && storeEntries.value.get(moduleId)?.state !== "busy";
435
259
  }
436
260
 
437
261
  // ── Actions ────────────────────────────────────────────────────────
@@ -544,6 +368,11 @@ const summaryCards = computed<StatGroupItem[]>(() =>
544
368
  :loading="isInitiallyLoading"
545
369
  />
546
370
 
371
+ <ModuleStoreBanners
372
+ :listing="storeListing"
373
+ :frontend-notice="frontendNotice"
374
+ />
375
+
547
376
  <!-- Installed -->
548
377
  <section>
549
378
  <DmsSectionHeader
@@ -582,38 +411,13 @@ const summaryCards = computed<StatGroupItem[]>(() =>
582
411
  <!-- Loading -->
583
412
  <div
584
413
  v-if="isInitiallyLoading"
585
- :class="TILE_GRID_CLASS"
414
+ :class="MODULE_TILE_GRID_CLASS"
586
415
  aria-busy="true"
587
416
  >
588
- <DmsCard
417
+ <ModuleTileSkeleton
589
418
  v-for="placeholder in skeletonItems"
590
419
  :key="placeholder"
591
- :padded="false"
592
- class="flex min-h-[228px] flex-col gap-3 p-4"
593
- >
594
- <div class="flex items-start justify-between">
595
- <USkeleton
596
- :aria-label="t('dms.a11y.loading')"
597
- class="size-9 rounded-[10px]"
598
- />
599
- <USkeleton
600
- :aria-label="t('dms.a11y.loading')"
601
- class="h-5 w-12 rounded-full"
602
- />
603
- </div>
604
- <USkeleton
605
- :aria-label="t('dms.a11y.loading')"
606
- class="h-2.5 w-4/5"
607
- />
608
- <USkeleton
609
- :aria-label="t('dms.a11y.loading')"
610
- class="h-2.5 w-3/5"
611
- />
612
- <USkeleton
613
- :aria-label="t('dms.a11y.loading')"
614
- class="mt-auto h-4 w-2/5"
615
- />
616
- </DmsCard>
420
+ />
617
421
  </div>
618
422
 
619
423
  <!-- Nothing installed -->
@@ -691,214 +495,23 @@ const summaryCards = computed<StatGroupItem[]>(() =>
691
495
  </DmsEmptyState>
692
496
  </DmsCard>
693
497
 
694
- <div v-else :class="TILE_GRID_CLASS">
498
+ <div v-else :class="MODULE_TILE_GRID_CLASS">
695
499
  <ModuleCatalogTile
696
500
  v-for="entry in visibleModules"
697
501
  :key="entry.id"
698
502
  :entry="entry"
699
- :to="moduleTarget(entry)"
503
+ :to="storeOpenTarget(entry, modules)"
504
+ :store-entry="storeEntries.get(entry.id)"
505
+ :is-locked="isTileLocked(entry.id)"
506
+ @action="
507
+ (kind) => {
508
+ const storeEntry = storeEntries.get(entry.id);
509
+ if (storeEntry) void runStoreAction(kind, storeEntry);
510
+ }
511
+ "
700
512
  />
701
513
  </div>
702
514
  </section>
703
-
704
- <!-- Module store: the official modules, installed or not. -->
705
- <section class="mt-10" aria-labelledby="module-store-title">
706
- <DmsSectionHeader
707
- class="mb-3"
708
- :count="isStoreLoading ? undefined : storeModules.length"
709
- :description="t('modules.store.description')"
710
- >
711
- <template #title>
712
- <span id="module-store-title">{{ t("modules.store.title") }}</span>
713
- </template>
714
- <template #trailing>
715
- <span
716
- v-if="storeListing?.frontendVersion"
717
- :class="[
718
- MONO_CHIP_CLASS,
719
- 'text-dimmed border border-(--ui-border) text-[11px] max-sm:hidden',
720
- ]"
721
- :title="
722
- storeListing.frontendVersionSource
723
- ? t(
724
- `modules.store.frontend_version.${storeListing.frontendVersionSource}`,
725
- )
726
- : undefined
727
- "
728
- >
729
- {{
730
- t("modules.store.frontend_version.label", {
731
- version: storeListing.frontendVersion,
732
- })
733
- }}
734
- </span>
735
- <DmsSegmented
736
- v-model="storeScope"
737
- :items="storeScopeItems"
738
- size="sm"
739
- :aria-label="t('modules.store.scope.label')"
740
- />
741
- </template>
742
- </DmsSectionHeader>
743
-
744
- <DmsBanner
745
- v-if="storeListing?.configError"
746
- class="mb-4"
747
- tone="error"
748
- size="sm"
749
- icon="i-ph-file-x"
750
- :title="t('modules.store.config_error')"
751
- :description="storeListing.configError"
752
- />
753
- <DmsBanner
754
- v-else-if="storeListing && !storeListing.enabled"
755
- class="mb-4"
756
- tone="info"
757
- size="sm"
758
- icon="i-ph-lock-simple"
759
- :title="t('modules.store.disabled.title')"
760
- :description="t('modules.store.disabled.description')"
761
- />
762
-
763
- <!-- The interface lags behind the backend: say what follows. -->
764
- <DmsBanner
765
- v-if="frontendNotice"
766
- class="mb-4"
767
- :tone="frontendNotice.key === 'dev' ? 'info' : 'warning'"
768
- size="sm"
769
- :icon="
770
- frontendNotice.key === 'dev'
771
- ? 'i-ph-circle-notch'
772
- : 'i-ph-arrows-clockwise'
773
- "
774
- :title="
775
- t(`modules.store.frontend.${frontendNotice.key}.title`, {
776
- modules: frontendNotice.names,
777
- })
778
- "
779
- >
780
- <template #description>
781
- <i18n-t
782
- :keypath="`modules.store.frontend.${frontendNotice.key}.description`"
783
- scope="global"
784
- tag="span"
785
- >
786
- <template #command>
787
- <code
788
- :class="[
789
- MONO_CHIP_CLASS,
790
- 'bg-elevated text-highlighted text-xs',
791
- ]"
792
- >
793
- {{ FRONTEND_BUILD_COMMAND }}
794
- </code>
795
- </template>
796
- </i18n-t>
797
- </template>
798
- </DmsBanner>
799
-
800
- <div v-if="isStoreLoading" :class="TILE_GRID_CLASS" aria-busy="true">
801
- <DmsCard
802
- v-for="placeholder in skeletonItems"
803
- :key="placeholder"
804
- :padded="false"
805
- class="flex min-h-[228px] flex-col gap-3 p-4"
806
- >
807
- <USkeleton
808
- :aria-label="t('dms.a11y.loading')"
809
- class="size-9 rounded-[10px]"
810
- />
811
- <USkeleton
812
- :aria-label="t('dms.a11y.loading')"
813
- class="mt-auto h-2.5 w-3/5"
814
- />
815
- </DmsCard>
816
- </div>
817
-
818
- <!-- Only when nothing is on screen yet: a follow-up read that fails
819
- while the frontend restarts keeps the tiles and tries again. -->
820
- <DmsBanner
821
- v-else-if="moduleStore.loadingError.value && !storeListing"
822
- tone="error"
823
- size="sm"
824
- icon="i-ph-plugs"
825
- :title="t('modules.store.load_error')"
826
- >
827
- <template #actions>
828
- <UButton
829
- color="error"
830
- size="sm"
831
- icon="i-ph-arrows-clockwise"
832
- :label="t('modules.error.retry')"
833
- @click="moduleStore.load()"
834
- />
835
- </template>
836
- </DmsBanner>
837
-
838
- <template v-else>
839
- <div :class="TOOLBAR_CLASS">
840
- <DmsSearchInput
841
- v-model="storeQuery"
842
- size="sm"
843
- :placeholder="t('modules.store.search_placeholder')"
844
- class="w-[300px] max-md:w-full"
845
- />
846
- <DmsChipGroup
847
- :items="storeCategoryOptions"
848
- :selected="[storeCategory]"
849
- :label="t('modules.store.category_label')"
850
- @pick="storeCategory = $event"
851
- />
852
- </div>
853
-
854
- <DmsCard
855
- v-if="scopedStoreModules.length === 0 && storeScope === 'available'"
856
- :padded="false"
857
- >
858
- <DmsEmptyState
859
- icon="i-ph-check-circle"
860
- :title="t('modules.store.all_installed.title')"
861
- :description="t('modules.store.all_installed.description')"
862
- />
863
- </DmsCard>
864
-
865
- <DmsCard v-else-if="visibleStoreModules.length === 0" :padded="false">
866
- <DmsEmptyState
867
- variant="no-result"
868
- :title="
869
- trimmedStoreQuery
870
- ? t('modules.store.no_result.title', {
871
- query: trimmedStoreQuery,
872
- })
873
- : t('modules.store.no_result.title_filtered')
874
- "
875
- >
876
- <template #actions>
877
- <UButton
878
- color="neutral"
879
- variant="outline"
880
- size="sm"
881
- :label="t('modules.no_result.clear')"
882
- @click="clearStoreFilters"
883
- />
884
- </template>
885
- </DmsEmptyState>
886
- </DmsCard>
887
-
888
- <div v-else :class="TILE_GRID_CLASS">
889
- <ModuleStoreTile
890
- v-for="entry in visibleStoreModules"
891
- :key="entry.id"
892
- :entry="entry"
893
- :is-locked="
894
- !storeListing?.enabled ||
895
- (storeListing?.busy === true && entry.state !== 'busy')
896
- "
897
- @action="runStoreAction($event, entry)"
898
- />
899
- </div>
900
- </template>
901
- </section>
902
515
  </template>
903
516
  </div>
904
517
  </template>
@@ -88,6 +88,9 @@ export default defineDmsMiddleware(async (to, from) => {
88
88
 
89
89
  const moduleEntry = siteLayout.modules.value?.[moduleId];
90
90
 
91
+ // A page of the Modules page itself (the module store), not of a module.
92
+ if (!moduleEntry && siteLayout.findMatchingRoute(to.path)?.metadata) return;
93
+
91
94
  if (!moduleId || !moduleEntry) {
92
95
  throw createDmsError({
93
96
  statusCode: MODULE_NOT_FOUND_STATUS,