@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
@@ -85,7 +85,8 @@
85
85
  "include": "Includes",
86
86
  "exclude": "Excludes",
87
87
  "contains": "Contains",
88
- "not_contains": "Does not contain"
88
+ "not_contains": "Does not contain",
89
+ "array_contains_string": "Contains"
89
90
  },
90
91
  "filter": {
91
92
  "boolean": {
@@ -108,7 +109,7 @@
108
109
  "show_archived": "Show archived items",
109
110
  "archived": "Archived",
110
111
  "archive_toggle": "Archive",
111
- "archived_count": "{count} archived",
112
+ "archived_count": "Archived: {count}",
112
113
  "archive_strip_title": "Viewing archived items.",
113
114
  "archive_strip_description": "They are hidden from the regular list until restored.",
114
115
  "archived_empty_title": "No archived items",
@@ -177,6 +178,8 @@
177
178
  "save_as": "Save as new view",
178
179
  "save_current": "Save current view",
179
180
  "delete": "Delete view",
181
+ "delete_title": "Delete this view?",
182
+ "delete_description": "\"{view}\" will be deleted for good. The table goes back to its default state if the view is open.",
180
183
  "save_as_title": "Save as a new view",
181
184
  "save_as_description": "Keep the current filters, search, sort, columns and display under a name. Only you will see it.",
182
185
  "save_as_confirm": "Save view",
@@ -257,6 +260,7 @@
257
260
  "reset": "Reset",
258
261
  "cancel": "Cancel",
259
262
  "save_changes": "Save changes",
263
+ "create": "Create",
260
264
  "refresh_data": "Refresh data",
261
265
  "export_data": "Export data"
262
266
  },
@@ -406,7 +410,8 @@
406
410
  "invalid": "No errors | 1 field to fix | {count} fields to fix"
407
411
  },
408
412
  "error_summary": {
409
- "title": "No field to fix | 1 field needs your attention | {count} fields need your attention"
413
+ "title": "No field to fix | 1 field needs your attention | {count} fields need your attention",
414
+ "resolved": "Everything is fixed: the form can be saved."
410
415
  },
411
416
  "repeater": {
412
417
  "add": "Add a row",
@@ -515,7 +520,7 @@
515
520
  },
516
521
  "livetoggle": {
517
522
  "l": {
518
- "description": "Switch the page between Direct and Pause"
523
+ "description": "Switch the page between Live and Pause"
519
524
  }
520
525
  }
521
526
  },
@@ -687,6 +692,9 @@
687
692
  "info": "note"
688
693
  }
689
694
  },
695
+ "flow_canvas": {
696
+ "delete_node": "Delete node"
697
+ },
690
698
  "password_rules": {
691
699
  "head": "{met} of {total} met",
692
700
  "verdict": {
@@ -750,6 +758,7 @@
750
758
  "show_new": "Show",
751
759
  "paused": "Paused",
752
760
  "resume": "Resume",
761
+ "refresh": "Refresh",
753
762
  "reason_hidden": "tab hidden",
754
763
  "polling": "no realtime, polling",
755
764
  "table_held": "Table on hold",
@@ -771,6 +780,9 @@
771
780
  "invalid_state": "The preview state could not be read.",
772
781
  "empty": "Nothing to preview yet.",
773
782
  "unsafe_method": "{name} was not drawn: a preview only reads its data, and {value} writes."
783
+ },
784
+ "drawer": {
785
+ "resize": "Resize the drawer"
774
786
  }
775
787
  }
776
788
  }
@@ -85,7 +85,8 @@
85
85
  "include": "contient",
86
86
  "exclude": "ne contient pas",
87
87
  "contains": "contient",
88
- "not_contains": "ne contient pas"
88
+ "not_contains": "ne contient pas",
89
+ "array_contains_string": "contient"
89
90
  },
90
91
  "filter": {
91
92
  "boolean": {
@@ -108,7 +109,7 @@
108
109
  "show_archived": "Voir les éléments archivés",
109
110
  "archived": "Archivés",
110
111
  "archive_toggle": "Archives",
111
- "archived_count": "{count} archivés",
112
+ "archived_count": "Archives : {count}",
112
113
  "archive_strip_title": "Éléments archivés.",
113
114
  "archive_strip_description": "Ils restent masqués de la liste habituelle jusqu’à leur restauration.",
114
115
  "archived_empty_title": "Aucun élément archivé",
@@ -177,6 +178,8 @@
177
178
  "save_as": "Enregistrer comme nouvelle vue",
178
179
  "save_current": "Enregistrer la vue actuelle",
179
180
  "delete": "Supprimer la vue",
181
+ "delete_title": "Supprimer cette vue ?",
182
+ "delete_description": "« {view} » sera supprimée définitivement. Si elle est ouverte, le tableau revient à son état par défaut.",
180
183
  "save_as_title": "Enregistrer comme nouvelle vue",
181
184
  "save_as_description": "Conservez les filtres, la recherche, le tri, les colonnes et l'affichage actuels sous un nom. Vous seul la verrez.",
182
185
  "save_as_confirm": "Enregistrer la vue",
@@ -257,6 +260,7 @@
257
260
  "reset": "Réinitialiser",
258
261
  "cancel": "Annuler",
259
262
  "save_changes": "Enregistrer les modifications",
263
+ "create": "Créer",
260
264
  "refresh_data": "Actualiser les données",
261
265
  "export_data": "Exporter les données"
262
266
  },
@@ -407,7 +411,8 @@
407
411
  "invalid": "Aucune erreur | 1 champ à corriger | {count} champs à corriger"
408
412
  },
409
413
  "error_summary": {
410
- "title": "Aucun champ à corriger | 1 champ demande votre attention | {count} champs demandent votre attention"
414
+ "title": "Aucun champ à corriger | 1 champ demande votre attention | {count} champs demandent votre attention",
415
+ "resolved": "Tout est corrigé : le formulaire peut être enregistré."
411
416
  },
