@antelopejs/dms-automation 0.0.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 (305) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +95 -0
  3. package/dist/cluster/bus.d.ts +17 -0
  4. package/dist/cluster/bus.js +82 -0
  5. package/dist/cluster/bus.js.map +1 -0
  6. package/dist/cluster/instance.d.ts +1 -0
  7. package/dist/cluster/instance.js +6 -0
  8. package/dist/cluster/instance.js.map +1 -0
  9. package/dist/cluster/leader.d.ts +7 -0
  10. package/dist/cluster/leader.js +123 -0
  11. package/dist/cluster/leader.js.map +1 -0
  12. package/dist/cluster/mode.d.ts +3 -0
  13. package/dist/cluster/mode.js +25 -0
  14. package/dist/cluster/mode.js.map +1 -0
  15. package/dist/data/procedures-table.d.ts +66 -0
  16. package/dist/data/procedures-table.js +271 -0
  17. package/dist/data/procedures-table.js.map +1 -0
  18. package/dist/data/runs-table.d.ts +23 -0
  19. package/dist/data/runs-table.js +125 -0
  20. package/dist/data/runs-table.js.map +1 -0
  21. package/dist/db/index.d.ts +7 -0
  22. package/dist/db/index.js +24 -0
  23. package/dist/db/index.js.map +1 -0
  24. package/dist/db/models/automation_template.model.d.ts +58 -0
  25. package/dist/db/models/automation_template.model.js +134 -0
  26. package/dist/db/models/automation_template.model.js.map +1 -0
  27. package/dist/db/models/index.d.ts +3 -0
  28. package/dist/db/models/index.js +20 -0
  29. package/dist/db/models/index.js.map +1 -0
  30. package/dist/db/models/procedure.model.d.ts +25 -0
  31. package/dist/db/models/procedure.model.js +28 -0
  32. package/dist/db/models/procedure.model.js.map +1 -0
  33. package/dist/db/models/procedure_run.model.d.ts +34 -0
  34. package/dist/db/models/procedure_run.model.js +39 -0
  35. package/dist/db/models/procedure_run.model.js.map +1 -0
  36. package/dist/db/models/stats.model.d.ts +73 -0
  37. package/dist/db/models/stats.model.js +267 -0
  38. package/dist/db/models/stats.model.js.map +1 -0
  39. package/dist/db/models/type_config.model.d.ts +47 -0
  40. package/dist/db/models/type_config.model.js +51 -0
  41. package/dist/db/models/type_config.model.js.map +1 -0
  42. package/dist/db/tables/action_type_config.table.d.ts +7 -0
  43. package/dist/db/tables/action_type_config.table.js +35 -0
  44. package/dist/db/tables/action_type_config.table.js.map +1 -0
  45. package/dist/db/tables/automation_template.table.d.ts +11 -0
  46. package/dist/db/tables/automation_template.table.js +52 -0
  47. package/dist/db/tables/automation_template.table.js.map +1 -0
  48. package/dist/db/tables/index.d.ts +5 -0
  49. package/dist/db/tables/index.js +22 -0
  50. package/dist/db/tables/index.js.map +1 -0
  51. package/dist/db/tables/procedure.table.d.ts +10 -0
  52. package/dist/db/tables/procedure.table.js +48 -0
  53. package/dist/db/tables/procedure.table.js.map +1 -0
  54. package/dist/db/tables/procedure_run.table.d.ts +13 -0
  55. package/dist/db/tables/procedure_run.table.js +57 -0
  56. package/dist/db/tables/procedure_run.table.js.map +1 -0
  57. package/dist/db/tables/trigger_type_config.table.d.ts +7 -0
  58. package/dist/db/tables/trigger_type_config.table.js +35 -0
  59. package/dist/db/tables/trigger_type_config.table.js.map +1 -0
  60. package/dist/implementations/dms-automation/index.d.ts +19 -0
  61. package/dist/implementations/dms-automation/index.js +50 -0
  62. package/dist/implementations/dms-automation/index.js.map +1 -0
  63. package/dist/index.d.ts +14 -0
  64. package/dist/index.js +121 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/pages/builder.d.ts +10 -0
  67. package/dist/pages/builder.js +28 -0
  68. package/dist/pages/builder.js.map +1 -0
  69. package/dist/pages/category.d.ts +1 -0
  70. package/dist/pages/category.js +10 -0
  71. package/dist/pages/category.js.map +1 -0
  72. package/dist/pages/index.d.ts +6 -0
  73. package/dist/pages/index.js +9 -0
  74. package/dist/pages/index.js.map +1 -0
  75. package/dist/pages/library.d.ts +10 -0
  76. package/dist/pages/library.js +28 -0
  77. package/dist/pages/library.js.map +1 -0
  78. package/dist/pages/overview.d.ts +10 -0
  79. package/dist/pages/overview.js +28 -0
  80. package/dist/pages/overview.js.map +1 -0
  81. package/dist/pages/procedures.d.ts +10 -0
  82. package/dist/pages/procedures.js +72 -0
  83. package/dist/pages/procedures.js.map +1 -0
  84. package/dist/pages/runs.d.ts +11 -0
  85. package/dist/pages/runs.js +61 -0
  86. package/dist/pages/runs.js.map +1 -0
  87. package/dist/routes/action-types.d.ts +23 -0
  88. package/dist/routes/action-types.js +80 -0
  89. package/dist/routes/action-types.js.map +1 -0
  90. package/dist/routes/data-node-types.d.ts +6 -0
  91. package/dist/routes/data-node-types.js +36 -0
  92. package/dist/routes/data-node-types.js.map +1 -0
  93. package/dist/routes/graphSchema.d.ts +92 -0
  94. package/dist/routes/graphSchema.js +60 -0
  95. package/dist/routes/graphSchema.js.map +1 -0
  96. package/dist/routes/index.d.ts +8 -0
  97. package/dist/routes/index.js +11 -0
  98. package/dist/routes/index.js.map +1 -0
  99. package/dist/routes/node-kinds.d.ts +9 -0
  100. package/dist/routes/node-kinds.js +35 -0
  101. package/dist/routes/node-kinds.js.map +1 -0
  102. package/dist/routes/procedures.d.ts +23 -0
  103. package/dist/routes/procedures.js +181 -0
  104. package/dist/routes/procedures.js.map +1 -0
  105. package/dist/routes/runs.d.ts +7 -0
  106. package/dist/routes/runs.js +81 -0
  107. package/dist/routes/runs.js.map +1 -0
  108. package/dist/routes/stats.d.ts +6 -0
  109. package/dist/routes/stats.js +35 -0
  110. package/dist/routes/stats.js.map +1 -0
  111. package/dist/routes/templates.d.ts +20 -0
  112. package/dist/routes/templates.js +243 -0
  113. package/dist/routes/templates.js.map +1 -0
  114. package/dist/routes/trigger-types.d.ts +24 -0
  115. package/dist/routes/trigger-types.js +80 -0
  116. package/dist/routes/trigger-types.js.map +1 -0
  117. package/dist/routes/typeConfigShared.d.ts +22 -0
  118. package/dist/routes/typeConfigShared.js +37 -0
  119. package/dist/routes/typeConfigShared.js.map +1 -0
  120. package/dist/runtime/builtins/actions/http.d.ts +13 -0
  121. package/dist/runtime/builtins/actions/http.js +47 -0
  122. package/dist/runtime/builtins/actions/http.js.map +1 -0
  123. package/dist/runtime/builtins/actions/index.d.ts +2 -0
  124. package/dist/runtime/builtins/actions/index.js +7 -0
  125. package/dist/runtime/builtins/actions/index.js.map +1 -0
  126. package/dist/runtime/builtins/actions/log.d.ts +13 -0
  127. package/dist/runtime/builtins/actions/log.js +49 -0
  128. package/dist/runtime/builtins/actions/log.js.map +1 -0
  129. package/dist/runtime/builtins/data/boolean.d.ts +2 -0
  130. package/dist/runtime/builtins/data/boolean.js +39 -0
  131. package/dist/runtime/builtins/data/boolean.js.map +1 -0
  132. package/dist/runtime/builtins/data/compare.d.ts +2 -0
  133. package/dist/runtime/builtins/data/compare.js +29 -0
  134. package/dist/runtime/builtins/data/compare.js.map +1 -0
  135. package/dist/runtime/builtins/data/constant.d.ts +2 -0
  136. package/dist/runtime/builtins/data/constant.js +29 -0
  137. package/dist/runtime/builtins/data/constant.js.map +1 -0
  138. package/dist/runtime/builtins/data/index.d.ts +2 -0
  139. package/dist/runtime/builtins/data/index.js +20 -0
  140. package/dist/runtime/builtins/data/index.js.map +1 -0
  141. package/dist/runtime/builtins/data/json.d.ts +2 -0
  142. package/dist/runtime/builtins/data/json.js +46 -0
  143. package/dist/runtime/builtins/data/json.js.map +1 -0
  144. package/dist/runtime/builtins/data/math.d.ts +2 -0
  145. package/dist/runtime/builtins/data/math.js +53 -0
  146. package/dist/runtime/builtins/data/math.js.map +1 -0
  147. package/dist/runtime/builtins/data/object.d.ts +2 -0
  148. package/dist/runtime/builtins/data/object.js +121 -0
  149. package/dist/runtime/builtins/data/object.js.map +1 -0
  150. package/dist/runtime/builtins/data/string.d.ts +2 -0
  151. package/dist/runtime/builtins/data/string.js +143 -0
  152. package/dist/runtime/builtins/data/string.js.map +1 -0
  153. package/dist/runtime/builtins/index.d.ts +17 -0
  154. package/dist/runtime/builtins/index.js +24 -0
  155. package/dist/runtime/builtins/index.js.map +1 -0
  156. package/dist/runtime/builtins/nodes/action.d.ts +1 -0
  157. package/dist/runtime/builtins/nodes/action.js +36 -0
  158. package/dist/runtime/builtins/nodes/action.js.map +1 -0
  159. package/dist/runtime/builtins/nodes/data.d.ts +1 -0
  160. package/dist/runtime/builtins/nodes/data.js +28 -0
  161. package/dist/runtime/builtins/nodes/data.js.map +1 -0
  162. package/dist/runtime/builtins/nodes/delay.d.ts +1 -0
  163. package/dist/runtime/builtins/nodes/delay.js +41 -0
  164. package/dist/runtime/builtins/nodes/delay.js.map +1 -0
  165. package/dist/runtime/builtins/nodes/forRange.d.ts +1 -0
  166. package/dist/runtime/builtins/nodes/forRange.js +52 -0
  167. package/dist/runtime/builtins/nodes/forRange.js.map +1 -0
  168. package/dist/runtime/builtins/nodes/foreach.d.ts +1 -0
  169. package/dist/runtime/builtins/nodes/foreach.js +31 -0
  170. package/dist/runtime/builtins/nodes/foreach.js.map +1 -0
  171. package/dist/runtime/builtins/nodes/groupInput.d.ts +1 -0
  172. package/dist/runtime/builtins/nodes/groupInput.js +11 -0
  173. package/dist/runtime/builtins/nodes/groupInput.js.map +1 -0
  174. package/dist/runtime/builtins/nodes/groupOutput.d.ts +1 -0
  175. package/dist/runtime/builtins/nodes/groupOutput.js +11 -0
  176. package/dist/runtime/builtins/nodes/groupOutput.js.map +1 -0
  177. package/dist/runtime/builtins/nodes/if.d.ts +1 -0
  178. package/dist/runtime/builtins/nodes/if.js +23 -0
  179. package/dist/runtime/builtins/nodes/if.js.map +1 -0
  180. package/dist/runtime/builtins/nodes/parallel.d.ts +1 -0
  181. package/dist/runtime/builtins/nodes/parallel.js +34 -0
  182. package/dist/runtime/builtins/nodes/parallel.js.map +1 -0
  183. package/dist/runtime/builtins/nodes/retry.d.ts +1 -0
  184. package/dist/runtime/builtins/nodes/retry.js +46 -0
  185. package/dist/runtime/builtins/nodes/retry.js.map +1 -0
  186. package/dist/runtime/builtins/nodes/setVariable.d.ts +1 -0
  187. package/dist/runtime/builtins/nodes/setVariable.js +31 -0
  188. package/dist/runtime/builtins/nodes/setVariable.js.map +1 -0
  189. package/dist/runtime/builtins/nodes/switch.d.ts +1 -0
  190. package/dist/runtime/builtins/nodes/switch.js +33 -0
  191. package/dist/runtime/builtins/nodes/switch.js.map +1 -0
  192. package/dist/runtime/builtins/nodes/trigger.d.ts +1 -0
  193. package/dist/runtime/builtins/nodes/trigger.js +25 -0
  194. package/dist/runtime/builtins/nodes/trigger.js.map +1 -0
  195. package/dist/runtime/builtins/nodes/tryCatch.d.ts +1 -0
  196. package/dist/runtime/builtins/nodes/tryCatch.js +35 -0
  197. package/dist/runtime/builtins/nodes/tryCatch.js.map +1 -0
  198. package/dist/runtime/builtins/triggers/cron.d.ts +9 -0
  199. package/dist/runtime/builtins/triggers/cron.js +52 -0
  200. package/dist/runtime/builtins/triggers/cron.js.map +1 -0
  201. package/dist/runtime/builtins/triggers/index.d.ts +2 -0
  202. package/dist/runtime/builtins/triggers/index.js +12 -0
  203. package/dist/runtime/builtins/triggers/index.js.map +1 -0
  204. package/dist/runtime/builtins/triggers/manual.d.ts +3 -0
  205. package/dist/runtime/builtins/triggers/manual.js +20 -0
  206. package/dist/runtime/builtins/triggers/manual.js.map +1 -0
  207. package/dist/runtime/builtins/triggers/webhook.d.ts +14 -0
  208. package/dist/runtime/builtins/triggers/webhook.js +90 -0
  209. package/dist/runtime/builtins/triggers/webhook.js.map +1 -0
  210. package/dist/runtime/executionResult.d.ts +6 -0
  211. package/dist/runtime/executionResult.js +3 -0
  212. package/dist/runtime/executionResult.js.map +1 -0
  213. package/dist/runtime/executor.d.ts +9 -0
  214. package/dist/runtime/executor.js +300 -0
  215. package/dist/runtime/executor.js.map +1 -0
  216. package/dist/runtime/flatten.d.ts +3 -0
  217. package/dist/runtime/flatten.js +188 -0
  218. package/dist/runtime/flatten.js.map +1 -0
  219. package/dist/runtime/nodeKinds.d.ts +64 -0
  220. package/dist/runtime/nodeKinds.js +43 -0
  221. package/dist/runtime/nodeKinds.js.map +1 -0
  222. package/dist/runtime/reconcileDiff.d.ts +9 -0
  223. package/dist/runtime/reconcileDiff.js +26 -0
  224. package/dist/runtime/reconcileDiff.js.map +1 -0
  225. package/dist/runtime/registry.d.ts +19 -0
  226. package/dist/runtime/registry.js +47 -0
  227. package/dist/runtime/registry.js.map +1 -0
  228. package/dist/runtime/resolveInputs.d.ts +14 -0
  229. package/dist/runtime/resolveInputs.js +64 -0
  230. package/dist/runtime/resolveInputs.js.map +1 -0
  231. package/dist/runtime/resolveSubgraph.d.ts +7 -0
  232. package/dist/runtime/resolveSubgraph.js +15 -0
  233. package/dist/runtime/resolveSubgraph.js.map +1 -0
  234. package/dist/runtime/subscriptions.d.ts +16 -0
  235. package/dist/runtime/subscriptions.js +419 -0
  236. package/dist/runtime/subscriptions.js.map +1 -0
  237. package/dist/runtime/subwalk.d.ts +17 -0
  238. package/dist/runtime/subwalk.js +74 -0
  239. package/dist/runtime/subwalk.js.map +1 -0
  240. package/dist/runtime/topo.d.ts +3 -0
  241. package/dist/runtime/topo.js +75 -0
  242. package/dist/runtime/topo.js.map +1 -0
  243. package/dist/runtime/typeOrigin.d.ts +7 -0
  244. package/dist/runtime/typeOrigin.js +24 -0
  245. package/dist/runtime/typeOrigin.js.map +1 -0
  246. package/dist/runtime/validate.d.ts +10 -0
  247. package/dist/runtime/validate.js +185 -0
  248. package/dist/runtime/validate.js.map +1 -0
  249. package/dist/runtime/validateTemplate.d.ts +3 -0
  250. package/dist/runtime/validateTemplate.js +54 -0
  251. package/dist/runtime/validateTemplate.js.map +1 -0
  252. package/dist/types/constants.d.ts +6 -0
  253. package/dist/types/constants.js +10 -0
  254. package/dist/types/constants.js.map +1 -0
  255. package/dist/types/graph.d.ts +51 -0
  256. package/dist/types/graph.js +23 -0
  257. package/dist/types/graph.js.map +1 -0
  258. package/dist/types/pagination.d.ts +8 -0
  259. package/dist/types/pagination.js +3 -0
  260. package/dist/types/pagination.js.map +1 -0
  261. package/dist/types/runLog.d.ts +26 -0
  262. package/dist/types/runLog.js +3 -0
  263. package/dist/types/runLog.js.map +1 -0
  264. package/frontend-vue/.prettierignore +4 -0
  265. package/frontend-vue/app/components/Actions.vue +141 -0
  266. package/frontend-vue/app/components/Catalog/TypeConfigForm.vue +166 -0
  267. package/frontend-vue/app/components/Catalog/TypeList.vue +198 -0
  268. package/frontend-vue/app/components/DataNodes.vue +259 -0
  269. package/frontend-vue/app/components/Editor/BuilderPalette.vue +234 -0
  270. package/frontend-vue/app/components/Editor/GraphCanvas.vue +994 -0
  271. package/frontend-vue/app/components/Editor/GroupPortEditor.vue +289 -0
  272. package/frontend-vue/app/components/Editor/JsonField.vue +63 -0
  273. package/frontend-vue/app/components/Editor/NodeInspector.vue +356 -0
  274. package/frontend-vue/app/components/Editor/ProcedureList.vue +156 -0
  275. package/frontend-vue/app/components/Editor/RunsDrawer.vue +312 -0
  276. package/frontend-vue/app/components/Editor/WiredField.vue +42 -0
  277. package/frontend-vue/app/components/Editor/nodes/GenericNode.vue +161 -0
  278. package/frontend-vue/app/components/Editor/nodes/GroupInputNode.vue +19 -0
  279. package/frontend-vue/app/components/Editor/nodes/GroupNode.vue +200 -0
  280. package/frontend-vue/app/components/Editor/nodes/GroupOutputNode.vue +19 -0
  281. package/frontend-vue/app/components/Editor/nodes/GroupSentinel.vue +161 -0
  282. package/frontend-vue/app/components/Editor/nodes/styles.ts +53 -0
  283. package/frontend-vue/app/components/Editor.vue +2504 -0
  284. package/frontend-vue/app/components/Library.vue +67 -0
  285. package/frontend-vue/app/components/Overview.vue +503 -0
  286. package/frontend-vue/app/components/RunDetailDrawer.vue +165 -0
  287. package/frontend-vue/app/components/RunFireTree.vue +170 -0
  288. package/frontend-vue/app/components/RunTraceDrawer.vue +59 -0
  289. package/frontend-vue/app/components/RunsStats.vue +126 -0
  290. package/frontend-vue/app/components/RunsTimelineDisplay.vue +287 -0
  291. package/frontend-vue/app/components/Templates.vue +663 -0
  292. package/frontend-vue/app/components/Triggers.vue +137 -0
  293. package/frontend-vue/app/composables/useAutomationNodeKinds.ts +48 -0
  294. package/frontend-vue/app/composables/useAutomationRuns.ts +75 -0
  295. package/frontend-vue/app/composables/useGraphConnect.ts +542 -0
  296. package/frontend-vue/app/plugins/runs-timeline-display.client.ts +16 -0
  297. package/frontend-vue/app/utils/automation.ts +51 -0
  298. package/frontend-vue/dms.frontend.ts +26 -0
  299. package/frontend-vue/eslint.config.mjs +24 -0
  300. package/frontend-vue/i18n/locales/dms-automation-en-GB.json +369 -0
  301. package/frontend-vue/i18n/locales/dms-automation-fr-FR.json +369 -0
  302. package/frontend-vue/package.json +33 -0
  303. package/frontend-vue/pnpm-lock.yaml +6483 -0
  304. package/frontend-vue/pnpm-workspace.yaml +2 -0
  305. package/package.json +81 -0
