@antelopejs/dms 0.8.3 → 0.9.1

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 (424) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/data/module-store/catalog.json +71 -0
  3. package/dist/config.d.ts +7 -0
  4. package/dist/config.js +8 -0
  5. package/dist/config.js.map +1 -1
  6. package/dist/implementations/dms/frontend-backend-proxies.d.ts +3 -0
  7. package/dist/implementations/dms/frontend-backend-proxies.js +39 -0
  8. package/dist/implementations/dms/frontend-backend-proxies.js.map +1 -0
  9. package/dist/implementations/dms/frontend-bootstrap.d.ts +1 -0
  10. package/dist/implementations/dms/frontend-bootstrap.js +13 -0
  11. package/dist/implementations/dms/frontend-bootstrap.js.map +1 -1
  12. package/dist/implementations/dms/frontend-modules.d.ts +6 -2
  13. package/dist/implementations/dms/frontend-modules.js +37 -8
  14. package/dist/implementations/dms/frontend-modules.js.map +1 -1
  15. package/dist/implementations/dms/frontend-ready.d.ts +8 -0
  16. package/dist/implementations/dms/frontend-ready.js +34 -0
  17. package/dist/implementations/dms/frontend-ready.js.map +1 -0
  18. package/dist/implementations/dms/manifest.d.ts +7 -0
  19. package/dist/implementations/dms/manifest.js +13 -0
  20. package/dist/implementations/dms/manifest.js.map +1 -1
  21. package/dist/implementations/dms/menu-categories.d.ts +16 -0
  22. package/dist/implementations/dms/menu-categories.js +30 -0
  23. package/dist/implementations/dms/menu-categories.js.map +1 -0
  24. package/dist/implementations/dms/nav-badges.d.ts +0 -8
  25. package/dist/implementations/dms/nav-badges.js +0 -16
  26. package/dist/implementations/dms/nav-badges.js.map +1 -1
  27. package/dist/implementations/dms/page.d.ts +13 -5
  28. package/dist/implementations/dms/page.js +72 -41
  29. package/dist/implementations/dms/page.js.map +1 -1
  30. package/dist/implementations/dms-html-render/index.js +5 -2
  31. package/dist/implementations/dms-html-render/index.js.map +1 -1
  32. package/dist/implementations/dms-notifications/index.d.ts +4 -3
  33. package/dist/implementations/dms-notifications/index.js +14 -6
  34. package/dist/implementations/dms-notifications/index.js.map +1 -1
  35. package/dist/pages/settings/settings-index.js +3 -2
  36. package/dist/pages/settings/settings-index.js.map +1 -1
  37. package/dist/pages/settings/users/invites.js +2 -2
  38. package/dist/pages/settings/users/invites.js.map +1 -1
  39. package/dist/pages/settings/users/member-change-notifications.js +17 -6
  40. package/dist/pages/settings/users/member-change-notifications.js.map +1 -1
  41. package/dist/pages/settings/users/member-invite-form.d.ts +3 -2
  42. package/dist/pages/settings/users/member-invite-form.js +7 -4
  43. package/dist/pages/settings/users/member-invite-form.js.map +1 -1
  44. package/dist/pages/settings/users/member-roles-field.js +2 -1
  45. package/dist/pages/settings/users/member-roles-field.js.map +1 -1
  46. package/dist/pages/settings/users/members.js +4 -1
  47. package/dist/pages/settings/users/members.js.map +1 -1
  48. package/dist/pages/settings/users/notifications.d.ts +6 -0
  49. package/dist/pages/settings/users/notifications.js +23 -15
  50. package/dist/pages/settings/users/notifications.js.map +1 -1
  51. package/dist/pages/settings/users/profile.js +1 -1
  52. package/dist/pages/settings/users/profile.js.map +1 -1
  53. package/dist/pages/settings/users/region-form.d.ts +1 -1
  54. package/dist/pages/settings/users/region-form.js +2 -1
  55. package/dist/pages/settings/users/region-form.js.map +1 -1
  56. package/dist/pages/settings/users/role-editor-store.d.ts +3 -1
  57. package/dist/pages/settings/users/role-editor-store.js +17 -4
  58. package/dist/pages/settings/users/role-editor-store.js.map +1 -1
  59. package/dist/pages/settings/users/roles.js +1 -0
  60. package/dist/pages/settings/users/roles.js.map +1 -1
  61. package/dist/realtime/registry.d.ts +1 -0
  62. package/dist/realtime/registry.js +5 -0
  63. package/dist/realtime/registry.js.map +1 -1
  64. package/dist/routes/index.d.ts +1 -0
  65. package/dist/routes/index.js +1 -0
  66. package/dist/routes/index.js.map +1 -1
  67. package/dist/routes/realtime.d.ts +5 -1
  68. package/dist/routes/realtime.js +16 -9
  69. package/dist/routes/realtime.js.map +1 -1
  70. package/dist/routes/store.d.ts +21 -0
  71. package/dist/routes/store.js +130 -0
  72. package/dist/routes/store.js.map +1 -0
  73. package/dist/store/activity.d.ts +2 -0
  74. package/dist/store/activity.js +12 -0
  75. package/dist/store/activity.js.map +1 -0
  76. package/dist/store/cache.d.ts +8 -0
  77. package/dist/store/cache.js +29 -0
  78. package/dist/store/cache.js.map +1 -0
  79. package/dist/store/catalog.d.ts +4 -0
  80. package/dist/store/catalog.js +69 -0
  81. package/dist/store/catalog.js.map +1 -0
  82. package/dist/store/compatibility.d.ts +3 -0
  83. package/dist/store/compatibility.js +45 -0
  84. package/dist/store/compatibility.js.map +1 -0
  85. package/dist/store/declarations.d.ts +18 -0
  86. package/dist/store/declarations.js +116 -0
  87. package/dist/store/declarations.js.map +1 -0
  88. package/dist/store/frontend-compatibility.d.ts +18 -0
  89. package/dist/store/frontend-compatibility.js +93 -0
  90. package/dist/store/frontend-compatibility.js.map +1 -0
  91. package/dist/store/interfaces.d.ts +6 -0
  92. package/dist/store/interfaces.js +126 -0
  93. package/dist/store/interfaces.js.map +1 -0
  94. package/dist/store/listing.d.ts +2 -0
  95. package/dist/store/listing.js +180 -0
  96. package/dist/store/listing.js.map +1 -0
  97. package/dist/store/operations.d.ts +8 -0
  98. package/dist/store/operations.js +254 -0
  99. package/dist/store/operations.js.map +1 -0
  100. package/dist/store/registry.d.ts +4 -0
  101. package/dist/store/registry.js +61 -0
  102. package/dist/store/registry.js.map +1 -0
  103. package/dist/store/requirements.d.ts +14 -0
  104. package/dist/store/requirements.js +81 -0
  105. package/dist/store/requirements.js.map +1 -0
  106. package/dist/store/runtime.d.ts +11 -0
  107. package/dist/store/runtime.js +78 -0
  108. package/dist/store/runtime.js.map +1 -0
  109. package/dist/store/tarball.d.ts +2 -0
  110. package/dist/store/tarball.js +80 -0
  111. package/dist/store/tarball.js.map +1 -0
  112. package/dist/store/types.d.ts +98 -0
  113. package/dist/store/types.js +3 -0
  114. package/dist/store/types.js.map +1 -0
  115. package/dist/test/integration/account-deletion.test.js +2 -2
  116. package/dist/test/integration/account-deletion.test.js.map +1 -1
  117. package/dist/test/integration/auth/auth-key-rotation.test.js +2 -1
  118. package/dist/test/integration/auth/auth-key-rotation.test.js.map +1 -1
  119. package/dist/test/integration/auth/current-password.test.js +2 -1
  120. package/dist/test/integration/auth/current-password.test.js.map +1 -1
  121. package/dist/test/integration/auth/session-revocation.test.js +2 -1
  122. package/dist/test/integration/auth/session-revocation.test.js.map +1 -1
  123. package/dist/test/integration/auth/sign-in-country.test.js +2 -1
  124. package/dist/test/integration/auth/sign-in-country.test.js.map +1 -1
  125. package/dist/test/integration/auth/two-factor-codes.test.js +2 -1
  126. package/dist/test/integration/auth/two-factor-codes.test.js.map +1 -1
  127. package/dist/test/integration/auth/two-factor-password.test.js +3 -2
  128. package/dist/test/integration/auth/two-factor-password.test.js.map +1 -1
  129. package/dist/test/integration/email-change.test.js +2 -1
  130. package/dist/test/integration/email-change.test.js.map +1 -1
  131. package/dist/test/integration/files/component-files.test.js +2 -2
  132. package/dist/test/integration/files/component-files.test.js.map +1 -1
  133. package/dist/test/integration/member-settings-access.test.js +8 -7
  134. package/dist/test/integration/member-settings-access.test.js.map +1 -1
  135. package/dist/test/integration/menu-access-events.test.d.ts +1 -0
  136. package/dist/test/integration/menu-access-events.test.js +148 -0
  137. package/dist/test/integration/menu-access-events.test.js.map +1 -0
  138. package/dist/test/integration/notification-inbox.test.js +4 -1
  139. package/dist/test/integration/notification-inbox.test.js.map +1 -1
  140. package/dist/test/integration/notification-locked-subjects.test.js +3 -2
  141. package/dist/test/integration/notification-locked-subjects.test.js.map +1 -1
  142. package/dist/test/integration/notification-unread-badge.test.js +51 -5
  143. package/dist/test/integration/notification-unread-badge.test.js.map +1 -1
  144. package/dist/test/integration/page-layout-route-guard.test.d.ts +1 -0
  145. package/dist/test/integration/page-layout-route-guard.test.js +56 -0
  146. package/dist/test/integration/page-layout-route-guard.test.js.map +1 -0
  147. package/dist/test/integration/permission-warnings.test.js +3 -2
  148. package/dist/test/integration/permission-warnings.test.js.map +1 -1
  149. package/dist/test/integration/removed-member-notifications.test.js +3 -2
  150. package/dist/test/integration/removed-member-notifications.test.js.map +1 -1
  151. package/dist/test/integration/role-delete-reassign.test.js +2 -1
  152. package/dist/test/integration/role-delete-reassign.test.js.map +1 -1
  153. package/dist/test/integration/role-permission-ancestors.test.js +2 -1
  154. package/dist/test/integration/role-permission-ancestors.test.js.map +1 -1
  155. package/dist/test/unit/frontend-bootstrap.test.js +13 -0
  156. package/dist/test/unit/frontend-bootstrap.test.js.map +1 -1
  157. package/dist/test/unit/implementations/dms/category-slug-registry.test.js +14 -5
  158. package/dist/test/unit/implementations/dms/category-slug-registry.test.js.map +1 -1
  159. package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.d.ts +1 -0
  160. package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js +66 -0
  161. package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js.map +1 -0
  162. package/dist/test/unit/implementations/dms/manifest-strip.test.js +26 -0
  163. package/dist/test/unit/implementations/dms/manifest-strip.test.js.map +1 -1
  164. package/dist/test/unit/implementations/dms/nav-badges.test.js +23 -9
  165. package/dist/test/unit/implementations/dms/nav-badges.test.js.map +1 -1
  166. package/dist/test/unit/implementations/dms-html-render/generate-html.test.js +22 -4
  167. package/dist/test/unit/implementations/dms-html-render/generate-html.test.js.map +1 -1
  168. package/dist/test/unit/interfaces/dms/page-layout-query.test.js +12 -0
  169. package/dist/test/unit/interfaces/dms/page-layout-query.test.js.map +1 -1
  170. package/dist/test/unit/interfaces/dms/page-teardown.test.js +1 -1
  171. package/dist/test/unit/interfaces/dms/page-teardown.test.js.map +1 -1
  172. package/dist/test/unit/pages/settings/member-roles-field.test.js +1 -1
  173. package/dist/test/unit/pages/settings/member-roles-field.test.js.map +1 -1
  174. package/dist/test/unit/pages/settings/role-delete-confirm.test.js.map +1 -1
  175. package/dist/test/unit/realtime/menu-topics.test.d.ts +1 -0
  176. package/dist/test/unit/realtime/menu-topics.test.js +26 -0
  177. package/dist/test/unit/realtime/menu-topics.test.js.map +1 -0
  178. package/dist/test/unit/store/store.test.d.ts +1 -0
  179. package/dist/test/unit/store/store.test.js +127 -0
  180. package/dist/test/unit/store/store.test.js.map +1 -0
  181. package/dist/validation/store.schema.d.ts +39 -0
  182. package/dist/validation/store.schema.js +27 -0
  183. package/dist/validation/store.schema.js.map +1 -0
  184. package/frontend-vue/deferred-plugins.ts +3 -4
  185. package/frontend-vue/dms.email.ts +5 -1
  186. package/frontend-vue/dms.frontend.ts +15 -5
  187. package/frontend-vue/dms.server.ts +74 -0
  188. package/frontend-vue/layers/dms-auth/app/composables/useLogout.ts +1 -1
  189. package/frontend-vue/layers/dms-auth/app/composables/usePostLoginRedirect.ts +16 -3
  190. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/2fa.vue +3 -1
  191. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/backup.vue +3 -1
  192. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/forgot-validation.vue +1 -1
  193. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/oauth-complete.vue +26 -3
  194. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/recover.vue +1 -1
  195. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/signup.vue +22 -25
  196. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/validate.vue +1 -1
  197. package/frontend-vue/layers/dms-auth/app/middleware/auth.ts +5 -7
  198. package/frontend-vue/layers/dms-auth/app/plugins/permissions-preload.ts +17 -0
  199. package/frontend-vue/layers/dms-auth/server/accounts.ts +135 -0
  200. package/frontend-vue/layers/dms-auth/server/actions.ts +251 -0
  201. package/frontend-vue/layers/dms-auth/server/oauth.ts +260 -0
  202. package/frontend-vue/layers/dms-auth/server/session.ts +158 -0
  203. package/frontend-vue/layers/dms-core/app/build/utils/asyncDataScope.ts +47 -0
  204. package/frontend-vue/layers/dms-core/app/build/utils/permission-preview.ts +6 -2
  205. package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +148 -0
  206. package/frontend-vue/layers/dms-core/app/composables/auth/useAuthFetch.ts +7 -20
  207. package/frontend-vue/layers/dms-core/app/composables/auth/useCurrentUser.ts +2 -2
  208. package/frontend-vue/layers/dms-core/app/composables/auth/useMultiAccount.ts +18 -5
  209. package/frontend-vue/layers/dms-core/app/composables/auth/usePermissions.ts +35 -6
  210. package/frontend-vue/layers/dms-core/app/composables/auth/useSessionRecovery.ts +1 -1
  211. package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +20 -14
  212. package/frontend-vue/layers/dms-core/app/composables/period/periodClock.ts +121 -0
  213. package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +42 -0
  214. package/frontend-vue/layers/dms-core/app/composables/period/periodTimeZone.ts +31 -0
  215. package/frontend-vue/layers/dms-core/app/composables/period/presetResolvers.ts +48 -19
  216. package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +33 -4
  217. package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +78 -25
  218. package/frontend-vue/layers/dms-core/app/composables/realtime/usePageRealtime.ts +3 -0
  219. package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +36 -6
  220. package/frontend-vue/layers/dms-core/app/composables/user/useUserRegionalPreferences.ts +1 -1
  221. package/frontend-vue/layers/dms-core/app/plugins/async-data-scope.ts +26 -0
  222. package/frontend-vue/layers/dms-core/app/utils/pageRoutes.ts +22 -0
  223. package/frontend-vue/layers/dms-core/app/utils/regional.ts +36 -3
  224. package/frontend-vue/layers/dms-core/app/utils/trailingDeduper.ts +10 -1
  225. package/frontend-vue/layers/dms-core/shared/types/app-config.ts +48 -4
  226. package/frontend-vue/layers/dms-layout/app/app.config.ts +42 -23
  227. package/frontend-vue/layers/dms-layout/app/assets/css/main.css +72 -20
  228. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardFrame.vue +9 -0
  229. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +0 -3
  230. package/frontend-vue/layers/dms-layout/app/build/components/notification/NotificationPopover.vue +9 -3
  231. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreDependencyPicker.vue +136 -0
  232. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +98 -0
  233. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +247 -29
  234. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +19 -2
  235. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/members/MemberRolePicker.vue +13 -12
  236. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +17 -7
  237. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationPreferencesForm.vue +0 -1
  238. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfilePersonalInfo.vue +36 -33
  239. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfileSecuritySummary.vue +1 -2
  240. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-types.ts +5 -4
  241. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecuritySessions.vue +35 -28
  242. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityStatus.vue +0 -1
  243. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityTwoFactor.vue +1 -1
  244. package/frontend-vue/layers/dms-layout/app/build/composables/dev-reload/useDevReloadHolder.ts +7 -6
  245. package/frontend-vue/layers/dms-layout/app/build/composables/layout/sidePanelState.ts +1 -1
  246. package/frontend-vue/layers/dms-layout/app/build/composables/navigation/useVisitNavBadges.ts +26 -0
  247. package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationCatalog.ts +25 -23
  248. package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationPreferences.ts +44 -23
  249. package/frontend-vue/layers/dms-layout/app/build/composables/security/useSessionHandoff.ts +1 -1
  250. package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/preferencesSummary.ts +1 -1
  251. package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useAccountData.ts +3 -2
  252. package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfileAvatar.ts +1 -18
  253. package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfilePreferences.ts +19 -23
  254. package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityFormat.ts +6 -3
  255. package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityOverview.ts +13 -18
  256. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +186 -120
  257. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-install.ts +146 -0
  258. package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +8 -6
  259. package/frontend-vue/layers/dms-layout/app/build/utils/publicErrorMessage.ts +19 -0
  260. package/frontend-vue/layers/dms-layout/app/components/navigation/CollapsedMenuItem.vue +0 -11
  261. package/frontend-vue/layers/dms-layout/app/components/navigation/NavigationMenu.vue +3 -13
  262. package/frontend-vue/layers/dms-layout/app/composables/general/useSystemState.ts +7 -11
  263. package/frontend-vue/layers/dms-layout/app/composables/notification/useNotifications.ts +103 -22
  264. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +109 -0
  265. package/frontend-vue/layers/dms-layout/app/composables/page/useModulesListing.ts +25 -102
  266. package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +0 -10
  267. package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +177 -96
  268. package/frontend-vue/layers/dms-layout/app/composables/profile/useUserLanguage.ts +3 -2
  269. package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +341 -62
  270. package/frontend-vue/layers/dms-layout/app/emails/components/EmailLayout.vue +31 -11
  271. package/frontend-vue/layers/dms-layout/app/error.vue +5 -2
  272. package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +1 -1
  273. package/frontend-vue/layers/dms-layout/app/middleware/page-leave-guard.global.ts +2 -2
  274. package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +12 -9
  275. package/frontend-vue/layers/dms-layout/app/plugins/dms-menu-sync.client.ts +31 -3
  276. package/frontend-vue/layers/dms-layout/app/plugins/favicon.ts +58 -0
  277. package/frontend-vue/layers/dms-layout/app/plugins/notification-stream.client.ts +15 -7
  278. package/frontend-vue/layers/dms-layout/app/plugins/profile-sync.client.ts +5 -1
  279. package/frontend-vue/layers/dms-layout/app/plugins/regional-preferences.ts +10 -4
  280. package/frontend-vue/layers/dms-layout/app/plugins/time-zone-cookie.client.ts +12 -0
  281. package/frontend-vue/layers/dms-layout/app/types/module-store.ts +134 -0
  282. package/frontend-vue/layers/dms-layout/app/types/page.ts +13 -0
  283. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +107 -9
  284. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +107 -9
  285. package/frontend-vue/layers/dms-layout/server/html-render.ts +58 -0
  286. package/frontend-vue/layers/dms-layout/server/render-token.ts +95 -0
  287. package/frontend-vue/layers/dms-onboarding/app/composables/onboarding/useOnboarding.ts +25 -21
  288. package/frontend-vue/layers/dms-onboarding/app/middleware/onboarding.global.ts +1 -1
  289. package/frontend-vue/layers/dms-onboarding/app/plugins/onboarding-preload.ts +9 -0
  290. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +4 -1
  291. package/frontend-vue/layers/dms-ui/app/build/composables/files/useFileMetadata.ts +162 -0
  292. package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +23 -4
  293. package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageDataRefresh.ts +101 -0
  294. package/frontend-vue/layers/dms-ui/app/build/composables/navigation/useNavBadges.ts +33 -6
  295. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +19 -16
  296. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodQuery.ts +40 -20
  297. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +6 -8
  298. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableListData.ts +42 -14
  299. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +19 -8
  300. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +1 -1
  301. package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +5 -2
  302. package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +9 -9
  303. package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +20 -18
  304. package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +2 -1
  305. package/frontend-vue/layers/dms-ui/app/components/form/components/File.vue +26 -132
  306. package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +40 -105
  307. package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +2 -0
  308. package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +4 -4
  309. package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +9 -13
  310. package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +2 -1
  311. package/frontend-vue/layers/dms-ui/app/components/table-view/FilePreview.vue +12 -24
  312. package/frontend-vue/layers/dms-ui/app/components/table-view/ImagePreview.vue +17 -29
  313. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +7 -3
  314. package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +20 -8
  315. package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +8 -11
  316. package/frontend-vue/layers/dms-ui/app/composables/chart/useChartFetch.ts +146 -182
  317. package/frontend-vue/layers/dms-ui/app/composables/live/usePageLiveRefresh.ts +8 -2
  318. package/frontend-vue/layers/dms-ui/app/composables/useFileReadUrls.ts +11 -99
  319. package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +32 -3
  320. package/frontend-vue/package.json +3 -3
  321. package/frontend-vue/pnpm-lock.yaml +16 -22
  322. package/frontend-vue/server/private-options.ts +27 -0
  323. package/frontend-vue/tests/async-data-scope.test.ts +121 -0
  324. package/frontend-vue/tests/auth-middleware-error.test.ts +3 -3
  325. package/frontend-vue/tests/banner-block-fetch.test.ts +3 -0
  326. package/frontend-vue/tests/block-fetch-options-forwarding.test.ts +3 -0
  327. package/frontend-vue/tests/block-items-period-realtime.test.ts +3 -0
  328. package/frontend-vue/tests/block-not-found.test.ts +16 -2
  329. package/frontend-vue/tests/block-page-refresh.test.ts +3 -0
  330. package/frontend-vue/tests/block-route-params.test.ts +3 -0
  331. package/frontend-vue/tests/branding-defaults.test.ts +34 -0
  332. package/frontend-vue/tests/builtin-emails.test.ts +23 -2
  333. package/frontend-vue/tests/chart-live-pause.test.ts +4 -1
  334. package/frontend-vue/tests/chart-value-propagation.test.ts +4 -1
  335. package/frontend-vue/tests/composed-text-blocks.test.ts +3 -0
  336. package/frontend-vue/tests/email-layout.test.ts +66 -4
  337. package/frontend-vue/tests/error-page-message.test.ts +25 -2
  338. package/frontend-vue/tests/favicon.test.ts +131 -0
  339. package/frontend-vue/tests/file-fields-ssr.test.ts +145 -0
  340. package/frontend-vue/tests/file-read-urls.test.ts +145 -34
  341. package/frontend-vue/tests/file-unavailable.test.ts +5 -1
  342. package/frontend-vue/tests/fixtures/dataEngine.ts +44 -0
  343. package/frontend-vue/tests/form-load-error.test.ts +55 -1
  344. package/frontend-vue/tests/helpers/api-data-runtime.ts +77 -0
  345. package/frontend-vue/tests/image-hydration-ids.test.ts +21 -16
  346. package/frontend-vue/tests/image-reference-preservation.test.ts +59 -53
  347. package/frontend-vue/tests/input-number-adjust.test.ts +14 -0
  348. package/frontend-vue/tests/key-value-list-copy.test.ts +3 -0
  349. package/frontend-vue/tests/member-role-picker.test.ts +4 -2
  350. package/frontend-vue/tests/menu-sync-reconnect.test.ts +47 -0
  351. package/frontend-vue/tests/metadata-retry.test.ts +5 -1
  352. package/frontend-vue/tests/module-routing.test.ts +2 -5
  353. package/frontend-vue/tests/module-store-catalog.test.ts +270 -51
  354. package/frontend-vue/tests/modules-catalog.test.ts +17 -4
  355. package/frontend-vue/tests/modules-listing-transfer.test.ts +95 -0
  356. package/frontend-vue/tests/multi-account-validate.test.ts +23 -1
  357. package/frontend-vue/tests/nav-badge-tones.test.ts +66 -3
  358. package/frontend-vue/tests/notification-counts-from-writes.test.ts +223 -0
  359. package/frontend-vue/tests/notification-counts-transfer.test.ts +88 -0
  360. package/frontend-vue/tests/notification-inbox-display.test.ts +57 -5
  361. package/frontend-vue/tests/notification-popover-errors.test.ts +21 -1
  362. package/frontend-vue/tests/oauth-complete-handoff.test.ts +209 -0
  363. package/frontend-vue/tests/onboarding-middleware.test.ts +1 -1
  364. package/frontend-vue/tests/onboarding-transfer.test.ts +106 -0
  365. package/frontend-vue/tests/page-data-refresh.test.ts +141 -0
  366. package/frontend-vue/tests/page-fill-height.test.ts +16 -3
  367. package/frontend-vue/tests/page-no-visible-blocks.test.ts +12 -2
  368. package/frontend-vue/tests/page-realtime-remount.test.ts +1 -1
  369. package/frontend-vue/tests/page-record-label.test.ts +0 -7
  370. package/frontend-vue/tests/page-validation-redirect.test.ts +12 -2
  371. package/frontend-vue/tests/period-hydration-now.test.ts +185 -0
  372. package/frontend-vue/tests/period-live-pause.test.ts +3 -0
  373. package/frontend-vue/tests/period-reader-zone.test.ts +137 -0
  374. package/frontend-vue/tests/period-scope-first-fetch.test.ts +17 -1
  375. package/frontend-vue/tests/period-ssr.test.ts +195 -0
  376. package/frontend-vue/tests/permission-preview-partial.test.ts +0 -2
  377. package/frontend-vue/tests/permissions-transfer.test.ts +194 -0
  378. package/frontend-vue/tests/post-login-redirect.test.ts +10 -5
  379. package/frontend-vue/tests/profile-personal-info-ssr.test.ts +150 -0
  380. package/frontend-vue/tests/profile-preferences-summary.test.ts +15 -11
  381. package/frontend-vue/tests/realtime-reconnect.test.ts +22 -3
  382. package/frontend-vue/tests/roles-name-errors.test.ts +1 -1
  383. package/frontend-vue/tests/row-side-panel.test.ts +1 -1
  384. package/frontend-vue/tests/security-sessions-load-error.test.ts +3 -1
  385. package/frontend-vue/tests/server-routes-vs-pages.test.ts +26 -0
  386. package/frontend-vue/tests/server-routes.test.ts +582 -0
  387. package/frontend-vue/tests/session-handoff.test.ts +1 -1
  388. package/frontend-vue/tests/settings-reads-ssr.test.ts +232 -0
  389. package/frontend-vue/tests/side-panels.test.ts +8 -1
  390. package/frontend-vue/tests/sign-in-exit-history.test.ts +219 -0
  391. package/frontend-vue/tests/signup-invitation-link.test.ts +56 -8
  392. package/frontend-vue/tests/site-layout-categories.test.ts +42 -0
  393. package/frontend-vue/tests/site-layout-hydration.test.ts +3 -3
  394. package/frontend-vue/tests/site-layout-rendered.test.ts +103 -0
  395. package/frontend-vue/tests/site-layout-resync.test.ts +87 -0
  396. package/frontend-vue/tests/ssr-guard-preload.test.ts +188 -0
  397. package/frontend-vue/tests/table-cell-tones.test.ts +36 -1
  398. package/frontend-vue/tests/table-data-ssr.test.ts +195 -0
  399. package/frontend-vue/tests/table-list-data-requests.test.ts +154 -0
  400. package/frontend-vue/tests/table-retry-loading.test.ts +37 -0
  401. package/frontend-vue/tests/table-source-navigation.test.ts +0 -1
  402. package/frontend-vue/tests/table-source-period-scope.test.ts +0 -1
  403. package/frontend-vue/tests/table-view-views.test.ts +1 -1
  404. package/frontend-vue/tests/top-list-card-actions.test.ts +3 -0
  405. package/frontend-vue/tests/top-list-card-period-badge-ssr.test.ts +212 -0
  406. package/frontend-vue/tests/top-list-card-period-badge.test.ts +79 -0
  407. package/frontend-vue/tests/top-list-card-value-suffix.test.ts +3 -0
  408. package/frontend-vue/tests/trailing-deduper.test.ts +22 -0
  409. package/frontend-vue/tests/use-api-data-update.test.ts +119 -0
  410. package/frontend-vue/tests/use-api-data.test.ts +410 -0
  411. package/frontend-vue/tests/user-language.test.ts +8 -5
  412. package/frontend-vue/tests/widget-label-i18n.test.ts +4 -1
  413. package/frontend-vue/vitest.config.ts +6 -0
  414. package/package.json +2 -2
  415. package/skills/dms/SKILL.md +2 -1
  416. package/skills/dms-module/SKILL.md +1 -1
  417. package/skills/dms-pages/SKILL.md +4 -1
  418. package/frontend-vue/layers/dms-core/app/build/composables/page/usePageRecordLabel.ts +0 -9
  419. package/frontend-vue/layers/dms-layout/app/composables/page/usePrefetch.ts +0 -21
  420. package/frontend-vue/layers/dms-ui/app/build/composables/form/useControlError.ts +0 -10
  421. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useServerRenderedAsyncData.ts +0 -23
  422. package/frontend-vue/tests/control-error-path.test.ts +0 -11
  423. package/frontend-vue/tests/modules-listing-per-request.test.ts +0 -104
  424. package/frontend-vue/tests/page-layout-background-refresh.test.ts +0 -321