412
417
  "repeater": {
413
418
  "add": "Ajouter une ligne",
@@ -688,6 +693,9 @@
688
693
  "info": "note"
689
694
  }
690
695
  },
696
+ "flow_canvas": {
697
+ "delete_node": "Supprimer le nœud"
698
+ },
691
699
  "password_rules": {
692
700
  "head": "{met} sur {total} respectées",
693
701
  "verdict": {
@@ -719,7 +727,7 @@
719
727
  },
720
728
  "save_bar": {
721
729
  "unsaved": "Modifications non enregistrées",
722
- "discard": "Annuler",
730
+ "discard": "Abandonner",
723
731
  "save": "Enregistrer",
724
732
  "saving": "Enregistrement",
725
733
  "saved": "Enregistré",
@@ -751,6 +759,7 @@
751
759
  "show_new": "Afficher",
752
760
  "paused": "En pause",
753
761
  "resume": "Reprendre",
762
+ "refresh": "Actualiser",
754
763
  "reason_hidden": "onglet masqué",
755
764
  "polling": "sans temps réel, relecture régulière",
756
765
  "table_held": "Tableau figé",
@@ -772,6 +781,9 @@
772
781
  "invalid_state": "L’état de l’aperçu est illisible.",
773
782
  "empty": "Rien à prévisualiser pour l’instant.",
774
783
  "unsafe_method": "{name} n’a pas été affiché : un aperçu ne fait que lire ses données, et {value} écrit."
784
+ },
785
+ "drawer": {
786
+ "resize": "Redimensionner le tiroir"
775
787
  }
776
788
  }
777
789
  }
@@ -11,7 +11,7 @@
11
11
  "license": "Apache-2.0",
12
12
  "packageManager": "pnpm@10.6.5",
13
13
  "engines": {
14
- "@antelopejs/dms-frontend": ">=0.6.3 <0.8.0"
14
+ "@antelopejs/dms-frontend": ">=0.8.0 <0.9.0"
15
15
  },
16
16
  "scripts": {
17
17
  "typecheck": "ajs dms verify-source --layer \"$PWD\" --local-package @antelopejs/dms=.. --local-package @antelopejs/interface-dms=../../interface-dms",
@@ -75,7 +75,7 @@
75
75
  },
76
76
  "devDependencies": {
77
77
  "@antelopejs/core": ">=1.16.0 <2",
78
- "@antelopejs/dms-frontend": ">=0.6.3 <0.7.0",
78
+ "@antelopejs/dms-frontend": ">=0.8.0 <0.9.0",
79
79
  "@eslint/js": "^8.57.0",
80
80
  "@inertiajs/vue3": "^3.7.0",
81
81
  "@nuxt/ui": "4.10.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antelopejs/dms",
3
- "version": "0.9.7",
3
+ "version": "0.10.0",
4
4
  "description": "The AntelopeJS dashboard management system: admin dashboards declared in TypeScript, with auth and tenancy, data management, file uploads, notifications and realtime.",
5
5
  "keywords": [
6
6
  "antelopejs",
@@ -22,6 +22,7 @@
22
22
  },
23
23
  "main": "dist/index.js",
24
24
  "types": "dist/index.d.ts",
25
+ "dmsFrontend": "dist/frontend-module.js",
25
26
  "files": [
26
27
  "dist",
27
28
  "!dist/.tsbuildinfo",
@@ -57,16 +58,15 @@
57
58
  "@antelopejs/interface-api": ">=0.0.15 <1.0.0",
58
59
  "@antelopejs/interface-api-util": ">=0.1.3 <1.0.0",
59
60
  "@antelopejs/interface-core": ">=0.2.4 <1.0.0",
60
- "@antelopejs/interface-data-api": ">=0.2.2 <1.0.0",
61
+ "@antelopejs/interface-data-api": ">=0.3.0 <1.0.0",
61
62
  "@antelopejs/interface-database": ">=0.1.10 <1.0.0",
62
63
  "@antelopejs/interface-database-decorators": ">=0.1.9 <1.0.0",
63
- "@antelopejs/interface-dms": ">=0.7.4 <0.8.0",
64
+ "@antelopejs/interface-dms": ">=0.8.0 <0.9.0",
64
65
  "@antelopejs/interface-email": ">=0.0.6 <1.0.0",
65
66
  "@antelopejs/interface-file-storage": ">=0.1.4 <1.0.0",
66
67
  "archiver": "^7.0.1",
67
68
  "csv-stringify": "^6.7.0",
68
69
  "defu": "^6.1.4",
69
- "ignore": "^7.0.5",
70
70
  "jsonwebtoken": "^9.0.3",
71
71
  "mmdb-lib": "^3.0.3",
72
72
  "node-cron": "^4.2.1",
@@ -116,7 +116,7 @@
116
116
  "registry": "https://registry.npmjs.org/"
117
117
  },
118
118
  "optionalDependencies": {
119
- "@antelopejs/interface-dms-automation": ">=0.2.1 <1.0.0",
119
+ "@antelopejs/interface-dms-automation": ">=0.4.0 <1.0.0",
120
120
  "@antelopejs/interface-redis": ">=0.0.9 <1.0.0"
121
121
  },
122
122
  "peerDependencies": {
@@ -145,6 +145,7 @@
145
145
  "knip:all": "knip",
146
146
  "docs:catalog": "node scripts/docs-catalog.mjs",
147
147
  "docs:catalog:check": "node scripts/docs-catalog.mjs --check",
148
- "docs:screenshots": "node scripts/docs-screenshots.mjs"
148
+ "docs:screenshots": "node scripts/docs-screenshots.mjs",
149
+ "realtime:tabs-check": "node scripts/realtime-tabs-check.mjs"
149
150
  }
150
151
  }