@@ -0,0 +1,67 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue'
3
+
4
+ // The Library consolidates the former Triggers / Actions / Data Nodes /
5
+ // Templates pages into one tabbed page (per autov2-library). Each tab mounts
6
+ // the existing catalog component in `embedded` mode so it drops its own
7
+ // page-head — the Library owns the single page-head above the tabs.
8
+ type Tab = 'triggers' | 'actions' | 'dataNodes' | 'templates'
9
+
10
+ const { t } = useI18n()
11
+ const tab = ref<Tab>('triggers')
12
+
13
+ const tabs = computed<
14
+ ReadonlyArray<{ value: Tab; label: string; icon: string }>
15
+ >(() => [
16
+ { value: 'triggers', label: t('dms_automation.library.tabs.triggers'), icon: 'i-ph-lightning' },
17
+ { value: 'actions', label: t('dms_automation.library.tabs.actions'), icon: 'i-ph-gear' },
18
+ { value: 'dataNodes', label: t('dms_automation.library.tabs.dataNodes'), icon: 'i-ph-cube' },
19
+ { value: 'templates', label: t('dms_automation.library.tabs.templates'), icon: 'i-ph-package' },
20
+ ])
21
+ </script>
22
+
23
+ <template>
24
+ <!--
25
+ Title / icon / description come from the page's native dms header
26
+ (src/pages/library.ts), so this component starts at its tabs.
27
+ -->
28
+ <div class="flex flex-col gap-5">
29
+ <!-- Tabs -->
30
+ <div class="border-b border-default">
31
+ <div class="flex flex-wrap items-center gap-1">
32
+ <button
33
+ v-for="item in tabs"
34
+ :key="item.value"
35
+ type="button"
36
+ class="-mb-px flex items-center gap-2 border-b-2 px-3 py-2.5 text-sm font-medium transition-colors"
37
+ :class="
38
+ tab === item.value
39
+ ? 'border-primary text-primary'
40
+ : 'border-transparent text-muted hover:text-highlighted'
41
+ "
42
+ @click="tab = item.value"
43
+ >
44
+ <UIcon :name="item.icon" class="size-4" />
45
+ {{ item.label }}
46
+ </button>
47
+ </div>
48
+ </div>
49
+
50
+ <!-- Panels (catalog components, page-head suppressed via `embedded`).
51
+ Wrapped in Suspense because the catalog components use top-level
52
+ await (async setup); the local boundary also gives a skeleton while
53
+ a freshly-switched tab loads its data. -->
54
+ <Suspense>
55
+ <DmsAutomationTriggers v-if="tab === 'triggers'" embedded />
56
+ <DmsAutomationActions v-else-if="tab === 'actions'" embedded />
57
+ <DmsAutomationDataNodes v-else-if="tab === 'dataNodes'" embedded />
58
+ <DmsAutomationTemplates v-else embedded />
59
+ <template #fallback>
60
+ <div class="flex flex-col gap-3">
61
+ <USkeleton class="h-9 w-64" />
62
+ <USkeleton class="h-64 w-full" />
63
+ </div>
64
+ </template>
65
+ </Suspense>
66
+ </div>
67
+ </template>
@@ -0,0 +1,503 @@
1
+ <script setup lang="ts">
2
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
3
+
4
+ interface HealthBucket {
5
+ total: number
6
+ ok: number
7
+ failed: number
8
+ successRate: number
9
+ avgDurationMs: number
10
+ p95DurationMs: number
11
+ }
12
+
13
+ interface StatsSnapshot {
14
+ counts: {
15
+ procedures: { total: number; enabled: number; disabled: number }
16
+ triggerTypes: number
17
+ actionTypes: number
18
+ dataNodeTypes: number
19
+ templates: number
20
+ }
21
+ health: {
22
+ '24h': HealthBucket
23
+ '7d': HealthBucket
24
+ }
25
+ runsPerDay7d: Array<{ day: string; ok: number; failed: number }>
26
+ recentRuns: Array<{
27
+ runId: string
28
+ procedureId: string
29
+ procedureName: string
30
+ status: string
31
+ startedAt: string
32
+ durationMs: number | null
33
+ }>
34
+ needsAttention: Array<{
35
+ procedureId: string
36
+ name: string
37
+ kind: 'paused' | 'failing'
38
+ failed: number
39
+ lastRunAt: string | null
40
+ }>
41
+ }
42
+
43
+ const { $authFetch } = useAuthFetch()
44
+ const { t } = useI18n()
45
+
46
+ const data = ref<StatsSnapshot | null>(null)
47
+ const loading = ref(false)
48
+ const loadError = ref<string | null>(null)
49
+ const autoPoll = ref(false)
50
+
51
+ async function refresh() {
52
+ loading.value = true
53
+ loadError.value = null
54
+ try {
55
+ data.value = await $authFetch<StatsSnapshot>('/api/automation/stats')
56
+ } catch (e) {
57
+ loadError.value = (e as Error).message ?? 'Failed to load stats'
58
+ } finally {
59
+ loading.value = false
60
+ }
61
+ }
62
+
63
+ let timer: ReturnType<typeof setInterval> | null = null
64
+ function clearTimer() {
65
+ if (timer) {
66
+ clearInterval(timer)
67
+ timer = null
68
+ }
69
+ }
70
+ watch(autoPoll, (v) => {
71
+ clearTimer()
72
+ if (v) timer = setInterval(refresh, 30_000)
73
+ })
74
+ onMounted(refresh)
75
+ onBeforeUnmount(clearTimer)
76
+
77
+ // ---- Derived metrics ----
78
+ const h24 = computed<HealthBucket | null>(() => data.value?.health['24h'] ?? null)
79
+
80
+ const runsToday = computed<number>(() => {
81
+ const days = data.value?.runsPerDay7d ?? []
82
+ const last = days[days.length - 1]
83
+ return last ? last.ok + last.failed : 0
84
+ })
85
+
86
+ const runs7dTotal = computed<number>(() =>
87
+ (data.value?.runsPerDay7d ?? []).reduce((s, d) => s + d.ok + d.failed, 0),
88
+ )
89
+
90
+ // Four execution-oriented stat cards (rendered as shared DmsKpiCard, static
91
+ // mode). `title` carries the `$`-prefix the card's processI18n() expects.
92
+ const statCards = computed(() => {
93
+ if (!data.value) return []
94
+ const c = data.value.counts
95
+ const rate = data.value.health['7d'].successRate
96
+ return [
97
+ {
98
+ key: 'procedures',
99
+ title: '$dms_automation.overview.stats.procedures',
100
+ value: c.procedures.total,
101
+ icon: 'i-ph-flow-arrow',
102
+ format: 'number' as const,
103
+ },
104
+ {
105
+ key: 'active',
106
+ title: '$dms_automation.overview.stats.active',
107
+ value: c.procedures.enabled,
108
+ icon: 'i-ph-check-circle',
109
+ format: 'number' as const,
110
+ },
111
+ {
112
+ key: 'runsToday',
113
+ title: '$dms_automation.overview.stats.runsToday',
114
+ value: runsToday.value,
115
+ icon: 'i-ph-activity',
116
+ format: 'compact' as const,
117
+ },
118
+ {
119
+ // DmsKpiCard's `percent` format expects a 0–100 figure (it appends %
120
+ // without re-scaling), so pass the rate as a percentage, not a 0–1 ratio.
121
+ key: 'successRate',
122
+ title: '$dms_automation.overview.stats.successRate',
123
+ value: data.value.health['7d'].total ? rate * 100 : 0,
124
+ icon: 'i-ph-trend-up',
125
+ format: 'percent' as const,
126
+ },
127
+ ]
128
+ })
129
+
130
+ // Execution health is "Operational" while the last 24h had no failures, else
131
+ // "Needs attention" — drives the badge colour next to the panel title.
132
+ const healthOperational = computed<boolean>(() => (h24.value?.failed ?? 0) === 0)
133
+
134
+ const healthMetrics = computed(() => {
135
+ const h = h24.value
136
+ if (!h) return []
137
+ return [
138
+ { label: t('dms_automation.overview.execHealth.avgDuration'), value: formatDuration(h.avgDurationMs) },
139
+ { label: t('dms_automation.overview.execHealth.p95Duration'), value: formatDuration(h.p95DurationMs) },
140
+ { label: t('dms_automation.overview.execHealth.failed24h'), value: String(h.failed) },
141
+ { label: t('dms_automation.overview.execHealth.total24h'), value: String(h.total) },
142
+ { label: t('dms_automation.overview.execHealth.successRate'), value: formatPercent(h.successRate) },
143
+ ]
144
+ })
145
+
146
+ // ---- Chart (7-day runs, OK vs Failed) ----
147
+ // ApexCharts (via DmsChart) doesn't read CSS tokens, so series colours are
148
+ // pinned hex from the DMS chart palette.
149
+ const C_OK = '#10b981'
150
+ const C_FAILED = '#ef4444'
151
+
152
+ function dayLabel(iso: string): string {
153
+ const parts = iso.split('-')
154
+ return parts.length === 3 ? `${parts[1]}-${parts[2]}` : iso
155
+ }
156
+
157
+ const hasRuns7d = computed(() =>
158
+ (data.value?.runsPerDay7d ?? []).some((d) => d.ok + d.failed > 0),
159
+ )
160
+
161
+ const chartSeries = computed(() => {
162
+ const days = data.value?.runsPerDay7d ?? []
163
+ return [
164
+ {
165
+ name: t('dms_automation.overview.chart.legendOk'),
166
+ color: C_OK,
167
+ data: days.map((d) => ({ x: dayLabel(d.day), y: d.ok })),
168
+ },
169
+ {
170
+ name: t('dms_automation.overview.chart.legendFailed'),
171
+ color: C_FAILED,
172
+ data: days.map((d) => ({ x: dayLabel(d.day), y: d.failed })),
173
+ },
174
+ ]
175
+ })
176
+
177
+ // ---- Formatting ----
178
+ function formatDuration(ms: number): string {
179
+ if (!ms || !Number.isFinite(ms)) return '0ms'
180
+ if (ms < 1000) return `${Math.round(ms)}ms`
181
+ const s = ms / 1000
182
+ if (s < 60) return `${s.toFixed(1)}s`
183
+ const m = Math.floor(s / 60)
184
+ const rem = Math.round(s % 60)
185
+ return `${m}m ${rem}s`
186
+ }
187
+
188
+ function formatPercent(rate: number): string {
189
+ if (!Number.isFinite(rate)) return '—'
190
+ return `${(rate * 100).toFixed(1)}%`
191
+ }
192
+
193
+ function timeAgo(iso: string): string {
194
+ const then = new Date(iso).getTime()
195
+ if (!Number.isFinite(then)) return ''
196
+ const sec = Math.max(0, Math.round((Date.now() - then) / 1000))
197
+ if (sec < 60) return t('dms_automation.overview.timeAgo.now')
198
+ const min = Math.floor(sec / 60)
199
+ if (min < 60) return t('dms_automation.overview.timeAgo.min', { n: min })
200
+ const hr = Math.floor(min / 60)
201
+ if (hr < 24) return t('dms_automation.overview.timeAgo.hour', { n: hr })
202
+ const day = Math.floor(hr / 24)
203
+ return t('dms_automation.overview.timeAgo.day', { n: day })
204
+ }
205
+
206
+ // ---- Links (note: module routes are /modules/* plural) ----
207
+ function procedureLink(procedureId: string): string {
208
+ return `/modules/automation/builder?selected=${encodeURIComponent(procedureId)}`
209
+ }
210
+ const runsAllLink = '/modules/automation/runs'
211
+ const proceduresLink = '/modules/automation/procedures'
212
+
213
+ function runIsFailed(status: string): boolean {
214
+ return status === 'failed'
215
+ }
216
+ </script>
217
+
218
+ <template>
219
+ <!--
220
+ Title / icon / description come from the page's native dms header
221
+ (src/pages/overview.ts) — only the page actions live here.
222
+ -->
223
+ <div class="flex flex-col gap-5">
224
+ <section class="flex flex-wrap items-center gap-4">
225
+ <div class="ml-auto flex flex-wrap items-center gap-2">
226
+ <label class="mr-1 flex items-center gap-2 text-sm text-muted">
227
+ <USwitch v-model="autoPoll" />
228
+ {{ $t('dms_automation.overview.autoPoll') }}
229
+ </label>
230
+ <UButton
231
+ color="neutral"
232
+ variant="ghost"
233
+ icon="i-ph-arrows-clockwise"
234
+ :loading="loading"
235
+ :aria-label="$t('dms_automation.overview.refresh')"
236
+ @click="refresh"
237
+ />
238
+ <UButton
239
+ :to="proceduresLink"
240
+ color="neutral"
241
+ variant="outline"
242
+ icon="i-ph-flow-arrow"
243
+ >
244
+ {{ $t('dms_automation.overview.head.procedures') }}
245
+ </UButton>
246
+ <UButton :to="proceduresLink" color="primary" icon="i-ph-plus">
247
+ {{ $t('dms_automation.overview.head.newProcedure') }}
248
+ </UButton>
249
+ </div>
250
+ </section>
251
+
252
+ <UAlert
253
+ v-if="loadError"
254
+ color="error"
255
+ variant="subtle"
256
+ icon="i-ph-warning"
257
+ :title="$t('dms_automation.overview.loadError')"
258
+ :description="loadError"
259
+ />
260
+
261
+ <!-- Stat cards -->
262
+ <div class="grid grid-cols-2 gap-4 lg:grid-cols-4">
263
+ <template v-if="data">
264
+ <DmsKpiCard
265
+ v-for="card in statCards"
266
+ :key="card.key"
267
+ variant="stat"
268
+ page-id="automation.overview"
269
+ :component-id="`overview-stat-${card.key}`"
270
+ :title="card.title"
271
+ :icon="card.icon"
272
+ :static-value="card.value"
273
+ :value-format="card.format"
274
+ :show-delta="false"
275
+ />
276
+ </template>
277
+ <template v-else>
278
+ <DmsCard v-for="i in 4" :key="`skel-${i}`" :padded="false" class="p-5">
279
+ <USkeleton class="h-10 w-full" />
280
+ </DmsCard>
281
+ </template>
282
+ </div>
283
+
284
+ <!-- Runs (7 days) chart + Execution health -->
285
+ <div class="grid grid-cols-1 gap-5 lg:grid-cols-2">
286
+ <!-- Runs (7 days) -->
287
+ <DmsCard :padded="false" class="flex flex-col">
288
+ <div class="flex flex-col gap-2 px-5 py-4 sm:px-6">
289
+ <div class="flex items-start justify-between gap-3">
290
+ <div>
291
+ <h2 class="font-semibold leading-tight text-highlighted">
292
+ {{ $t('dms_automation.overview.runs7d.title') }}
293
+ </h2>
294
+ <div class="mt-1 text-3xl font-semibold tracking-tight tabular-nums text-highlighted">
295
+ {{ data ? runs7dTotal.toLocaleString() : '—' }}
296
+ </div>
297
+ </div>
298
+ <div class="flex items-center gap-3 pt-1 text-xs">
299
+ <span class="flex items-center gap-1.5">
300
+ <span class="inline-block size-2.5 rounded-sm" :style="{ background: C_OK }" />
301
+ <span class="text-muted">{{ $t('dms_automation.overview.chart.legendOk') }}</span>
302
+ </span>
303
+ <span class="flex items-center gap-1.5">
304
+ <span class="inline-block size-2.5 rounded-sm" :style="{ background: C_FAILED }" />
305
+ <span class="text-muted">{{ $t('dms_automation.overview.chart.legendFailed') }}</span>
306
+ </span>
307
+ </div>
308
+ </div>
309
+ </div>
310
+ <div class="flex-1 px-2 pb-3 sm:px-3">
311
+ <DmsChart
312
+ v-if="data && hasRuns7d"
313
+ type="area"
314
+ :static-dataset="chartSeries"
315
+ :show-legend="false"
316
+ height="208px"
317
+ />
318
+ <div
319
+ v-else-if="data"
320
+ class="flex h-[208px] items-center justify-center text-sm text-dimmed"
321
+ >
322
+ {{ $t('dms_automation.overview.chart.empty') }}
323
+ </div>
324
+ <USkeleton v-else class="mx-3 h-[208px]" />
325
+ </div>
326
+ </DmsCard>
327
+
328
+ <!-- Execution health -->
329
+ <DmsCard :padded="false" class="flex flex-col">
330
+ <div
331
+ class="flex items-center justify-between gap-3 border-b border-default px-5 py-4 sm:px-6"
332
+ >
333
+ <h2 class="font-semibold leading-tight text-highlighted">
334
+ {{ $t('dms_automation.overview.execHealth.title') }}
335
+ </h2>
336
+ <UBadge
337
+ v-if="data"
338
+ :color="healthOperational ? 'success' : 'warning'"
339
+ variant="subtle"
340
+ class="gap-1.5"
341
+ >
342
+ <span
343
+ class="inline-block size-1.5 rounded-full"
344
+ :class="healthOperational ? 'bg-success' : 'bg-warning'"
345
+ />
346
+ {{
347
+ healthOperational
348
+ ? $t('dms_automation.overview.execHealth.operational')
349
+ : $t('dms_automation.overview.execHealth.needsAttention')
350
+ }}
351
+ </UBadge>
352
+ </div>
353
+ <div v-if="data" class="px-5 py-1 sm:px-6">
354
+ <div
355
+ v-for="m in healthMetrics"
356
+ :key="m.label"
357
+ class="flex items-center justify-between border-b border-default/60 py-2.5 last:border-0"
358
+ >
359
+ <span class="text-sm text-muted">{{ m.label }}</span>
360
+ <span class="text-sm font-medium tabular-nums text-highlighted">
361
+ {{ m.value }}
362
+ </span>
363
+ </div>
364
+ </div>
365
+ <div v-else class="px-5 py-4 sm:px-6">
366
+ <USkeleton class="h-40 w-full" />
367
+ </div>
368
+ </DmsCard>
369
+ </div>
370
+
371
+ <!-- Needs attention + Recent runs -->
372
+ <div class="grid grid-cols-1 gap-5 lg:grid-cols-2">
373
+ <!-- Needs attention -->
374
+ <DmsCard :padded="false" class="flex flex-col overflow-hidden">
375
+ <div
376
+ class="flex items-start justify-between gap-3 border-b border-default px-5 py-4 sm:px-6"
377
+ >
378
+ <div>
379
+ <h2 class="font-semibold leading-tight text-highlighted">
380
+ {{ $t('dms_automation.overview.needsAttention.title') }}
381
+ </h2>
382
+ <p class="text-xs text-muted">
383
+ {{ $t('dms_automation.overview.needsAttention.subtitle') }}
384
+ </p>
385
+ </div>
386
+ <DmsLink
387
+ :to="proceduresLink"
388
+ class="shrink-0 text-xs font-medium text-primary hover:underline"
389
+ >
390
+ {{ $t('dms_automation.overview.needsAttention.all') }}
391
+ </DmsLink>
392
+ </div>
393
+ <div v-if="!data" class="px-5 py-4 sm:px-6">
394
+ <USkeleton class="h-24 w-full" />
395
+ </div>
396
+ <div
397
+ v-else-if="data.needsAttention.length === 0"
398
+ class="px-5 py-10 text-center text-sm text-dimmed"
399
+ >
400
+ {{ $t('dms_automation.overview.needsAttention.empty') }}
401
+ </div>
402
+ <ul v-else class="flex flex-col">
403
+ <li
404
+ v-for="a in data.needsAttention"
405
+ :key="a.procedureId"
406
+ class="flex items-center gap-3 border-b border-default/60 px-5 py-3 last:border-0 sm:px-6"
407
+ >
408
+ <span
409
+ class="grid size-8 shrink-0 place-items-center rounded-lg"
410
+ :class="
411
+ a.kind === 'failing'
412
+ ? 'bg-error/10 text-error'
413
+ : 'bg-warning/10 text-warning'
414
+ "
415
+ >
416
+ <UIcon
417
+ :name="a.kind === 'failing' ? 'i-ph-x-circle' : 'i-ph-pause'"
418
+ class="size-4"
419
+ />
420
+ </span>
421
+ <div class="min-w-0 flex-1">
422
+ <div class="truncate text-sm font-semibold text-highlighted">
423
+ {{ a.name }}
424
+ </div>
425
+ <div class="truncate text-xs text-dimmed">
426
+ <template v-if="a.kind === 'failing'">
427
+ {{ $t('dms_automation.overview.needsAttention.failingDetail', { n: a.failed }) }}
428
+ </template>
429
+ <template v-else>
430
+ {{ $t('dms_automation.overview.needsAttention.pausedDetail') }}
431
+ <template v-if="a.lastRunAt">
432
+ · {{ $t('dms_automation.overview.needsAttention.lastRun', { ago: timeAgo(a.lastRunAt) }) }}
433
+ </template>
434
+ </template>
435
+ </div>
436
+ </div>
437
+ <DmsLink
438
+ :to="procedureLink(a.procedureId)"
439
+ class="flex shrink-0 items-center gap-0.5 text-xs font-medium text-primary hover:underline"
440
+ >
441
+ {{ $t('dms_automation.overview.needsAttention.open') }}
442
+ <UIcon name="i-ph-caret-right" class="size-3.5" />
443
+ </DmsLink>
444
+ </li>
445
+ </ul>
446
+ </DmsCard>
447
+
448
+ <!-- Recent runs -->
449
+ <DmsCard :padded="false" class="flex flex-col overflow-hidden">
450
+ <div
451
+ class="flex items-center justify-between gap-3 border-b border-default px-5 py-4 sm:px-6"
452
+ >
453
+ <h2 class="font-semibold leading-tight text-highlighted">
454
+ {{ $t('dms_automation.overview.recentRuns.title') }}
455
+ </h2>
456
+ <DmsLink
457
+ :to="runsAllLink"
458
+ class="shrink-0 text-xs font-medium text-primary hover:underline"
459
+ >
460
+ {{ $t('dms_automation.overview.recentRuns.viewAll') }}
461
+ </DmsLink>
462
+ </div>
463
+ <div v-if="!data" class="px-5 py-4 sm:px-6">
464
+ <USkeleton class="h-24 w-full" />
465
+ </div>
466
+ <div
467
+ v-else-if="data.recentRuns.length === 0"
468
+ class="px-5 py-10 text-center text-sm text-dimmed"
469
+ >
470
+ {{ $t('dms_automation.overview.recentRuns.empty') }}
471
+ </div>
472
+ <ul v-else class="flex flex-col">
473
+ <li
474
+ v-for="r in data.recentRuns"
475
+ :key="r.runId"
476
+ class="flex items-center gap-3 border-b border-default/60 px-5 py-3 last:border-0 sm:px-6"
477
+ >
478
+ <span
479
+ class="grid size-7 shrink-0 place-items-center rounded-lg"
480
+ :class="
481
+ runIsFailed(r.status)
482
+ ? 'bg-error/10 text-error'
483
+ : 'bg-success/10 text-success'
484
+ "
485
+ >
486
+ <UIcon
487
+ :name="runIsFailed(r.status) ? 'i-ph-x-circle' : 'i-ph-check'"
488
+ class="size-4"
489
+ />
490
+ </span>
491
+ <div class="min-w-0 flex-1 truncate text-sm text-highlighted">
492
+ {{ r.procedureName }}
493
+ </div>
494
+ <div class="shrink-0 text-xs tabular-nums text-dimmed">
495
+ <span v-if="r.durationMs !== null">{{ formatDuration(r.durationMs) }} · </span>
496
+ {{ $t('dms_automation.overview.recentRuns.ago', { ago: timeAgo(r.startedAt) }) }}
497
+ </div>
498
+ </li>
499
+ </ul>
500
+ </DmsCard>
501
+ </div>
502
+ </div>
503
+ </template>