@antelopejs/dms 0.9.7 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +1 -1
  3. package/data/module-store/catalog.json +2 -2
  4. package/dist/attachments/table-view.js +2 -2
  5. package/dist/attachments/table-view.js.map +1 -1
  6. package/dist/automation/actions.d.ts +2 -0
  7. package/dist/automation/actions.js +116 -71
  8. package/dist/automation/actions.js.map +1 -1
  9. package/dist/automation/i18n.d.ts +4 -0
  10. package/dist/automation/i18n.js +19 -0
  11. package/dist/automation/i18n.js.map +1 -0
  12. package/dist/automation/send-notification.d.ts +21 -0
  13. package/dist/automation/send-notification.js +163 -0
  14. package/dist/automation/send-notification.js.map +1 -0
  15. package/dist/automation/triggers.js +19 -25
  16. package/dist/automation/triggers.js.map +1 -1
  17. package/dist/client-origin-cors.d.ts +1 -0
  18. package/dist/client-origin-cors.js +19 -0
  19. package/dist/client-origin-cors.js.map +1 -0
  20. package/dist/config.d.ts +3 -0
  21. package/dist/config.js +5 -3
  22. package/dist/config.js.map +1 -1
  23. package/dist/frontend-module.d.ts +4 -0
  24. package/dist/frontend-module.js +37 -0
  25. package/dist/frontend-module.js.map +1 -0
  26. package/dist/implementations/dms/client-ip.js +1 -1
  27. package/dist/implementations/dms/client-ip.js.map +1 -1
  28. package/dist/implementations/dms/dev-reload.d.ts +5 -0
  29. package/dist/implementations/dms/dev-reload.js +23 -3
  30. package/dist/implementations/dms/dev-reload.js.map +1 -1
  31. package/dist/implementations/dms/frontend-modules.d.ts +2 -4
  32. package/dist/implementations/dms/frontend-modules.js +22 -89
  33. package/dist/implementations/dms/frontend-modules.js.map +1 -1
  34. package/dist/implementations/dms/frontend-ready.d.ts +12 -2
  35. package/dist/implementations/dms/frontend-ready.js +34 -4
  36. package/dist/implementations/dms/frontend-ready.js.map +1 -1
  37. package/dist/implementations/dms/layout-banners.d.ts +2 -1
  38. package/dist/implementations/dms/layout-banners.js +9 -0
  39. package/dist/implementations/dms/layout-banners.js.map +1 -1
  40. package/dist/implementations/dms/manifest.d.ts +1 -2
  41. package/dist/implementations/dms/manifest.js +2 -0
  42. package/dist/implementations/dms/manifest.js.map +1 -1
  43. package/dist/implementations/dms/page.d.ts +4 -3
  44. package/dist/implementations/dms/page.js +49 -18
  45. package/dist/implementations/dms/page.js.map +1 -1
  46. package/dist/implementations/dms/permissions-resolver.d.ts +2 -2
  47. package/dist/implementations/dms/permissions-resolver.js +2 -2
  48. package/dist/implementations/dms/permissions-resolver.js.map +1 -1
  49. package/dist/implementations/dms/realtime.js +1 -0
  50. package/dist/implementations/dms/realtime.js.map +1 -1
  51. package/dist/implementations/dms-base/bulk-selection.d.ts +1 -1
  52. package/dist/implementations/dms-base/bulk-selection.js +23 -1
  53. package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
  54. package/dist/implementations/dms-base/filter-tokens.js +5 -3
  55. package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
  56. package/dist/implementations/dms-base/search-route.d.ts +1 -0
  57. package/dist/implementations/dms-base/search-route.js +35 -13
  58. package/dist/implementations/dms-base/search-route.js.map +1 -1
  59. package/dist/implementations/dms-base/table-view.js +11 -6
  60. package/dist/implementations/dms-base/table-view.js.map +1 -1
  61. package/dist/implementations/dms-notifications/index.d.ts +2 -2
  62. package/dist/implementations/dms-notifications/index.js +5 -5
  63. package/dist/implementations/dms-notifications/index.js.map +1 -1
  64. package/dist/index.js +28 -45
  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/realtime.d.ts +13 -1
  117. package/dist/routes/realtime.js +114 -29
  118. package/dist/routes/realtime.js.map +1 -1
  119. package/dist/startup-config.d.ts +2 -0
  120. package/dist/startup-config.js +62 -0
  121. package/dist/startup-config.js.map +1 -0
  122. package/dist/store/interfaces.d.ts +3 -2
  123. package/dist/store/interfaces.js +16 -6
  124. package/dist/store/interfaces.js.map +1 -1
  125. package/dist/store/listing.js +24 -13
  126. package/dist/store/listing.js.map +1 -1
  127. package/dist/store/operations.js +4 -0
  128. package/dist/store/operations.js.map +1 -1
  129. package/dist/store/requirements.js +3 -2
  130. package/dist/store/requirements.js.map +1 -1
  131. package/dist/store/runtime.d.ts +10 -1
  132. package/dist/store/runtime.js +33 -6
  133. package/dist/store/runtime.js.map +1 -1
  134. package/dist/store/types.d.ts +10 -0
  135. package/dist/validation/auth.schema.d.ts +2 -2
  136. package/dist/validation/member-invite.schema.d.ts +2 -10
  137. package/dist/validation/member-invite.schema.js +2 -15
  138. package/dist/validation/member-invite.schema.js.map +1 -1
  139. package/frontend-vue/dms.frontend.ts +24 -5
  140. package/frontend-vue/dms.server.ts +17 -0
  141. package/frontend-vue/layers/dms-auth/server/accounts.ts +3 -4
  142. package/frontend-vue/layers/dms-core/app/build/composables/renderNow.ts +34 -0
  143. package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +5 -1
  144. package/frontend-vue/layers/dms-core/app/build/utils/crossTab.ts +199 -0
  145. package/frontend-vue/layers/dms-core/app/build/utils/sharedRealtimeConnection.ts +410 -0
  146. package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +77 -12
  147. package/frontend-vue/layers/dms-core/app/composables/data-types/useDataType.ts +26 -0
  148. package/frontend-vue/layers/dms-core/app/composables/hydration/isHydratingComponent.ts +29 -0
  149. package/frontend-vue/layers/dms-core/app/composables/live/livePauseSource.ts +29 -0
  150. package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +150 -9
  151. package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +6 -4
  152. package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +27 -1
  153. package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +256 -119
  154. package/frontend-vue/layers/dms-core/app/composables/translation/useComposedText.ts +21 -8
  155. package/frontend-vue/layers/dms-core/app/composables/translation/useTranslation.ts +22 -5
  156. package/frontend-vue/layers/dms-core/app/plugins/dev-error-log.ts +16 -0
  157. package/frontend-vue/layers/dms-core/app/types/user.ts +6 -0
  158. package/frontend-vue/layers/dms-core/app/utils/composedText.ts +7 -2
  159. package/frontend-vue/layers/dms-core/app/utils/formatter.ts +166 -35
  160. package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +18 -1
  161. package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +134 -2
  162. package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +134 -2
  163. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +3 -2
  164. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +41 -21
  165. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +9 -0
  166. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +4 -19
  167. package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +11 -3
  168. package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
  169. package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionBar.vue +8 -0
  170. package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +14 -0
  171. package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +43 -82
  172. package/frontend-vue/layers/dms-layout/app/build/components/layout/SidebarUserMenu.vue +44 -3
  173. package/frontend-vue/layers/dms-layout/app/build/components/layout/UserIdentity.vue +11 -3
  174. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleCatalogTile.vue +132 -35
  175. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleManageMenu.vue +59 -0
  176. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleSourceLabel.vue +25 -0
  177. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreBanners.vue +73 -0
  178. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreOperation.vue +116 -0
  179. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +8 -3
  180. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +85 -207
  181. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +10 -3
  182. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTileSkeleton.vue +68 -0
  183. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +24 -8
  184. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxItem.vue +8 -5
  185. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/region/RegionTimeZoneInput.vue +1 -0
  186. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-permissions.ts +19 -0
  187. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/useRoleEditor.ts +2 -1
  188. package/frontend-vue/layers/dms-layout/app/build/composables/layout/useSidePanelWidth.ts +31 -70
  189. package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationGroupsOpen.ts +11 -1
  190. package/frontend-vue/layers/dms-layout/app/build/utils/dev-reload-stream.ts +81 -0
  191. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +232 -36
  192. package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +122 -14
  193. package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +32 -3
  194. package/frontend-vue/layers/dms-layout/app/composables/layout/usePageRecord.ts +50 -0
  195. package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +25 -7
  196. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
  197. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +51 -4
  198. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +194 -0
  199. package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +23 -3
  200. package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +4 -2
  201. package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
  202. package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +9 -0
  203. package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +9 -0
  204. package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +291 -0
  205. package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +87 -474
  206. package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +3 -0
  207. package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +6 -22
  208. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +2 -1
  209. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +14 -44
  210. package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
  211. package/frontend-vue/layers/dms-layout/app/theme/form-controls.ts +9 -4
  212. package/frontend-vue/layers/dms-layout/app/theme/surfaces.ts +22 -6
  213. package/frontend-vue/layers/dms-layout/app/types/module-store.ts +9 -0
  214. package/frontend-vue/layers/dms-layout/app/types/page.ts +15 -1
  215. package/frontend-vue/layers/dms-layout/app/utils/account-menu.ts +11 -0
  216. package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +57 -0
  217. package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +26 -0
  218. package/frontend-vue/layers/dms-layout/app/utils/pageRecord.ts +71 -0
  219. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +39 -14
  220. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +38 -13
  221. package/frontend-vue/layers/dms-ui/app/build/components/containers/TargetComponentTree.vue +31 -0
  222. package/frontend-vue/layers/dms-ui/app/build/components/containers/constants.ts +25 -0
  223. package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +244 -15
  224. package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +42 -25
  225. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowHeader.vue +153 -0
  226. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowNotFound.vue +44 -0
  227. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/drawerRowHeader.ts +35 -0
  228. package/frontend-vue/layers/dms-ui/app/build/components/form/FormEntries.vue +44 -9
  229. package/frontend-vue/layers/dms-ui/app/build/components/form/FormErrorSummary.vue +11 -4
  230. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +38 -5
  231. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldNotes.vue +29 -5
  232. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +3 -1
  233. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSections.vue +106 -27
  234. package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +12 -4
  235. package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +4 -2
  236. package/frontend-vue/layers/dms-ui/app/build/components/table/FiltersRow.vue +38 -1
  237. package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +55 -54
  238. package/frontend-vue/layers/dms-ui/app/build/components/table/RowSelection.vue +2 -1
  239. package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +396 -288
  240. package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +2 -1
  241. package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +2 -1
  242. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardAvatar.vue +59 -0
  243. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +19 -2
  244. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardRowActions.vue +38 -0
  245. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +148 -104
  246. package/frontend-vue/layers/dms-ui/app/build/components/table-view/LiveHoldBanner.vue +17 -2
  247. package/frontend-vue/layers/dms-ui/app/build/composables/actions/bulkSelection.ts +43 -9
  248. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowComponentOptions.ts +76 -0
  249. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +172 -27
  250. package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +270 -46
  251. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +2 -0
  252. package/frontend-vue/layers/dms-ui/app/build/composables/containers/closingLayers.ts +186 -0
  253. package/frontend-vue/layers/dms-ui/app/build/composables/containers/context.ts +14 -0
  254. package/frontend-vue/layers/dms-ui/app/build/composables/containers/drawerSize.ts +229 -0
  255. package/frontend-vue/layers/dms-ui/app/build/composables/containers/useResizableSize.ts +175 -0
  256. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +18 -35
  257. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/numberSuffix.ts +47 -0
  258. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +99 -24
  259. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerFieldTypes.ts +2 -1
  260. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationFilter.ts +75 -0
  261. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationRows.ts +71 -0
  262. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationValue.ts +39 -0
  263. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/twoLineCell.ts +3 -3
  264. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/useColumnValueRenderer.ts +6 -2
  265. package/frontend-vue/layers/dms-ui/app/build/composables/form/createFormFields.ts +15 -0
  266. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldSaved.ts +40 -0
  267. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +62 -0
  268. package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryContext.ts +5 -0
  269. package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +42 -7
  270. package/frontend-vue/layers/dms-ui/app/build/composables/form/formLayout.ts +106 -4
  271. package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
  272. package/frontend-vue/layers/dms-ui/app/build/composables/form/readonlyValue.ts +19 -0
  273. package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +27 -0
  274. package/frontend-vue/layers/dms-ui/app/build/composables/form/repeaterTable.ts +65 -0
  275. package/frontend-vue/layers/dms-ui/app/build/composables/form/serverFieldErrors.ts +49 -0
  276. package/frontend-vue/layers/dms-ui/app/build/composables/form/toasterClearance.ts +118 -0
  277. package/frontend-vue/layers/dms-ui/app/build/composables/form/useInstantForm.ts +12 -1
  278. package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +41 -3
  279. package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +10 -2
  280. package/frontend-vue/layers/dms-ui/app/build/composables/live/pageReadClock.ts +28 -0
  281. package/frontend-vue/layers/dms-ui/app/build/composables/live/shortcuts.ts +1 -1
  282. package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +17 -3
  283. package/frontend-vue/layers/dms-ui/app/build/composables/live/useUpdatedLabel.ts +38 -0
  284. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/pageToolbarContext.ts +75 -0
  285. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/toolbarControlHeight.ts +29 -0
  286. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparison.ts +52 -0
  287. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +21 -0
  288. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodShortLabels.ts +28 -0
  289. package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +1 -0
  290. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRelationFilterLabels.ts +73 -0
  291. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRowScrollAnchor.ts +106 -0
  292. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +9 -0
  293. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +1 -2
  294. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/archivedCount.ts +31 -0
  295. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/filterEditorValue.ts +38 -0
  296. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/formatFilterValue.ts +33 -2
  297. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/scrollEdges.ts +31 -0
  298. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useCardFields.ts +33 -0
  299. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useMobileDisplay.ts +48 -0
  300. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +41 -18
  301. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +3 -2
  302. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLinkedVisit.ts +78 -0
  303. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +135 -22
  304. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +56 -25
  305. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +77 -32
  306. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +86 -10
  307. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/bulkActions.ts +14 -4
  308. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/card.ts +127 -2
  309. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/compareValues.ts +13 -0
  310. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/expandedRows.ts +28 -0
  311. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/groupedRows.ts +114 -1
  312. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/linkedArrival.ts +308 -0
  313. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/locateRow.ts +106 -0
  314. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pageArrival.ts +34 -0
  315. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +17 -0
  316. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/quickFilters.ts +41 -33
  317. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +24 -8
  318. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableCounts.ts +113 -0
  319. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +27 -4
  320. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +22 -6
  321. package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
  322. package/frontend-vue/layers/dms-ui/app/build/utils/eyebrow.ts +12 -0
  323. package/frontend-vue/layers/dms-ui/app/build/utils/fieldOptionsI18n.ts +51 -11
  324. package/frontend-vue/layers/dms-ui/app/build/utils/inputWidth.ts +53 -0
  325. package/frontend-vue/layers/dms-ui/app/build/utils/numberAdjustment.ts +6 -3
  326. package/frontend-vue/layers/dms-ui/app/build/utils/relativeDate.ts +19 -5
  327. package/frontend-vue/layers/dms-ui/app/build/utils/scrollWithin.ts +99 -0
  328. package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +36 -14
  329. package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
  330. package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +9 -1
  331. package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +80 -32
  332. package/frontend-vue/layers/dms-ui/app/components/blocks/PageToolbarBlock.vue +137 -0
  333. package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +5 -0
  334. package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +16 -2
  335. package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +24 -5
  336. package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +41 -23
  337. package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +1 -1
  338. package/frontend-vue/layers/dms-ui/app/components/container-footer/ContainerFooter.vue +34 -0
  339. package/frontend-vue/layers/dms-ui/app/components/field-row/FieldRow.vue +25 -2
  340. package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +2 -2
  341. package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +118 -36
  342. package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +20 -1
  343. package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +13 -1
  344. package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +8 -3
  345. package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +53 -5
  346. package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +29 -5
  347. package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +45 -8
  348. package/frontend-vue/layers/dms-ui/app/components/form/components/InputPercentage.vue +28 -5
  349. package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +5 -0
  350. package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +19 -2
  351. package/frontend-vue/layers/dms-ui/app/components/form/components/InputTime.vue +2 -1
  352. package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +205 -65
  353. package/frontend-vue/layers/dms-ui/app/components/form/components/Repeater.vue +80 -30
  354. package/frontend-vue/layers/dms-ui/app/components/form/components/RepeaterTable.vue +203 -0
  355. package/frontend-vue/layers/dms-ui/app/components/form/components/Select.vue +4 -2
  356. package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +11 -2
  357. package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +79 -70
  358. package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +117 -21
  359. package/frontend-vue/layers/dms-ui/app/components/meter/Meter.vue +2 -1
  360. package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +113 -34
  361. package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +14 -2
  362. package/frontend-vue/layers/dms-ui/app/components/section-header/Eyebrow.vue +6 -1
  363. package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +13 -0
  364. package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +52 -3
  365. package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +2 -1
  366. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +45 -35
  367. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanDisplay.vue +4 -1
  368. package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +410 -96
  369. package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +33 -5
  370. package/frontend-vue/layers/dms-ui/app/composables/chart/apexCategoryLabels.ts +68 -0
  371. package/frontend-vue/layers/dms-ui/app/composables/chart/apexDatetimeBuckets.ts +306 -0
  372. package/frontend-vue/layers/dms-ui/app/composables/chart/formatValue.ts +4 -3
  373. package/frontend-vue/layers/dms-ui/app/composables/chart/heatmapColors.ts +93 -0
  374. package/frontend-vue/layers/dms-ui/app/composables/chart/localizeSeries.ts +62 -0
  375. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +116 -8
  376. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.types.ts +7 -0
  377. package/frontend-vue/layers/dms-ui/app/composables/containers/types.ts +84 -1
  378. package/frontend-vue/layers/dms-ui/app/composables/containers/useDrawer.ts +69 -8
  379. package/frontend-vue/layers/dms-ui/app/composables/form/types/field-saved.ts +11 -0
  380. package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +4 -0
  381. package/frontend-vue/layers/dms-ui/app/composables/form/types/functions.ts +1 -0
  382. package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +142 -21
  383. package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +11 -4
  384. package/frontend-vue/layers/dms-ui/app/composables/page-toolbar/usePageToolbarSize.ts +30 -0
  385. package/frontend-vue/layers/dms-ui/app/composables/table-view/kanban.ts +4 -2
  386. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +22 -3
  387. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +4 -0
  388. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +24 -1
  389. package/frontend-vue/layers/dms-ui/app/plugins/closing-layers.client.ts +10 -0
  390. package/frontend-vue/layers/dms-ui/app/plugins/live-pause-source.ts +15 -0
  391. package/frontend-vue/layers/dms-ui/app/plugins/register.ts +2 -0
  392. package/frontend-vue/layers/dms-ui/app/types/input-number.ts +3 -2
  393. package/frontend-vue/layers/dms-ui/app/types/page-toolbar.ts +7 -0
  394. package/frontend-vue/layers/dms-ui/app/types/row-action.ts +0 -2
  395. package/frontend-vue/layers/dms-ui/app/types/row-navigation.ts +17 -0
  396. package/frontend-vue/layers/dms-ui/app/types/table/query.ts +2 -1
  397. package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +3 -1
  398. package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
  399. package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +16 -4
  400. package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +16 -4
  401. package/frontend-vue/package.json +2 -2
  402. package/package.json +7 -6
  403. package/skills/dms/SKILL.md +1 -1
  404. package/skills/dms-auth/SKILL.md +5 -3
  405. package/skills/dms-best-practices/REFERENCE.md +2 -0
  406. package/skills/dms-best-practices/SKILL.md +4 -0
  407. package/skills/dms-module/SKILL.md +3 -2
  408. package/skills/dms-pages/REFERENCE.md +125 -16
  409. package/skills/dms-pages/SKILL.md +1 -1
  410. package/skills/dms-project/DEV-SERVERS.md +23 -1
  411. package/skills/dms-project/REFERENCE.md +1 -1
  412. package/skills/dms-project/SKILL.md +4 -1
  413. package/skills/dms-project/scripts/dms-dev.sh +205 -17
  414. package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