@@ -34,7 +34,7 @@ skill (shipped with `@antelopejs/core`).
34
34
  `pageRoutePath(slug, route)`); a plain `Controller` the frontend calls sits under
35
35
  `/api/<module>/`. A URL names the page's record with `{{params.id}}` /
36
36
  `{{query.X}}`; `periodScope` binds a block to a `PeriodSelector`; `realtimeTopic` re-fetches on a
37
- `PublishMessage`. Texts are `$` i18n keys or `ComposedText`, never formatted server-side.
37
+ `PublishMessage` (publish tenant data with `{ tenantId }`). Texts are `$` i18n keys or `ComposedText`, never formatted server-side.
38
38
  - **It's interfaces all the way down.** Everything you consume is imported from an
39
39
  `@antelopejs/interface-dms/...` subpath; a distributable module exposes its own API from a companion
40
40
  `@antelopejs/interface-<name>` package listed in `antelopeJs.implements`.
@@ -78,7 +78,7 @@ RegisterPermission("cookbook.edit", {
78
78
  title: "$dms_cookbook.perm.edit", // i18n key — add to every locale file
79
79
  icon: "i-ph-pencil",
80
80
  defaultGranted: false,
81
- // dependencies: ["cookbook.view"], // declarative metadata only — not enforced or auto-granted
81
+ // dependencies: ["cookbook.view"], // enforced by HasPermission; granted with it by the roles editor and on save
82
82
  });
83
83
  ```
84
84
 
@@ -86,7 +86,9 @@ Then gate with `@AuthUserWithPermission(...)` or the page/component permission o
86
86
  check imperatively, use `dms/permissions`: `GetEffectiveUserPermissions`, `HasPermission`,
87
87
  `GetPermission` / `GetPermissions` (`GetUserPermissions` for the base set). Two extension
88
88
  points reshape the model: `RegisterPermissionsResolver` (`dms/permissions-resolver`) rewrites
89
- a user's effective set, and `RegisterTenantAccessGate` (`dms/tenant-access`, checked via
89
+ a user's effective set (the role preview also runs it, with `context.preview` set on the
90
+ resolver's fourth argument and the viewer's user id: shape the set as for an ordinary
91
+ member there, never from the viewer's standing), and `RegisterTenantAccessGate` (`dms/tenant-access`, checked via
90
92
  `CheckTenantAccess` / `AssertTenantAccess`) can block a whole tenant. The `dms/guards`
91
93
  decorators also run the tenant access gate; routes that must stay reachable for a denied
92
94
  tenant (billing, recovery) opt out with `@AuthTenantOwner({ bypassTenantAccessGate: true })`.
@@ -102,7 +104,7 @@ there too); `RoleModel`, `UserInviteModel`, `TenantModel`, `TenantMemberModel` u
102
104
  The DMS is **multi-tenant by default** (single-tenant apps live in the `default` tenant).
103
105
  Primitives, covered in `docs/03.auth-and-tenancy/03.multi-tenant.md`: `@TenantScopedModel`
104
106
  (`dms/tenant-scoped-model`) scopes a model to the request's tenant, `getRequestTenantId`
105
- (`dms/request-tenant`) reads it, `applyTenantOwnership`
107
+ (`dms/request-tenant`) reads it (the verified credential's tenant, `default` without one), `applyTenantOwnership`
106
108
  (`dms/tenant-ownership`) upserts a user's tenant membership (roles + owner flag),
107
109
  `inviteUserToTenant` (`dms/invites`, expiry `INVITE_EXPIRY_DAYS`), and `CROSS_INSTANCE`
108
110
  (`@antelopejs/interface-database`) for cross-tenant queries. SaaS mode (Stripe billing,
@@ -13,11 +13,13 @@ Material for [SKILL.md](SKILL.md): mapping design elements to DMS blocks, the `P
13
13
  | Ranked list, leaderboard | `TopListCard` | Bar position, rank badge style |
14
14
  | Date range picker | `PeriodSelector` (`variant: "segmented"` for pills) | Preset labels |
15
15
  | Live / pause switch | `LiveToggle` | Pill vs segmented |
16
+ | Context bar of a page with a period or live data | `pageToolbar()` (a `PageToolbar` holding the `LiveToggle` and `PeriodSelector`) | Leading blocks, `size` |
16
17
  | Table, list with actions | `TableView` over a DataController (`displays` for cards / kanban) | Column widths, row height (`density`) |
17
18
  | Detail facts of a record | `KeyValueList` | Value alignment |
18
19
  | Recent events, audit log | `ActivityFeed` | Day separators vs relative times |
19
20
  | Usage bar, quota | `Meter` | Bar thickness (`size`) |
20
21
  | Notice, alert strip | `Banner` | Wash vs flat background |
22
+ | Strip under the dashboard header, across pages | `RegisterLayoutBanner` (backend): the app's pages by default, `scope: "everywhere"` for a workspace-wide state | Not inside `/modules` unless scoped `everywhere` |
21
23
  | Empty / first-run screen | `EmptyState` (or `Guard` around blocks) | Illustration vs icon well |
22
24
  | Links to sub-pages as cards | `NavCardGrid` | Card height |
23
25
  | Code snippet | `CodeBlock` | Theme of the highlighting |
@@ -73,5 +73,9 @@ The protocol is done when every visibly different component has an answer and ev
73
73
 
74
74
  - Every page field and child is a DMS block, except custom blocks the user chose through the path.
75
75
  - Every `CustomComponent` has `.meta({ name, description, icon })` with `$` keys present in every locale file.