@@ -1,4 +1,5 @@
1
1
  <script setup lang="ts">
2
+ import type { Ref, VNode } from "vue";
2
3
  import { MONO_CHIP_CLASS } from "#dms-ui/app/build/utils/monoChip";
3
4
  import UButton from "@nuxt/ui/components/Button.vue";
4
5
  import UIcon from "@nuxt/ui/runtime/vue/components/Icon.vue";
@@ -13,6 +14,7 @@ import { useKeyboardPlatform } from "#dms-ui/app/composables/global/keyboardPlat
13
14
  import DmsChipGroup from "#dms-ui/app/build/components/form/ChipGroup.vue";
14
15
  import ModuleCatalogTile from "../build/components/pages/modules/ModuleCatalogTile.vue";
15
16
  import ModuleStoreTile from "../build/components/pages/modules/ModuleStoreTile.vue";
17
+ import ModuleStoreDependencyPicker from "../build/components/pages/modules/ModuleStoreDependencyPicker.vue";
16
18
  import { usePageHeaderActions } from "../composables/layout/usePageHeaderActions";
17
19
  import {
18
20
  ENDPOINT_MODULES_LISTING,
@@ -30,13 +32,33 @@ import {
30
32
  type ModuleSortMode,
31
33
  } from "../build/utils/modules-catalog";
32
34
  import {
33
- MODULE_STORE_CATALOG,
34
- availableStoreModules,
35
+ type ModuleStoreEntry,
36
+ type StoreScope,
37
+ FRONTEND_BUILD_COMMAND,
38
+ filterByScope,
39
+ isAwaitingFrontend,
40
+ needsDependencyChoice,
41
+ sortStoreModules,
42
+ toModuleStoreEntry,
35
43
  } 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
+ 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";
36
59
  import DmsSearchInput from "#dms-ui/app/build/components/form/SearchInput.vue";
37
60
 
38
61
  const SKELETON_PLACEHOLDER_COUNT = 6;
39
- const MODULES_LISTING_DATA_KEY = "modules-page-listing";
40
62
 
41
63
  const { t } = useI18n();
42
64
  const { processI18n } = useTranslation();
@@ -85,37 +107,16 @@ if (!siteLayout.siteLayout.value) {
85
107
  // 403 page, the same answer as any page they may not open (the entry is not in
86
108
  // their menu either).
87
109
  if (!isOwner.value) {
88
- throw showError({
110
+ throw showDmsError({
89
111
  statusCode: HTTP_FORBIDDEN,
90
112
  statusMessage: "Access denied",
91
113
  });
92
114
  }
93
115
 
94
- // Whatever is cached from an earlier visit shows at once; stale live figures
95
- // are then refreshed in the background on mount. The hydration reuses the
96
- // listing the server rendered: fetched again before hydrating, its live
97
- // readouts (an uptime a minute on) would no longer match the markup.
98
- if (isOwner.value) {
99
- const { data: renderedListing } = await useDmsAsyncData<
100
- ModuleCatalogEntry[] | null
101
- >(MODULES_LISTING_DATA_KEY, async () => {
102
- await modulesListing.loadModulesListing();
103
- return modulesListing.modules.value ?? null;
104
- });
105
- if (modulesListing.modules.value === undefined) {
106
- if (renderedListing.value) {
107
- modulesListing.modules.value = renderedListing.value;
108
- } else {
109
- await modulesListing.loadModulesListing();
110
- }
111
- }
112
- }
113
-
114
- onMounted(() => {
115
- if (isOwner.value && modulesListing.modules.value !== undefined) {
116
- void modulesListing.revalidate();
117
- }
118
- });
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();
119
120
 
120
121
  const isForbidden = computed(
121
122
  () =>
@@ -204,7 +205,7 @@ const trimmedQuery = computed(() => query.value.trim());
204
205
  const pageMatch = computed(() => {
205
206
  if (visibleModules.value.length > 0 || !trimmedQuery.value) return null;
206
207
  const match = findModulePageMatch(
207
- siteLayout.siteLayout.value?.pages,
208
+ siteLayout.siteLayout.value,
208
209
  trimmedQuery.value,
209
210
  new Set(modules.value.map((entry) => entry.id)),
210
211
  processI18n,
@@ -219,26 +220,49 @@ function clearFilters(): void {
219
220
  category.value = MODULE_CATEGORY_ALL;
220
221
  }
221
222
 
222
- // ── Module store (preview) ─────────────────────────────────────────
223
- // A static list of the official modules not installed here: nothing installs
224
- // yet, only the search and the category chips work, locally.
223
+ // ── Module store ───────────────────────────────────────────────────
224
+ const moduleStore = useModuleStore();
225
+ const toast = useToast();
226
+ const { confirm } = useConfirm();
227
+
228
+ onMounted(() => {
229
+ if (isOwner.value) void moduleStore.load();
230
+ });
231
+ onBeforeUnmount(() => moduleStore.stop());
232
+
233
+ const storeListing = computed(() => moduleStore.listing.value);
234
+ const isStoreLoading = computed(
235
+ () => storeListing.value === undefined && !moduleStore.loadingError.value,
236
+ );
225
237
  const storeModules = computed(() =>
226
- availableStoreModules(
227
- MODULE_STORE_CATALOG,
228
- new Set((modulesListing.modules.value ?? []).map((entry) => entry.id)),
238
+ sortStoreModules(
239
+ (storeListing.value?.modules ?? []).map(toModuleStoreEntry),
229
240
  processI18n,
230
241
  ),
231
242
  );
243
+ const storeScope = ref<StoreScope>("all");
232
244
  const storeQuery = ref("");
233
245
  const storeCategory = ref<string>(MODULE_CATEGORY_ALL);
234
246
 
247
+ const storeScopeItems = computed(() =>
248
+ (["all", "available", "installed"] as const).map((scope) => ({
249
+ value: scope,
250
+ label: t(`modules.store.scope.${scope}`, {
251
+ count: filterByScope(storeModules.value, scope).length,
252
+ }),
253
+ })),
254
+ );
255
+ const scopedStoreModules = computed(() =>
256
+ filterByScope(storeModules.value, storeScope.value),
257
+ );
258
+
235
259
  const storeCategoryOptions = computed(() => [
236
260
  {
237
261
  value: MODULE_CATEGORY_ALL,
238
262
  label: t("modules.category.all"),
239
- count: storeModules.value.length,
263
+ count: scopedStoreModules.value.length,
240
264
  },
241
- ...moduleCategoryOptions(storeModules.value, processI18n),
265
+ ...moduleCategoryOptions(scopedStoreModules.value, processI18n),
242
266
  ]);
243
267
 
244
268
  watch(storeCategoryOptions, (options) => {
@@ -248,7 +272,7 @@ watch(storeCategoryOptions, (options) => {
248
272
  });
249
273
 
250
274
  const visibleStoreModules = computed(() =>
251
- filterModules(storeModules.value, {
275
+ filterModules(scopedStoreModules.value, {
252
276
  query: storeQuery.value,
253
277
  category: storeCategory.value,
254
278
  resolve: processI18n,
@@ -261,6 +285,153 @@ function clearStoreFilters(): void {
261
285
  storeCategory.value = MODULE_CATEGORY_ALL;
262
286
  }
263
287
 
288
+ /** Store modules whose interface the running frontend does not match yet. */
289
+ const frontendLagging = computed(() =>
290
+ storeModules.value.filter(
291
+ (entry) =>
292
+ isAwaitingFrontend(entry) ||
293
+ (entry.frontend === "unknown" &&
294
+ moduleStore.awaitingReload.value.has(entry.id)),
295
+ ),
296
+ );
297
+ const frontendNotice = computed(() => {
298
+ const lagging = frontendLagging.value;
299
+ if (lagging.length === 0) return null;
300
+ const names = lagging.map((entry) => processI18n(entry.title)).join(", ");
301
+ const hasReported = lagging.every((entry) => entry.frontend !== "unknown");
302
+ const key = !hasReported
303
+ ? "unknown"
304
+ : storeListing.value?.dev
305
+ ? "dev"
306
+ : "production";
307
+ return { key, names };
308
+ });
309
+
310
+ /**
311
+ * Runs `kind` from a dialog: a refusal is worded as the toast words it and
312
+ * shown in the dialog, which stays open.
313
+ */
314
+ async function runFromDialog(
315
+ kind: StoreOperationKind,
316
+ entry: ModuleStoreEntry,
317
+ body?: StoreActionBody,
318
+ ): Promise<void> {
319
+ try {
320
+ await moduleStore.run(kind, entry, body);
321
+ } catch (error) {
322
+ throw new ConfirmActionError({
323
+ title: t("modules.store.action_failed", {
324
+ module: processI18n(entry.title),
325
+ }),
326
+ description: resolveStoreError(error),
327
+ });
328
+ }
329
+ }
330
+
331
+ /** What the owner last picked in a module's install dialog, for a retry. */
332
+ const lastDependencyDrafts = new Map<string, DependencyDraft[]>();
333
+
334
+ function renderDependencyPicker(
335
+ entry: ModuleStoreEntry,
336
+ drafts: Ref<DependencyDraft[]>,
337
+ showErrors: boolean,
338
+ ): VNode {
339
+ return h(ModuleStoreDependencyPicker, {
340
+ missing: entry.missingInterfaces,
341
+ drafts: drafts.value,
342
+ showErrors,
343
+ onUpdate: (index: number, patch: Partial<DependencyDraft>) => {
344
+ drafts.value = drafts.value.map((draft, position) =>
345
+ position === index ? { ...draft, ...patch } : draft,
346
+ );
347
+ },
348
+ });
349
+ }
350
+
351
+ /**
352
+ * Asks which modules implement the interfaces the release misses, then
353
+ * installs them with it. A refusal shows in the dialog, kept open.
354
+ */
355
+ async function installWithDependencies(entry: ModuleStoreEntry): Promise<void> {
356
+ const title = processI18n(entry.title);
357
+ const drafts = ref(
358
+ createDependencyDrafts(
359
+ entry.missingInterfaces,
360
+ lastDependencyDrafts.get(entry.id),
361
+ ),
362
+ );
363
+ const showErrors = ref(false);
364
+ await confirm({
365
+ title: t("modules.store.install_dialog.title", { module: title }),
366
+ description: t("modules.store.install_dialog.description", {
367
+ module: title,
368
+ }),
369
+ confirmLabel: t("modules.store.actions.install"),
370
+ confirmIcon: "i-ph-download-simple",
371
+ icon: "i-ph-puzzle-piece",
372
+ body: () => renderDependencyPicker(entry, drafts, showErrors.value),
373
+ validate: () => {
374
+ showErrors.value = true;
375
+ return areDependencyDraftsValid(drafts.value);
376
+ },
377
+ onConfirm: async () => {
378
+ lastDependencyDrafts.set(entry.id, drafts.value);
379
+ await runFromDialog(
380
+ "install",
381
+ entry,
382
+ buildInstallBody(entry.id, drafts.value),
383
+ );
384
+ },
385
+ });
386
+ }
387
+
388
+ async function runStoreAction(
389
+ kind: StoreOperationKind,
390
+ entry: ModuleStoreEntry,
391
+ ): Promise<void> {
392
+ const title = processI18n(entry.title);
393
+ if (needsDependencyChoice(entry, kind)) {
394
+ await installWithDependencies(entry);
395
+ return;
396
+ }
397
+ if (kind === "uninstall") {
398
+ await confirm({
399
+ title: t("modules.store.uninstall_confirm.title", { module: title }),
400
+ description: t("modules.store.uninstall_confirm.description", {
401
+ module: title,
402
+ }),
403
+ confirmLabel: t("modules.store.actions.uninstall"),
404
+ color: "error",
405
+ icon: "i-ph-trash",
406
+ onConfirm: () => runFromDialog(kind, entry),
407
+ });
408
+ return;
409
+ }
410
+ try {
411
+ await moduleStore.run(kind, entry);
412
+ } catch (error) {
413
+ toast.add({
414
+ color: "error",
415
+ icon: "i-ph-warning-circle",
416
+ title: t("modules.store.action_failed", { module: title }),
417
+ description: resolveStoreError(error),
418
+ });
419
+ }
420
+ }
421
+
422
+ /** What a refused store request carries: the server's error key, if any. */
423
+ interface StoreRequestError {
424
+ data?: unknown;
425
+ message?: string;
426
+ }
427
+
428
+ function resolveStoreError(error: unknown): string {
429
+ const details = error as StoreRequestError | undefined;
430
+ const key = typeof details?.data === "string" ? details.data : undefined;
431
+ if (!key) return details?.message ?? "";
432
+ return t(`modules.store.errors.${key.replace("error.", "")}`);
433
+ }
434
+
264
435
  // ── Actions ────────────────────────────────────────────────────────
265
436
  async function retry(): Promise<void> {
266
437
  if (isRetrying.value) return;
@@ -528,49 +699,141 @@ const summaryCards = computed<StatGroupItem[]>(() =>
528
699
  </div>
529
700
  </section>
530
701
 
531
- <!-- Module store: a preview, nothing installs from here yet. -->
702
+ <!-- Module store: the official modules, installed or not. -->
532
703
  <section class="mt-10" aria-labelledby="module-store-title">
533
704
  <DmsSectionHeader
534
705
  class="mb-3"
535
- :count="isInitiallyLoading ? undefined : storeModules.length"
706
+ :count="isStoreLoading ? undefined : storeModules.length"
536
707
  :description="t('modules.store.description')"
537
708
  >
538
709
  <template #title>
539
710
  <span id="module-store-title">{{ t("modules.store.title") }}</span>
540
711
  </template>
541
- <template #badge>
542
- <DmsStatusPill
543
- tone="info"
544
- :label="t('modules.store.soon')"
712
+ <template #trailing>
713
+ <span
714
+ v-if="storeListing?.frontendVersion"
715
+ :class="[
716
+ MONO_CHIP_CLASS,
717
+ 'text-dimmed border border-(--ui-border) text-[11px] max-sm:hidden',
718
+ ]"
719
+ :title="
720
+ storeListing.frontendVersionSource
721
+ ? t(
722
+ `modules.store.frontend_version.${storeListing.frontendVersionSource}`,
723
+ )
724
+ : undefined
725
+ "
726
+ >
727
+ {{
728
+ t("modules.store.frontend_version.label", {
729
+ version: storeListing.frontendVersion,
730
+ })
731
+ }}
732
+ </span>
733
+ <DmsSegmented
734
+ v-model="storeScope"
735
+ :items="storeScopeItems"
545
736
  size="sm"
546
- uppercase
737
+ :aria-label="t('modules.store.scope.label')"
547
738
  />
548
739
  </template>
549
740
  </DmsSectionHeader>
550
741
 
551
742
  <DmsBanner
743
+ v-if="storeListing?.configError"
744
+ class="mb-4"
745
+ tone="error"
746
+ size="sm"
747
+ icon="i-ph-file-x"
748
+ :title="t('modules.store.config_error')"
749
+ :description="storeListing.configError"
750
+ />
751
+ <DmsBanner
752
+ v-else-if="storeListing && !storeListing.enabled"
552
753
  class="mb-4"
553
754
  tone="info"
554
755
  size="sm"
555
- icon="i-ph-storefront"
556
- :title="t('modules.store.banner.title')"
557
- :description="t('modules.store.banner.description')"
756
+ icon="i-ph-lock-simple"
757
+ :title="t('modules.store.disabled.title')"
758
+ :description="t('modules.store.disabled.description')"
558
759
  />
559
760
 
560
- <!-- Which modules are installed is unknown until the listing loads:
561
- the store list waits for it. -->
562
- <DmsCard
563
- v-if="!isInitiallyLoading && storeModules.length === 0"
564
- :padded="false"
761
+ <!-- The interface lags behind the backend: say what follows. -->
762
+ <DmsBanner
763
+ v-if="frontendNotice"
764
+ class="mb-4"
765
+ :tone="frontendNotice.key === 'dev' ? 'info' : 'warning'"
766
+ size="sm"
767
+ :icon="
768
+ frontendNotice.key === 'dev'
769
+ ? 'i-ph-circle-notch'
770
+ : 'i-ph-arrows-clockwise'
771
+ "
772
+ :title="
773
+ t(`modules.store.frontend.${frontendNotice.key}.title`, {
774
+ modules: frontendNotice.names,
775
+ })
776
+ "
565
777
  >
566
- <DmsEmptyState
567
- icon="i-ph-check-circle"
568
- :title="t('modules.store.all_installed.title')"
569
- :description="t('modules.store.all_installed.description')"
570
- />
571
- </DmsCard>
778
+ <template #description>
779
+ <i18n-t
780
+ :keypath="`modules.store.frontend.${frontendNotice.key}.description`"
781
+ scope="global"
782
+ tag="span"
783
+ >
784
+ <template #command>
785
+ <code
786
+ :class="[
787
+ MONO_CHIP_CLASS,
788
+ 'bg-elevated text-highlighted text-xs',
789
+ ]"
790
+ >
791
+ {{ FRONTEND_BUILD_COMMAND }}
792
+ </code>
793
+ </template>
794
+ </i18n-t>
795
+ </template>
796
+ </DmsBanner>
572
797
 
573
- <template v-else-if="!isInitiallyLoading">
798
+ <div v-if="isStoreLoading" :class="TILE_GRID_CLASS" aria-busy="true">
799
+ <DmsCard
800
+ v-for="placeholder in skeletonItems"
801
+ :key="placeholder"
802
+ :padded="false"
803
+ class="flex min-h-[228px] flex-col gap-3 p-4"
804
+ >
805
+ <USkeleton
806
+ :aria-label="t('dms.a11y.loading')"
807
+ class="size-9 rounded-[10px]"
808
+ />
809
+ <USkeleton
810
+ :aria-label="t('dms.a11y.loading')"
811
+ class="mt-auto h-2.5 w-3/5"
812
+ />
813
+ </DmsCard>
814
+ </div>
815
+
816
+ <!-- Only when nothing is on screen yet: a follow-up read that fails
817
+ while the frontend restarts keeps the tiles and tries again. -->
818
+ <DmsBanner
819
+ v-else-if="moduleStore.loadingError.value && !storeListing"
820
+ tone="error"
821
+ size="sm"
822
+ icon="i-ph-plugs"
823
+ :title="t('modules.store.load_error')"
824
+ >
825
+ <template #actions>
826
+ <UButton
827
+ color="error"
828
+ size="sm"
829
+ icon="i-ph-arrows-clockwise"
830
+ :label="t('modules.error.retry')"
831
+ @click="moduleStore.load()"
832
+ />
833
+ </template>
834
+ </DmsBanner>
835
+
836
+ <template v-else>
574
837
  <div :class="TOOLBAR_CLASS">
575
838
  <DmsSearchInput
576
839
  v-model="storeQuery"
@@ -586,7 +849,18 @@ const summaryCards = computed<StatGroupItem[]>(() =>
586
849
  />
587
850
  </div>
588
851
 
589
- <DmsCard v-if="visibleStoreModules.length === 0" :padded="false">
852
+ <DmsCard
853
+ v-if="scopedStoreModules.length === 0 && storeScope === 'available'"
854
+ :padded="false"
855
+ >
856
+ <DmsEmptyState
857
+ icon="i-ph-check-circle"
858
+ :title="t('modules.store.all_installed.title')"
859
+ :description="t('modules.store.all_installed.description')"
860
+ />
861
+ </DmsCard>
862
+
863
+ <DmsCard v-else-if="visibleStoreModules.length === 0" :padded="false">
590
864
  <DmsEmptyState
591
865
  variant="no-result"
592
866
  :title="
@@ -614,6 +888,11 @@ const summaryCards = computed<StatGroupItem[]>(() =>
614
888
  v-for="entry in visibleStoreModules"
615
889
  :key="entry.id"
616
890
  :entry="entry"
891
+ :is-locked="
892
+ !storeListing?.enabled ||
893
+ (storeListing?.busy === true && entry.state !== 'busy')
894
+ "
895
+ @action="runStoreAction($event, entry)"
617
896
  />
618
897
  </div>
619
898
  </template>
@@ -1,16 +1,20 @@
1
1
  <script setup lang="ts">
2
+ import type { DmsLogoSources } from "#dms-core/shared/types/app-config";
2
3
  import { emailLogoUrl, emailStyles } from "./index";
3
4
 
5
+ interface EmailLogoConfig {
6
+ default?: Partial<DmsLogoSources>;
7
+ email?: Partial<DmsLogoSources>;
8
+ }
9
+
4
10
  interface EmailAppConfig {
5
11
  branding?: {
6
- logo?: {
7
- default?: {
8
- light?: string;
9
- };
10
- };
12
+ logo?: EmailLogoConfig;
11
13
  };
12
14
  }
13
15
 
16
+ const DARK_SCHEME_MEDIA = "(prefers-color-scheme: dark)";
17
+
14
18
  const props = withDefaults(
15
19
  defineProps<{
16
20
  /** Shown as the email's heading; no heading when omitted. */
@@ -33,13 +37,20 @@ const props = withDefaults(
33
37
  const appConfig = useDmsAppConfig() as EmailAppConfig;
34
38
  const runtimeConfig = useDmsRuntimeConfig();
35
39
 
36
- const logoPath = appConfig.branding?.logo?.default?.light;
40
+ const logo = appConfig.branding?.logo;
37
41
  const clientBaseUrl = runtimeConfig.public.dms?.clientBaseUrl ?? "";
38
- const logoUrl = props.publicLogoOnly
39
- ? emailLogoUrl(logoPath, clientBaseUrl)
40
- : logoPath?.startsWith("/")
41
- ? `${clientBaseUrl}${logoPath}`
42
- : logoPath;
42
+
43
+ function logoSource(path: string | undefined): string {
44
+ if (props.publicLogoOnly) return emailLogoUrl(path, clientBaseUrl);
45
+ return path?.startsWith("/") ? `${clientBaseUrl}${path}` : (path ?? "");
46
+ }
47
+
48
+ const logoUrl = logoSource(logo?.email?.light || logo?.default?.light);
49
+ // Offered to the mail clients that follow the reader's dark mode; the others
50
+ // skip the picture's source and draw the light image. Taken from the email
51
+ // slot only, so an instance that sets none keeps its single image.
52
+ const darkCandidate = logoSource(logo?.email?.dark);
53
+ const darkLogoUrl = darkCandidate === logoUrl ? "" : darkCandidate;
43
54
  </script>
44
55
 
45
56
  <template>
@@ -52,7 +63,16 @@ const logoUrl = props.publicLogoOnly
52
63
  <EContainer :style="emailStyles.container">
53
64
  <ESection :style="emailStyles.card">
54
65
  <ESection v-if="logoUrl" :style="emailStyles.logo">
66
+ <picture v-if="darkLogoUrl">
67
+ <source :media="DARK_SCHEME_MEDIA" :srcset="darkLogoUrl" />
68
+ <EImg
69
+ :src="logoUrl"
70
+ :alt="logoAlt"
71
+ :style="emailStyles.logoImage"
72
+ />
73
+ </picture>
55
74
  <EImg
75
+ v-else
56
76
  :src="logoUrl"
57
77
  :alt="logoAlt"
58
78
  :style="emailStyles.logoImage"
@@ -4,6 +4,7 @@ import StageCard, {
4
4
  type StageCardTone,
5
5
  } from "./build/components/layout/StageCard.vue";
6
6
  import EmptyLayout from "./custom-layouts/EmptyLayout.vue";
7
+ import { publicErrorMessage } from "./build/utils/publicErrorMessage";
7
8
 
8
9
  interface ErrorConfig {
9
10
  icon: string;
@@ -66,10 +67,12 @@ const statusCode = computed(
66
67
  () => props.error.statusCode || DEFAULT_STATUS_CODE,
67
68
  );
68
69
 
70
+ // Never the backend's address a failed request names (see
71
+ // `publicErrorMessage`).
69
72
  const visibleErrorMessage = computed(() =>
70
73
  STATUSES_WITHOUT_ERROR_MESSAGE.has(statusCode.value)
71
74
  ? undefined
72
- : props.error.message,
75
+ : publicErrorMessage(props.error.message),
73
76
  );
74
77
 
75
78
  const errorConfig = computed(
@@ -89,7 +92,7 @@ const description = computed(() =>
89
92
  );
90
93
 
91
94
  const handleError = async () => {
92
- clearError();
95
+ clearDmsError();
93
96
  if (!loggedIn.value) {
94
97
  await redirectToAuth(homepage);
95
98
  } else {
@@ -89,7 +89,7 @@ export default defineDmsMiddleware(async (to, from) => {
89
89
  const moduleEntry = siteLayout.modules.value?.[moduleId];
90
90
 
91
91
  if (!moduleId || !moduleEntry) {
92
- throw createError({
92
+ throw createDmsError({
93
93
  statusCode: MODULE_NOT_FOUND_STATUS,
94
94
  statusMessage: MODULE_NOT_FOUND_MESSAGE,
95
95
  data: { kind: MODULE_NOT_FOUND_KIND },
@@ -8,7 +8,7 @@ export default defineDmsMiddleware(async (to, from) => {
8
8
  // Unsaved changes anywhere (the page's forms, an open drawer or modal, a
9
9
  // settings panel): one warning dialog, whatever holds them.
10
10
  if (!(await confirmLeave())) {
11
- return abortNavigation();
11
+ return abortDmsNavigation();
12
12
  }
13
13
 
14
14
  const { executeGuards } = useLeaveGuard();
@@ -17,6 +17,6 @@ export default defineDmsMiddleware(async (to, from) => {
17
17
  const canLeave = await executeGuards(pageKey);
18
18
 
19
19
  if (!canLeave) {
20
- return abortNavigation();
20
+ return abortDmsNavigation();
21
21
  }
22
22
  });