@@ -1,35 +1,22 @@
1
1
  import type { Ref } from "vue";
2
+ import {
3
+ RESIZE_KEYBOARD_STEP_PX,
4
+ useResizableSize,
5
+ } from "#dms-ui/app/build/composables/containers/useResizableSize";
2
6
  import {
3
7
  resolveSidePanelWidthBounds,
4
8
  type SidePanel,
5
9
  type SidePanelWidthBounds,
6
10
  } from "../../../composables/useAppSidePanels";
7
11
  import {
8
- clampSidePanelWidth,
9
12
  readSidePanelPreferences,
10
- storedSidePanelWidth,
11
13
  storeSidePanelWidth,
12
14
  useSidePanelDragWidth,
13
15
  useSidePanelPreferences,
14
16
  } from "./sidePanelState";
15
17
 
16
18
  /** @internal */
17
- export const SIDE_PANEL_KEYBOARD_STEP_PX = 16;
18
-
19
- type WidthStep = (width: number, bounds: SidePanelWidthBounds) => number;
20
-
21
- // The panel sits on the right: the arrow pointing away from it widens it.
22
- const KEYBOARD_STEPS: Record<string, WidthStep> = {
23
- ArrowLeft: (width) => width + SIDE_PANEL_KEYBOARD_STEP_PX,
24
- ArrowRight: (width) => width - SIDE_PANEL_KEYBOARD_STEP_PX,
25
- Home: (_, bounds) => bounds.minWidth,
26
- End: (_, bounds) => bounds.maxWidth,
27
- };
28
-
29
- interface DragStart {
30
- x: number;
31
- width: number;
32
- }
19
+ export const SIDE_PANEL_KEYBOARD_STEP_PX = RESIZE_KEYBOARD_STEP_PX;
33
20
 