76
+ - Every automation node a module registers names itself, its description and each schema property's `title` and `description` with `$` keys present in every locale file (`docs/02.building/06.backend-services.md`, Texts in the builder).
77
+ - An automation action with side effects (email, notification, write) declares `dryRun(input, ctx)`: it checks the input as `execute` would and acts on nothing, so a builder Test run sends nothing (`docs/02.building/06.backend-services.md`, Test runs).
78
+ - Locale values escape vue-i18n's special characters as literals (`{'@'}`, `{'{'}`, `{'}'}`, `{'|'}`): a raw `@` or brace breaks the message's compilation (`docs/02.building/07.localization.md`, Special characters).
76
79
  - Data comes from routes (`fetchUrl`) rather than options computed at registration; a custom block reads its route with `useApiData` (server-rendered, refreshed with the page) and writes with `$authFetch`.
80
+ - Publish tenant data with the tenant id: a `PublishMessage` about one workspace's data passes `{ tenantId }`, or it wakes the page of every workspace.
77
81
  - Verified live with **dms-project** (the build only typechecks `src/`).
@@ -85,7 +85,7 @@ module's frontend; without it the module is backend-only.
85
85
 
86
86
  `renderer` names the framework (Vue 3), not a loader release. The `@antelopejs/dms-frontend`
87
87
  releases the frontend module runs on go in the frontend module's own `package.json`, as
88
- `"engines": { "@antelopejs/dms-frontend": ">=0.6.0 <0.7.0" }`: the loader refuses the module
88
+ `"engines": { "@antelopejs/dms-frontend": ">=0.8.0 <0.9.0" }`: the loader refuses the module
89
89
  outside that range. Cap it below the next loader minor while the loader is 0.x.
90
90
 
91
91
  An absolute link (a public file, a snippet to copy, a mail) is built on `await GetApiBaseUrl()`
@@ -103,7 +103,8 @@ module root** (nest under what `RegisterModule` returned, or rely on `defaultCat
103
103
  module pages cannot use the Settings category, and the module must exist before a page names
104
104
  it. Pages resolve at `/modules/<moduleId>/<urlSlug>`; entering the module lands on
105
105
  `landingPage` if set, else the first page by `order` — `/modules/<id>` itself redirects there (server
106
- redirect, query and hash kept), so never register a hidden page to forward it. Access is **owner-only by design**: the
106
+ redirect, query and hash kept), so never register a hidden page to forward it. `store` is not a
107
+ module id to take: `/modules/store` is the DMS's module store. Access is **owner-only by design**: the
107
108
  Modules group is visible to the platform owner alone (the `*` holder), and the
108
109
  `modules.<moduleId>.…` permission ids are module-scoped — never grantable through Roles. On a
109
110
  SaaS deployment that means you, the maker, not your customers; a screen for end users belongs
@@ -10,7 +10,7 @@ lists them with their option schemas); `docs/04.components/` has one page per bl
10
10
 
11
11
  | Group | Blocks |
12
12
  | --- | --- |
13
- | Layout | `Grid` + `GridRow` (children must be rows; `{ colSpan }` child metadata), `HStack` / `VStack` / `Spacer`, `Card` (slots `actions`, `footer`), `Tab` (`{ slot }` per child), `Guard`, `Placeholder` |
13
+ | Layout | `Grid` + `GridRow` (children must be rows; `{ colSpan }` child metadata; a `GridRow` inside a cell, e.g. in a `Section`, spreads its own children rather than the grid's columns), `HStack` / `VStack` / `Spacer`, `PageToolbar` (`pageToolbar()`), `Card` (slots `actions`, `footer`), `Tab` (`{ slot }` per child), `Guard`, `Placeholder` |
14
14
  | Charts & widgets | `KpiCard`, `ChartCard` (wraps one chart), `TopListCard`, `PeriodSelector`, `LiveToggle`; charts `ChartLine`, `ChartArea`, `ChartRangeArea`, `ChartBar`, `ChartColumn`, `ChartScatter`, `ChartMixed`, `ChartRadar`, `ChartCandlestick`, `ChartDonut`, `ChartPie`, `ChartRadialBar`, `ChartHeatmap` |
15
15
  | Display | `StatGroup`, `KeyValueList`, `NavCardGrid`, `EmptyState` (its `code` shows a snippet), `Banner`, `CodeBlock` (`code` + `language`, or `fetchUrl` answering `{ code, language? }`) |
16
16
  | Lists & settings | `ActivityFeed`, `Meter`, `Section` + `FieldRow` |
@@ -38,7 +38,8 @@ gets `navigation` (J / K, `index` / `total` in the order shown) —
38
38
  A page's `headerActions` follow its URL: a target URL takes `{{params.X}}` / `{{query.X}}`
39
39
  (`?workspace={{query.workspace}}`, a pair the page has no value for is dropped), a label's
40
40
  translation takes the page's route and query parameters (`"Download {locale}.json"`), and an
41
- `availability` reads `context.query`, asked again when the query changes — no need to build
41
+ `availability` reads `context.query` and the route parameters in `context.params` (`:id`),
42
+ asked again when either changes (a detail page hides "Edit" for a missing record) — no need to build
42
43
  the header in Vue with `usePageHeaderActions` to carry the scope —
43
44
  `packages/interface-dms/docs/3.base/6.layout.md#buttons-that-follow-the-page-url`. An `availability`
44
45
  answering `{ hidden: true }` leaves the button out for that query (`{ reason }` only
@@ -48,13 +49,42 @@ component's client-side ones (default `0`, served first on a tie: `order: -1` pu
48
49
  "Export" before a served "Add key") — `packages/interface-dms/docs/3.base/6.layout.md#ordering-the-header`.
49
50
 
50
51
  A button or row action (`customButtons`, `rowActions.custom`, a page's `headerActions`)
51
- points at an `ActionTarget`. A `drawer` target opens from the bottom; give it
52
- `direction: "right"` for a side sheet (an inspector, a detail panel) instead of hosting your
53
- own drawer in a `CustomComponent`.
54
- To read rows one after the other next to the table (a request log), use a `sidePanel`
55
- target instead: a non-modal panel docked on the right whose header the DMS draws (title,
56
- `rowTitle` naming the row from its fields, `positionLabel` "x of y", previous / next, J / K, close), `?record=` kept by default (`deepLink: false` opts out), a "not found" state on a 404 (`notFound: { title, description }`) and the table's
57
- `rowClick: "single"` (a `TableView.fromSource` table reads a deep-linked row beyond its first page through `fetchRowUrl: "/api/x/{{row.id}}"`) — never bridge the table's DOM to build one —
52
+ points at an `ActionTarget`. A detail or record panel (an inspector, a request, a run) is a
53
+ `drawer` target — never a `sidePanel`, and never your own drawer hosted in a
54
+ `CustomComponent`. It opens from the bottom; `direction: "right"` makes it a side sheet,
55
+ 600px wide from `lg` (`size: "3xl"` opens it at a modal's width instead), which the user
56
+ resizes by its inner edge and which opens again at that size. On a row it takes the side
57
+ panel's row options: the DMS draws the row header once `rowTitle` (naming the row from its
58
+ fields) or `positionLabel` ("Request 4 of 50") is set (`rowHeader: true` with the defaults),
59
+ with previous / next next to close; J / K step in place, through every row the table lists
60
+ (the position counts them all, a step past the page loaded turns the page, a step goes to
61
+ the open row's neighbour by id so live rows never make it skip or revisit one, and a
62
+ deep-linked row past the page loaded is located in the table's result, in the order
63
+ the table shows, pinned rows leading their own page); `deepLink:
64
+ true` on the target keeps
65
+ `?record=`; `notFound: { title, description }` draws the 404 state — with the table's
66
+ `rowClick: "single"` (a `TableView.fromSource` table reads a deep-linked row beyond its first
67
+ page through `fetchRowUrl: "/api/x/{{row.id}}"`) — never bridge the table's DOM to build one —
68
+ `docs/04.components/08.tables/8.row-drawers.md#detail-drawers`.
69
+ A drawer a module opens itself through `useDrawer()` keeps the size the user gives it under
70
+ its `sizeKey`, by default its `containerId`, else its component's name — pass `sizeKey` only
71
+ when one component opens drawers that must not share a size, or when the component is
72
+ anonymous — `docs/06.frontend-composables/05.ui-toolkit.md`. On a row of a list the module draws
73
+ itself, `rowHeader: { title, positionLabel, navigation }` draws the DMS's row header (position,
74
+ previous / next, J and K; `titleClass`, `titleBreakAfter`, `subtitle`, `subtitleClass` and
75
+ `component` / `componentOptions` under the position line shape it — never a header component of
76
+ the module's own); `patch({ componentOptions, componentKey, rowHeader })` steps it — never
77
+ import the layer's `RowHeader` component —
78
+ `docs/06.frontend-composables/05.ui-toolkit.md#a-drawer-on-a-row-of-your-own`.
79
+ A drawer's body is a flex column: a body component with a growing root (`flex flex-1 flex-col`)
80
+ fills it and ends with `<DmsContainerFooter>` for its buttons — never measure the drawer nor use
81
+ `--dms-form-foot-*`; a code editor taking the height left (`DmsInputCode fill`, in a stacked
82
+ `DmsFieldRow fill`) — never count its lines from the drawer's size —
83
+ `docs/06.frontend-composables/05.ui-toolkit.md#a-body-that-fills-the-drawer-and-its-footer`.
84
+ A `useDrawer()` body injects what the component calling `useDrawer()` (in its setup) provides —
85
+ never pass a context as a prop to provide it again in the body.
86
+ A `sidePanel` target (non-modal, docked on the right) still works, but is meant for global,
87
+ page-independent panels such as an assistant dock —
58
88
  `docs/04.components/08.tables/8.row-drawers.md#side-panel`.
59
89
  A button downloading a file the user's session may read (`/api/...`) takes a `download`
60
90
  target, `{ url, filename? }`, with the same `{{row.field}}` / `{{query.X}}` tokens — never a Vue
@@ -76,9 +106,15 @@ row's ⋯ button — `docs/04.components/08.tables/0.index.md#pinned-rows`.
76
106
  - `.watch(FormEvents.SUBMIT_SUCCESS, BlockFunctions.REFRESH_PAGE)` makes the page's data blocks
77
107
  (table views included) re-fetch after a form saves.
78
108
  - `periodScope` binds a card, chart, list block, `Meter` or `TableView.fromSource` to a
79
- `PeriodSelector`, which keeps its period in the page URL (`?<id>.period=`).
80
- - `realtimeTopic` re-fetches a chart, list block, `Meter`, `Tab` badges or `TableView` on each
81
- `PublishMessage`.
109
+ `PeriodSelector`, which keeps its period in the page URL (`?<id>.period=`). Its selected
110
+ comparison drives the cards: `KpiCard` names it ("vs previous year", leave `compareLabel`
111
+ unset) and prints the route's `previousValue`; "No comparison" hides the change, the value
112
+ compared against and the label on `KpiCard`, `ChartCard` and `TopListCard`.
113
+ - `realtimeTopic` re-fetches a chart, a `KpiCard`, list block, `Meter`, `Tab` badges or `TableView` on each
114
+ `PublishMessage`. Publish tenant data with the tenant id (`{ tenantId }`): without it the message
115
+ is global and refetches the page in every workspace. A `TableView` needs no option: the DMS
116
+ publishes to every tenant for a table of the shared schema, and a platform (module) page receives
117
+ every tenant's changes.
82
118
 
83
119
  ### Live pages
84
120
 
@@ -86,7 +122,24 @@ row's ⋯ button — `docs/04.components/08.tables/0.index.md#pinned-rows`.
86
122
  options (`slowRateAfter: { period: "7d", maxRate: 60 }` for long periods, `rememberKey: "<module>"`
87
123
  to share the choice; `maxRate` is also the polling interval while the stream is down) and places a
88
124
  `LiveToggle()` (Live / Pause, L key; `variant: "pill"`, `missedLabel` for "N new requests").
89
- - `liveHold: true` keeps a table's rows while paused; `liveHold: { whileRowOpen, whileScrolled,
125
+ - A page with a period or live data starts with `pageToolbar({ leading, live, period })`
126
+ (`base/page-toolbar`): the same bar on every page — leading blocks, then "Updated N s ago",
127
+ Live / Pause, the window and the 1H / 24H / 7D / 30D presets, at one `size` and one height
128
+ (24 / 28 / 32 px, with live or not). `live: false` or `period: false` drops a control; without
129
+ `live` it shows the last read and a refresh button. A module's leading chip takes the toolbar's
130
+ height from `usePageToolbarSize().heightClass`. More than one comparison offered
131
+ (`comparisons: ["previous-period", "previous-year", "none"]`) draws a menu naming the compared window ("vs 16 – 17 Oct ▾", the range label then
132
+ without its "vs …"); a single one
133
+ stays on without a control. A `custom` preset shows as a calendar icon, named in full.
134
+ `live` / `period` also take `childId` (keep an id roles already grant, `live` / `period` by
135
+ default) and `meta` (roles-editor name), so a page never builds the toolbar by hand for that;
136
+ `transformOptions` changes a child's options at each registration (a setting read in
137
+ `construct()`) — never patch the toolbar's children afterwards;
138
+ an editor reads the default children from the block type's `initialChildren`.
139
+ A component may draw `<DmsPageToolbarBlock>` and `<DmsLiveToggleBlock>` itself, around controls
140
+ of its own: neither needs a `componentId` or `pageId` there.
141
+ - `liveHold: true` keeps a table's rows while paused, and in Direct while they are on screen (new
142
+ rows wait behind a "N new · Show" pill over the rows, counting the rows the table's query matches now beyond those held, through its count route); `liveHold: { whileRowOpen, whileScrolled,
90
143
  whileSearching, whileSorted }` also holds the table alone; `liveHold.missedLabel` words its "N new".
91
144
  Paused, an event counts as its `payload.ids` rows or its `payload.count`, so a module may batch
92
145
  publishes with `{ count }`. A hidden tab and `?live=0` pause the page too.
@@ -94,7 +147,18 @@ row's ⋯ button — `docs/04.components/08.tables/0.index.md#pinned-rows`.
94
147
  `setMissedDetail()`); one that reads its own data on a topic calls