34
21
  /** @internal */
35
22
  export interface SidePanelWidth {
@@ -54,60 +41,34 @@ export interface SidePanelWidth {
54
41
  export function useSidePanelWidth(panel: SidePanel): SidePanelWidth {
55
42
  const bounds = resolveSidePanelWidthBounds(panel);
56
43
  const preferences = useSidePanelPreferences();
57
- const dragWidth = useSidePanelDragWidth();
58
- const width = computed(
59
- () =>
60
- dragWidth.value ??
61
- storedSidePanelWidth(
62
- readSidePanelPreferences(preferences.value),
63
- panel.id,
64
- bounds,
65
- ),
66
- );
67
- let dragStart: DragStart | null = null;
68
-
69
- function commit(next: number): void {
70
- storeSidePanelWidth(
71
- preferences,
72
- panel.id,
73
- clampSidePanelWidth(next, bounds),
74
- );
75
- }
76
-
77
- function startDrag(event: PointerEvent, renderedWidth: number): void {
78
- event.preventDefault();
79
- dragStart = { x: event.clientX, width: renderedWidth };
80
- dragWidth.value = clampSidePanelWidth(renderedWidth, bounds);
81
- }
82
-
83
- function drag(event: PointerEvent): void {
84
- if (dragStart === null) return;
85
- const next = dragStart.width + dragStart.x - event.clientX;
86
- dragWidth.value = clampSidePanelWidth(next, bounds);
87
- }
88
-
89
- function endDrag(): void {
90
- if (dragStart === null) return;
91
- dragStart = null;
92
- commit(width.value);
93
- dragWidth.value = null;
94
- }
95
-
96
- function resizeFromKey(event: KeyboardEvent): void {
97
- const step = KEYBOARD_STEPS[event.key];
98
- if (!step) return;
99
- event.preventDefault();
100
- commit(step(width.value, bounds));
101
- }
44
+ const resizable = useResizableSize({
45
+ side: "right",
46
+ bounds: {
47
+ defaultSize: bounds.defaultWidth,
48
+ minSize: bounds.minWidth,
49
+ maxSize: bounds.maxWidth,
50
+ },
51
+ storage: {
52
+ read: () => readSidePanelPreferences(preferences.value).widths[panel.id],
53
+ write: (width) =>
54
+ storeSidePanelWidth(
55
+ preferences,
56
+ panel.id,
57
+ width ?? bounds.defaultWidth,
58
+ ),
59
+ },
60
+ liveSize: useSidePanelDragWidth(),
61
+ });
102
62
 
103
63
  return {
104
- width,
64
+ // The panel always has a default width: its size is never natural.
65
+ width: computed(() => resizable.size.value ?? bounds.defaultWidth),
105
66
  bounds,
106
- isDragging: computed(() => dragWidth.value !== null),
107
- startDrag,
108
- drag,
109
- endDrag,
110
- resizeFromKey,
111
- reset: () => commit(bounds.defaultWidth),
67
+ isDragging: resizable.isDragging,
68
+ startDrag: resizable.startDrag,
69
+ drag: resizable.drag,
70
+ endDrag: resizable.endDrag,
71
+ resizeFromKey: (event) => resizable.resizeFromKey(event),
72
+ reset: resizable.reset,
112
73
  };
113
74
  }
@@ -28,12 +28,22 @@ function writeStorage(key: string, value: string): void {
28
28
  * Which groups of the preferences matrix are open. A group starts folded,
29
29
  * so the page opens short and each group row's count sums it up; the state
30
30
  * the viewer picks is kept per group in this browser.
31
+ *
32
+ * The kept state is read once mounted: the server cannot see it, so the
33
+ * server render and the browser hydrating it both draw every group folded,
34
+ * and a kept group opens right after (reading it in setup left the
35
+ * collapsible open on one side and folded on the other).
31
36
  */
32
37
  export const useNotificationGroupsOpen = () => {
33
38
  const picked = ref<Record<string, boolean>>({});
39
+ const isMounted = ref(false);
40
+ onMounted(() => {
41
+ isMounted.value = true;
42
+ });
34
43
 
35
44
  const isOpen = (categoryId: string): boolean =>
36
- picked.value[categoryId] ?? readStorage(storageKey(categoryId)) === OPEN;
45
+ picked.value[categoryId] ??
46
+ (isMounted.value && readStorage(storageKey(categoryId)) === OPEN);
37
47
 
38
48
  const toggleOpen = (categoryId: string) => {
39
49
  const open = !isOpen(categoryId);
@@ -1,5 +1,13 @@
1
+ import { type Ref, watch } from "vue";
2
+ import {
3
+ type CrossTabEnvironment,
4
+ shareAcrossTabs,
5
+ } from "#dms-core/app/build/utils/crossTab";
6
+
1
7
  const RELOAD_EVENT_NAME = "reload";
2
8
  const REOPEN_DELAY_MS = 2000;
9
+ const SHARED_STREAM_NAME_PREFIX = "dms-dev-reload";
10
+ const RELOAD_MESSAGE = "reload";
3
11
 
4
12
  export interface DevReloadStreamOptions {
5
13
  endpoint: string;
@@ -78,3 +86,76 @@ export function openDevReloadStream(
78
86
  },
79
87
  };
80
88
  }
89
+
90
+ /**
91
+ * The `reload` stream of every tab of the browser, held by one of them: a
92
+ * stream per tab would take one of the browser's six HTTP/1.1 connections to
93
+ * the dev server each. When the holding tab goes, another one opens it.
94
+ */
95
+ export function openSharedDevReloadStream(
96
+ options: DevReloadStreamOptions,
97
+ environment?: CrossTabEnvironment,
98
+ ): DevReloadStream {
99
+ const leave = shareAcrossTabs(
100
+ `${SHARED_STREAM_NAME_PREFIX}:${options.endpoint}`,
101
+ (emit) =>
102
+ openDevReloadStream({
103
+ endpoint: options.endpoint,
104
+ onReload: () => emit(RELOAD_MESSAGE),
105
+ }).close,
106
+ (message) => {
107
+ if (message === RELOAD_MESSAGE) options.onReload();
108
+ },
109
+ environment,
110
+ );
111
+ return { close: leave };
112
+ }
113
+
114
+ /** The user's realtime connection, as far as the reload signal goes. */
115
+ export interface DevReloadCarrier {
116
+ /**
117
+ * The topic the open connection carries the reload signal under, as the
118
+ * backend named it; unset while it carries none.
119
+ */
120
+ devReloadTopic: Ref<string | undefined>;
121
+ subscribe: (topic: string, handler: () => void) => () => void;
122
+ }
123
+
124
+ /**
125
+ * Follows the backend's reloads over the user's realtime connection while it
126
+ * is open and carries them, and over the reload stream the browser's tabs
127
+ * share only while it does not (signed out, reconnecting): a tab adds no
128
+ * long-lived connection of its own to the six HTTP/1.1 allows per host.
129
+ */
130
+ export function followDevReload(
131
+ options: DevReloadStreamOptions,
132
+ carrier: DevReloadCarrier,
133
+ environment?: CrossTabEnvironment,
134
+ ): DevReloadStream {
135
+ let stream: DevReloadStream | undefined;
136
+ let unsubscribe: (() => void) | undefined;
137
+ const stopWatching = watch(
138
+ carrier.devReloadTopic,
139
+ (topic) => {
140
+ unsubscribe?.();
141
+ unsubscribe = undefined;
142
+ if (topic) {
143
+ unsubscribe = carrier.subscribe(topic, () => options.onReload());
144
+ stream?.close();
145
+ stream = undefined;
146
+ return;
147
+ }
148
+ stream ??= openSharedDevReloadStream(options, environment);
149
+ },
150
+ { immediate: true },
151
+ );
152
+ return {
153
+ close: () => {
154
+ stopWatching();
155
+ unsubscribe?.();
156
+ unsubscribe = undefined;
157
+ stream?.close();
158
+ stream = undefined;
159
+ },
160
+ };
161
+ }
@@ -1,12 +1,17 @@
1
1
  import type { Tone } from "#dms-ui/app/types/tone";
2
2
  import type { StatusPillDot } from "#dms-ui/app/components/status-pill/StatusPill.vue";
3
- import type { ModuleCatalogEntry } from "../../types/page";
3
+ import { type ModuleCatalogEntry, MODULE_URL_PREFIX } from "../../types/page";
4
4
  import type {
5
+ ModuleSourceKind,
5
6
  StoreListingEntry,
6
7
  StoreModuleState,
7
8
  StoreOperationKind,
8
9
  } from "../../types/module-store";
9
- import type { DisplayResolver } from "./modules-catalog";
10
+ import {
11
+ type DisplayResolver,
12
+ compareDisplayNames,
13
+ moduleStatus,
14
+ } from "./modules-catalog";
10
15
 
11
16
  /** A store module as the Modules page filters and draws it. */
12
17
  export type ModuleStoreEntry = StoreListingEntry & {
@@ -17,13 +22,19 @@ export type ModuleStoreEntry = StoreListingEntry & {
17
22
  /** Which store modules the store section shows. */
18
23
  export type StoreScope = "all" | "available" | "installed";
19
24
 
20
- /** What a store tile's pill says. */
21
- export interface StoreTileStatus {
22
- tone: Tone;
23
- /** i18n key under `modules.store.state`. */
24
- labelKey: string;
25
- dot?: StatusPillDot;
26
- }
25
+ /**
26
+ * The scopes in the order the store's switch shows them. The store is for
27
+ * discovering, so it opens on what can still be installed; "all" comes last,
28
+ * the widest view.
29
+ */
30
+ export const STORE_SCOPES: readonly StoreScope[] = [
31
+ "available",
32
+ "installed",
33
+ "all",
34
+ ];
35
+
36
+ /** The scope the store opens on. */
37
+ export const DEFAULT_STORE_SCOPE: StoreScope = "available";
27
38
 
28
39
  /** One action a store tile offers. */
29
40
  export interface StoreAction {
@@ -42,18 +53,18 @@ export interface StoreAction {
42
53
 
43
54
  const CATEGORY_PREFIX = "$modules.store.category";
44
55
 
56
+ /** The icon a tile's footer names a module's source with. */
57
+ export const MODULE_SOURCE_ICONS: Record<ModuleSourceKind, string> = {
58
+ npm: "i-ph-package",
59
+ github: "i-ph-github-logo",
60
+ gitlab: "i-ph-gitlab-logo",
61
+ git: "i-ph-git-branch",
62
+ local: "i-ph-folder-simple",
63
+ };
64
+
45
65
  /** The command that turns a production frontend into one serving new layers. */
46
66
  export const FRONTEND_BUILD_COMMAND = "ajs dms build";
47
67
 
48
- const STATUSES: Record<StoreModuleState, StoreTileStatus> = {
49
- available: { tone: "neutral", labelKey: "available", dot: "none" },
50
- busy: { tone: "info", labelKey: "busy", dot: "live" },
51
- active: { tone: "success", labelKey: "active" },
52
- disabled: { tone: "neutral", labelKey: "disabled" },
53
- error: { tone: "error", labelKey: "error" },
54
- pending: { tone: "warning", labelKey: "pending" },
55
- };
56
-
57
68
  const ACTIONS: Record<StoreOperationKind, StoreAction> = {
58
69
  install: {
59
70
  kind: "install",
@@ -85,21 +96,85 @@ const INSTALLED_STATES = new Set<StoreModuleState>([
85
96
  "pending",
86
97
  ]);
87
98
 
99
+ /**
100
+ * The category a module is filed under, the same on its installed tile and
101
+ * its store tile: the one its registration names, else the store catalog's.
102
+ */
103
+ export function resolveCatalogCategory(
104
+ registered: Pick<ModuleCatalogEntry, "catalogCategory"> | undefined,
105
+ entry: Pick<StoreListingEntry, "category">,
106
+ ): string {
107
+ return (
108
+ registered?.catalogCategory?.trim() ||
109
+ `${CATEGORY_PREFIX}.${entry.category}`
110
+ );
111
+ }
112
+
88
113
  export function toModuleStoreEntry(
89
114
  entry: StoreListingEntry,
90
- registeredModules: readonly Pick<ModuleCatalogEntry, "id" | "title">[] = [],
115
+ registeredModules: readonly Pick<
116
+ ModuleCatalogEntry,
117
+ "id" | "title" | "catalogCategory"
118
+ >[] = [],
91
119
  ): ModuleStoreEntry {
120
+ const registered = registeredModules.find((module) => module.id === entry.id);
92
121
  return {
93
122
  ...entry,
94
123
  // The registration also names Installed and navigation. Keep the catalog
95
124
  // fallback for unloaded modules, whose translations may not be served.
96
- title:
97
- registeredModules.find((module) => module.id === entry.id)?.title ??
98
- entry.title,
99
- catalogCategory: `${CATEGORY_PREFIX}.${entry.category}`,
125
+ title: registered?.title ?? entry.title,
126
+ catalogCategory: resolveCatalogCategory(registered, entry),
100
127
  };
101
128
  }
102
129
 
130
+ /** A version as written, not a range: what an installed tile prints. */
131
+ const PLAIN_VERSION = /^\d+\.\d+\.\d+/;
132
+
133
+ /**
134
+ * Installed modules with the version, category and source their registration
135
+ * leaves out, read from the store listing of the same id. A module that registers
136
+ * neither still reads like the others on its tile and in the filters.
137
+ */
138
+ export function withStoreDetails<T extends ModuleCatalogEntry>(
139
+ modules: readonly T[],
140
+ store: readonly StoreListingEntry[],
141
+ ): T[] {
142
+ const byId = new Map(store.map((entry) => [entry.id, entry]));
143
+ return modules.map((module) => {
144
+ const entry = byId.get(module.id);
145
+ if (!entry) return module;
146
+ const installed = entry.installedVersion;
147
+ return {
148
+ ...module,
149
+ version:
150
+ module.version ??
151
+ (installed && PLAIN_VERSION.test(installed) ? installed : undefined),
152
+ catalogCategory: resolveCatalogCategory(module, entry),
153
+ source: entry.source,
154
+ };
155
+ });
156
+ }
157
+
158
+ /**
159
+ * Where a store tile's Open button leads: the landing page of the installed
160
+ * module of the same id, when the caller may open it and it has pages (a
161
+ * module without one lands on its own address, which has nothing to show).
162
+ */
163
+ export function storeOpenTarget(
164
+ entry: Pick<StoreListingEntry, "id">,
165
+ installedModules: readonly Pick<
166
+ ModuleCatalogEntry,
167
+ "id" | "hasAccess" | "landingSlug"
168
+ >[],
169
+ ): string | undefined {
170
+ const module = installedModules.find(
171
+ (candidate) => candidate.id === entry.id,
172
+ );
173
+ if (!module?.hasAccess || !module.landingSlug) return undefined;
174
+ const ownAddress = `${MODULE_URL_PREFIX}/${entry.id}`;
175
+ return module.landingSlug === ownAddress ? undefined : module.landingSlug;
176
+ }
177
+
103
178
  /** Whether the module is installed, from the store or by the config. */
104
179
  export function isInstalled(entry: StoreListingEntry): boolean {
105
180
  if (INSTALLED_STATES.has(entry.state)) return true;
@@ -115,29 +190,26 @@ export function filterByScope<T extends StoreListingEntry>(
115
190
  return entries.filter((entry) => isInstalled(entry) === wantsInstalled);
116
191
  }
117
192
 
118
- /** The store modules, alphabetical by their shown name. */
193
+ /** The store modules, alphabetical by their shown name in `locale`'s order. */
119
194
  export function sortStoreModules<T extends StoreListingEntry>(
120
195
  entries: readonly T[],
121
196
  resolve: DisplayResolver,
197
+ locale?: string,
122
198
  ): T[] {
199
+ const compare = compareDisplayNames(locale);
123
200
  return [...entries].sort((a, b) =>
124
- resolve(a.title).localeCompare(resolve(b.title)),
201
+ compare(resolve(a.title), resolve(b.title)),
125
202
  );
126
203
  }
127
204
 
128
- export function storeTileStatus(entry: StoreListingEntry): StoreTileStatus {
129
- if (entry.state === "active" && entry.updateAvailable) {
130
- return { tone: "warning", labelKey: "update_available" };
131
- }
132
- return STATUSES[entry.state];
133
- }
134
-
135
205
  /**
136
206
  * What the tile offers, the main action first. A module in error offers to
137
207
  * try its last step again: installing it when the file does not hold it,
138
208
  * applying the file otherwise.
139
209
  */
140
210
  export function storeActions(entry: StoreListingEntry): StoreAction[] {
211
+ // The developer's working copy is theirs to change in antelope.config.ts.
212
+ if (entry.isWorkingCopy) return [];
141
213
  const installed = entry.installedVersion !== undefined;
142
214
  const byState: Record<StoreModuleState, StoreOperationKind[]> = {
143
215
  available: ["install"],
@@ -166,6 +238,7 @@ export type StoreActionBlock =
166
238
  | "incompatible"
167
239
  | "frontend"
168
240
  | "interfaces"
241
+ | "used_by_project"
169
242
  | "dependents";
170
243
 
171
244
  /** Actions that load a new release of the module. */
@@ -197,10 +270,55 @@ export function storeActionBlock(
197
270
  kind: StoreOperationKind,
198
271
  ): StoreActionBlock | undefined {
199
272
  if (RELEASE_ACTIONS.has(kind)) return releaseBlock(entry, kind);
200
- if (TAKE_DOWN_ACTIONS.has(kind) && entry.dependents.length > 0) {
201
- return "dependents";
202
- }
203
- return undefined;
273
+ if (!TAKE_DOWN_ACTIONS.has(kind)) return undefined;
274
+ // The project cannot be removed first: only a change to its code frees it.
275
+ if (entry.usedByProject) return "used_by_project";
276
+ return entry.dependents.length > 0 ? "dependents" : undefined;
277
+ }
278
+
279
+ /** Translates an i18n key with its named parameters. */
280
+ export type StoreTranslate = (
281
+ key: string,
282
+ params?: Record<string, string>,
283
+ ) => string;
284
+
285
+ const LIST_SEPARATOR = ", ";
286
+
287
+ const BLOCK_MESSAGES: Record<
288
+ StoreActionBlock,
289
+ (entry: StoreListingEntry, t: StoreTranslate) => string
290
+ > = {
291
+ no_release: (_entry, t) => t("modules.store.no_release"),
292
+ incompatible: (entry, t) =>
293
+ t("modules.store.incompatible", {
294
+ range: entry.requiredInterfaceRange ?? "",
295
+ }),
296
+ frontend: (entry, t) =>
297
+ t("modules.store.frontend_requirement.needs", {
298
+ range: entry.frontendRequirement?.range ?? "",
299
+ running: entry.frontendRequirement?.running ?? "",
300
+ }),
301
+ interfaces: (entry, t) =>
302
+ t("modules.store.requires_first", {
303
+ interfaces: entry.missingInterfaces
304
+ .map((missing) => missing.interfacePackage)
305
+ .join(LIST_SEPARATOR),
306
+ }),
307
+ used_by_project: (_entry, t) => t("modules.store.used_by_project"),
308
+ dependents: (entry, t) =>
309
+ t("modules.store.used_by", {
310
+ modules: entry.dependents.join(LIST_SEPARATOR),
311
+ }),
312
+ };
313
+
314
+ /** Why `kind` cannot run on the module, worded for its tile, if it cannot. */
315
+ export function storeBlockMessage(
316
+ entry: StoreListingEntry,
317
+ kind: StoreOperationKind,
318
+ t: StoreTranslate,
319
+ ): string | undefined {
320
+ const block = storeActionBlock(entry, kind);
321
+ return block ? BLOCK_MESSAGES[block](entry, t) : undefined;
204
322
  }
205
323
 
206
324
  /** Whether `kind` first asks which modules implement the missing interfaces. */
@@ -210,3 +328,81 @@ export function needsDependencyChoice(
210
328
  ): boolean {
211
329
  return PICKS_DEPENDENCIES.has(kind) && entry.missingInterfaces.length > 0;
212
330
  }
331
+
332
+ /** What an installed module's pill says. */
333
+ export interface InstalledTileStatus {
334
+ tone: Tone;
335
+ /** Full i18n key of the label. */
336
+ labelKey: string;
337
+ dot?: StatusPillDot;
338
+ }
339
+
340
+ /** Store states an installed module's pill says before anything else. */
341
+ const STORE_STATE_PILLS: Partial<
342
+ Record<StoreModuleState, InstalledTileStatus>
343
+ > = {
344
+ busy: { tone: "info", labelKey: "modules.store.state.busy", dot: "live" },
345
+ error: { tone: "error", labelKey: "modules.store.state.error" },
346
+ disabled: { tone: "warning", labelKey: "modules.store.state.disabled" },
347
+ pending: { tone: "warning", labelKey: "modules.store.state.pending" },
348
+ };
349
+
350
+ const MODULE_STATUS_PILLS: Record<
351
+ ReturnType<typeof moduleStatus>,
352
+ InstalledTileStatus
353
+ > = {
354
+ live: { tone: "success", labelKey: "modules.status.live" },
355
+ beta: { tone: "secondary", labelKey: "modules.status.beta" },
356
+ update: { tone: "warning", labelKey: "modules.status.update" },
357
+ attention: { tone: "error", labelKey: "modules.status.attention" },
358
+ };
359
+
360
+ /**
361
+ * The pill of an installed module: what the store says of it first (an
362
+ * operation, an error, paused, not applied, a frontend to restart, an
363
+ * update), then what the module reports of itself, Active otherwise.
364
+ */
365
+ export function installedTileStatus(
366
+ module: ModuleCatalogEntry,
367
+ entry: StoreListingEntry | undefined,
368
+ ): InstalledTileStatus {
369
+ const storeStatus = entry && STORE_STATE_PILLS[entry.state];
370
+ if (storeStatus) return storeStatus;
371
+ if (entry && isAwaitingFrontend(entry)) {
372
+ return { tone: "warning", labelKey: "modules.status.restart_required" };
373
+ }
374
+ if (entry?.updateAvailable) {
375
+ return { tone: "warning", labelKey: "modules.status.update" };
376
+ }
377
+ return MODULE_STATUS_PILLS[moduleStatus(module)];
378
+ }
379
+
380
+ /**
381
+ * Installed store modules that register no module of their own, as the
382
+ * Installed grid lists them: a paused module, one that did not start, or one
383
+ * without pages. antelope.config.ts still declares them, so they stay listed
384
+ * and managed there, with what the store knows of them.
385
+ */
386
+ export function storeOnlyModules(
387
+ store: readonly ModuleStoreEntry[],
388
+ registered: readonly Pick<ModuleCatalogEntry, "id">[],
389
+ ): ModuleCatalogEntry[] {
390
+ const registeredIds = new Set(registered.map((module) => module.id));
391
+ return store
392
+ .filter((entry) => isInstalled(entry) && !registeredIds.has(entry.id))
393
+ .map((entry) => ({
394
+ id: entry.id,
395
+ title: entry.title,
396
+ description: entry.description,
397
+ icon: entry.icon,
398
+ hasAccess: true,
399
+ // Nothing to open: the module serves no page.
400
+ landingSlug: "",
401
+ catalogCategory: entry.catalogCategory,
402
+ version:
403
+ entry.installedVersion && PLAIN_VERSION.test(entry.installedVersion)
404
+ ? entry.installedVersion
405
+ : undefined,
406
+ source: entry.source,
407
+ }));
408
+ }