95
148
  `usePageLiveRefresh(refresh, { realtimeTopic, idleRefresh })` — never a throttle of its own.
96
149
  `refreshPageBlocks()` (user actions) always reads; `refreshLivePageBlocks()` (timers, feeds) is
97
- held while paused.
150
+ held while paused. A table view's write (row/bulk action, its form saved) already
151
+ calls `refreshPageBlocks()`: KPIs and badges of the page need no wiring of their own.
152
+ - Making a block live: a built-in data block names a `realtimeTopic` (its builder registers the
153
+ topic on the page) and the backend calls `PublishMessage`; a table view follows its controller's
154
+ row events by itself. A block without a topic reads only on `refreshLivePageBlocks()`, the
155
+ polling while the stream is down and `refreshPageBlocks()` — no timer while the stream is up. A
156
+ custom component's own topic needs `RegisterPageTopic(pageId, topic)` in the backend.
157
+ - Pause (user, hold, hidden tab) freezes every block of the page at once: realtime events, row
158
+ events, page-topic events behind `useApiData`, `refreshLivePageBlocks()`, `idleRefresh`, the
159
+ stream-down polling and a component's `useApiData` `refreshInterval`. Each block that missed
160
+ something reads once on resume. User actions and inputs (period, filter, search, sort, page)
161
+ still read; notifications, presence and the menu are not the page and keep updating.
98
162
 
99
163
  ### Texts
100
164
 
@@ -103,6 +167,13 @@ row's ⋯ button — `docs/04.components/08.tables/0.index.md#pinned-rows`.
103
167
  KeyValueList / Banner / NavCardGrid / ActivityFeed / Meter / TopListCard item texts, ActivityFeed
104
168
  `params` (typed `count` for plurals), tab badges and layout banners (`RegisterLayoutBanner({ resolve })`
105
169
  → `{ text, actions }`) take it. Never format text server-side.
170
+ - A layout banner (`RegisterLayoutBanner`) shows on the application's pages only by default, never
171
+ inside `/modules` (modules page, module store, module pages). Give `scope:
172
+ LayoutBannerScope.EVERYWHERE` to a workspace-wide state (billing past due, suspended workspace,
173
+ maintenance) that must follow the user into modules; leave the default for anything about the
174
+ application's own data.
175
+ - A route that must word a text reads the request's `x-content-language` header (the language on
176
+ screen), never `users.language` (the profile's, for mails).
106
177
  - A status the backend computes (health, outage) is a `Banner({ fetchUrl })`: the route answers a
107
178
  `BannerContent` (its buttons may run `api` / modal / quick-action targets with `confirm`) or
108
179
  `null` for no banner.
@@ -110,13 +181,20 @@ row's ⋯ button — `docs/04.components/08.tables/0.index.md#pinned-rows`.
110
181
  ### Blocks
111
182
 
112
183
  - `StatGroup` details and `NavCardGrid` card titles take up to two lines before their ellipsis.
184
+ - `StatGroup({ columnsFrom: "md" })` (`DmsStatGroup columns-from`) sets all `columns` side by side from
185
+ that group width — never a class forcing the group's grid (`[&>div]:grid-cols-*`).
113
186
  - `ActivityFeed`: `wrap: true` for entries read whole (leave `fillHeight` off), `visibleItems: N`
114
187
  for a "Show N more" fold.
115
188
  - `KeyValueList` rows take `copy: true` or `copyValue` (the text to copy instead).
189
+ - A fetched list block draws `skeletonCount` placeholders until its first answer in a client
190
+ navigation (a server render waits for the data): set it to the usual number of items so the page
191
+ does not move when they land. A refresh keeps the items on screen until the new ones land.
116
192
  - `TopListCard({ actions })` puts link buttons in its head; `Tab({ fit: true })` keeps pill tabs to
117
193
  their labels' width; with `persistState` the URL names the open tab by its slot (`?tab=tester`).
118
194
  - A `ChartCard` plotting several series names them with `showSeriesLegend: true` (placed by the
119
195
  chart's `legendPosition`) — never a raw Apex `legend` in `rawOptions`.
196
+ - A chart route sends series names, categories (`x`) and point labels as `$` keys, bare chart or
197
+ `ChartCard` alike: the browser translates them, so donut slices are `{ x: "$app.plan.pro", y }`.
120
198
 
121
199
  ### Tables
122
200
 
@@ -128,13 +206,42 @@ row's ⋯ button — `docs/04.components/08.tables/0.index.md#pinned-rows`.
128
206
  `{ label, tone?, variant: "soft" | "outline" }`.
129
207
  - A `TableView` over a shared controller hides a column on one page with
130
208
  `columns: { hidden: ["workspace"] }` — no view just to hide it.
209
+ - Conditional fields on a `TableView`'s generated forms go in `forms: { new?, edit?, view? }`, each a
210
+ callback receiving the form's builder: `new: (form) => form.watch(FormEvents.FIELD_CHANGE, …)` —
211
+ they run in a drawer, a modal or a form page alike; no custom page just to add a watch.
212
+ - A table of more than a few columns that phones will read offers the cards and opens them there:
213
+ `displays: [{ id: "table" }, { id: "cards" }], mobileDisplay: "cards"`. Only a display the user
214
+ saved or the open view's own `display` overrides it: a `views.defaultView` that sets no display
215
+ still opens phones on the cards.
131
216
  - A table ordered by hand (`reorder: { field }`) moves rows on the grid and cards on the `cards`
132
217
  display alike; a `card.component` receives a `reorder` prop and `v-bind`s `reorder.handle`.
218
+ - A `cards` display whose cards need more room sets `card.minWidth` (a CSS length, `230px` by
219
+ default, the skeleton follows): cards stretch to fill the row, a short list's up to twice that.
220
+ - A `cards` display whose `card.component` is taller than the built-in card sets
221
+ `card.skeletonHeight` to its height (a CSS length): the loading placeholder takes it, so the
222
+ pagination footer does not drop when the cards land.
223
+ - A built-in card carries its row's actions top right (the grid row's buttons and … menu, same
224
+ rules and confirmations); a `card.component` opts in with `<component :is="rowActions" />`.
133
225
 
134
226
  ### Forms
135
227
 
136
- - A unit after a number field is `NumberType({ suffix: "$…days" })`; a character counter is
228
+ - A unit after a number field is `NumberType({ suffix: "$…days" })` (written in its table cells,
229
+ cards and read-only fields too); a character counter is
137
230
  `StringType({ maxLength, showCount: true })` — no custom `inputComponent` for either.
231
+ - A relation picker depending on another field of the form takes `filterBy: { field, from }`:
232
+ `RelationType({ dataApiController: SiteController, filterBy: { field: "customer", from: "customer" } })`
233
+ lists only the picked customer's sites, searches again when it changes and clears a pick made for
234
+ the previous one — no custom picker. `field` must be filterable on the related controller (it
235
+ throws at registration otherwise) — `docs/04.components/10.data-types.md`.
236
+ - A field whose value follows another one (stage → probability) takes a watch running
237
+ `FormFunctions.SET_FIELD_VALUE` with `{ targetField, valueMap: { won: 100, lost: 0 } }` (or a fixed
238
+ `value`) on `FIELD_CHANGE` keyed on the source `fieldId` — never a custom component. It writes on a
239
+ change only, never over the values a record opens with; a rule that must hold stays in the write
240
+ route — `docs/04.components/07.forms/0.index.md#setting-a-value-from-another-field`.
241
+ - A short number or text field (an estimate, a count, a code) takes `width: "compact"` on its
242
+ `NumberType` / `StringType` rather than filling the row; one bounded to 6 digits or a
243
+ `maxLength` of 16 or less is compact already, and `width: "full"` opts out. Phones keep the
244
+ full row.
138
245
  - A long sectioned `Form` groups its navigation with `FormSection.navGroup`, in the side list and
139
246
  the `sectionNav: "jump"` chips alike.
140
247
  - A module's own `inputComponent` submits its form, adds a validator and focuses a field with
@@ -145,7 +252,9 @@ row's ⋯ button — `docs/04.components/08.tables/0.index.md#pinned-rows`.
145
252
  ### Custom components
146
253
 
147
254
  - Name the record at the end of the breadcrumb with
148
- `usePageRecordLabel().setLabel(useDmsRoute().path, label)`.
255
+ `usePageRecordLabel().setLabel(useDmsRoute().path, label)`. A hidden detail page next to its
256
+ list (`project/:id` beside `projects`) declares `parent: ProjectsPage` in its menu options so
257
+ the breadcrumb shows the list before the record — `docs/02.building/04.navigation.md`.
149
258
  - Format through `useRegionalFormat()`; `useRegionalFormat({ locale: "format" })` writes in the
150
259
  locale of the DMS's texts (`en` → `en-GB`).
151
260
  - Bring a block into view with `scrollToBlock("tabs.statistics")` — never a DOM query.
@@ -63,7 +63,7 @@ export class SubmitRecipePage extends PageController("submit-recipe", { displayN
63
63
  ],
64
64
  submitUrl: "/api/recipes", // a route — often declared on this same class
65
65
  submitUrlMethod: HttpMethod.post,
66
- kind: "action", // sends something new each time; omit for a record form (save bar)
66
+ kind: "action", // sends something new each time; "create" for a form making a row; omit for a record form (save bar)
67
67
  });
68
68
  }
69
69
  ```
@@ -46,11 +46,33 @@ not survive orb updates.
46
46
  Only the project in `DMS_BACK_DIR`. The script records the pids it starts in
47
47
  `DMS_BACK_DIR/.antelope/dms-dev.pid`; without that file it matches only dev processes whose working
48
48
  directory or command line lies under `DMS_BACK_DIR`. Each process gets SIGTERM, then SIGKILL after
49
- `DMS_DEV_STOP_GRACE` seconds (default 10). Other projects' dev servers on the machine keep running,
49
+ `DMS_DEV_STOP_GRACE` seconds (default 10); on Windows, `taskkill /T /F` ([below](#windows)). Other projects' dev servers on the machine keep running,
50
50
  which is why the script replaces `pkill` by name (`pkill -9 node`, `pkill -f antelope-runner`).
51
51
 
52
+ `restart-back` and `restart-front` touch one server only: the pid the pid file records for it
53
+ (`backend` or `frontend`) and its process tree, or, when that pid is gone (the server crashed),
54
+ the processes of that server alone that the scan below finds. The other server keeps running.
55
+
52
56
  Run one `start` or `restart` at a time: a second concurrent call hits a port or lock conflict.
53
57
 
58
+ ## Windows
59
+
60
+ The script runs in Git Bash, MSYS2 or Cygwin. Their `ps` lists only their own processes and takes
61
+ no `-o`, and they have no `setsid`, while the dev servers run as native `node.exe` processes. So on
62
+ Windows the script:
63
+
64
+ - reads the Windows process table through PowerShell (`Get-CimInstance Win32_Process`: pid, parent
65
+ pid, creation time, command line), and maps the pids it recorded to Windows pids through
66
+ `/proc/<pid>/winpid`;
67
+ - follows parent pids for the tree (Windows has no process groups), counting only children created
68
+ after their parent, so that a process holding a dead parent's reused pid is left alone;
69
+ - stops each tree at once with `taskkill /T /F`: a console node process handles no SIGTERM, so
70
+ there is no grace period (`DMS_DEV_STOP_GRACE` is unused);
71
+ - without a pid file, matches only processes whose command line names `DMS_BACK_DIR`: a native
72
+ process exposes no working directory. Servers started through `pnpm` rarely do (only binaries
73
+ installed in the project's `node_modules` show its path), so start them with the script, which
74
+ records their pids.
75
+
54
76
  ## Pitfalls
55
77
 
56
78
  - **Zombie frontend processes.** The frontend CLI spawns children. `stop` signals the whole
@@ -20,5 +20,5 @@ Put secrets/per-deploy values in `envOverrides` (a framework feature); per-envir
20
20
  | --- | --- |
21
21
  | Pages, TableViews, the component builders | **dms-pages** (`docs/02.building/03.pages-and-components.md`, `docs/04.components/`) |
22
22
  | Auth on pages/routes, permissions, tenants | **dms-auth** (`docs/03.auth-and-tenancy/`) |
23
- | Hooks, notifications, realtime, replayable jobs, HTML render, export jobs | `docs/02.building/06.backend-services.md` |
23
+ | Hooks, notifications, realtime, replayable jobs, automation nodes, HTML render, export jobs | `docs/02.building/06.backend-services.md` |
24
24
  | Custom Vue in your `frontend-vue/` module | `docs/02.building/08.frontend-layer.md`, `docs/05.extending-the-dashboard/`, `docs/06.frontend-composables/` |
@@ -193,6 +193,8 @@ DMS_BACK_DIR=<resolved-path> bash "<skill-base-dir>/scripts/dms-dev.sh" restart
193
193
  | Command | Use when |
194
194
  | --- | --- |
195
195
  | `restart` | Hot reload cannot apply a change, or the state is unknown. |
196
+ | `restart-front` | The frontend crashed or hangs while the backend runs: only the frontend is stopped (if it runs) and started again. |
197
+ | `restart-back` | The backend crashed alone: only the backend is restarted, and the frontend keeps running. Run `restart-front` too if the frontend then serves stale layers. |
196
198
  | `start` | Nothing is running (fresh shell, after `stop`). |
197
199
  | `stop` | Before walking away, before destructive operations, on session cleanup. |
198
200
  | `status` | The user asks whether the DMS is running. |
@@ -202,7 +204,8 @@ The call returns once both servers print their ready line and reports the URLs t
202
204
  fails fast when a server exits early, otherwise after 180 s per server (`DMS_DEV_TIMEOUT`). The
203
205
  servers keep running detached. By default the script runs the project's `pnpm dev` and
204
206
  `pnpm frontend:dev` scripts (a project convention that expands to the two commands above);
205
- override them with `DMS_BACK_CMD` / `DMS_FRONT_CMD`.
207
+ override them with `DMS_BACK_CMD` / `DMS_FRONT_CMD`. It runs in bash on Linux and macOS, and in
208
+ Git Bash, MSYS2 or Cygwin on Windows ([DEV-SERVERS.md](DEV-SERVERS.md#windows)).
206
209
 
207
210
  When you hand off for manual testing, run `restart` yourself, wait for both ready lines, then tell
208
211
  the user what to click. Boot failures, stale frontends, a refused credential (401), stop scope and