@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.
- package/LICENSE +190 -0
- package/README.md +95 -0
- package/dist/cluster/bus.d.ts +17 -0
- package/dist/cluster/bus.js +82 -0
- package/dist/cluster/bus.js.map +1 -0
- package/dist/cluster/instance.d.ts +1 -0
- package/dist/cluster/instance.js +6 -0
- package/dist/cluster/instance.js.map +1 -0
- package/dist/cluster/leader.d.ts +7 -0
- package/dist/cluster/leader.js +123 -0
- package/dist/cluster/leader.js.map +1 -0
- package/dist/cluster/mode.d.ts +3 -0
- package/dist/cluster/mode.js +25 -0
- package/dist/cluster/mode.js.map +1 -0
- package/dist/data/procedures-table.d.ts +66 -0
- package/dist/data/procedures-table.js +271 -0
- package/dist/data/procedures-table.js.map +1 -0
- package/dist/data/runs-table.d.ts +23 -0
- package/dist/data/runs-table.js +125 -0
- package/dist/data/runs-table.js.map +1 -0
- package/dist/db/index.d.ts +7 -0
- package/dist/db/index.js +24 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/models/automation_template.model.d.ts +58 -0
- package/dist/db/models/automation_template.model.js +134 -0
- package/dist/db/models/automation_template.model.js.map +1 -0
- package/dist/db/models/index.d.ts +3 -0
- package/dist/db/models/index.js +20 -0
- package/dist/db/models/index.js.map +1 -0
- package/dist/db/models/procedure.model.d.ts +25 -0
- package/dist/db/models/procedure.model.js +28 -0
- package/dist/db/models/procedure.model.js.map +1 -0
- package/dist/db/models/procedure_run.model.d.ts +34 -0
- package/dist/db/models/procedure_run.model.js +39 -0
- package/dist/db/models/procedure_run.model.js.map +1 -0
- package/dist/db/models/stats.model.d.ts +73 -0
- package/dist/db/models/stats.model.js +267 -0
- package/dist/db/models/stats.model.js.map +1 -0
- package/dist/db/models/type_config.model.d.ts +47 -0
- package/dist/db/models/type_config.model.js +51 -0
- package/dist/db/models/type_config.model.js.map +1 -0
- package/dist/db/tables/action_type_config.table.d.ts +7 -0
- package/dist/db/tables/action_type_config.table.js +35 -0
- package/dist/db/tables/action_type_config.table.js.map +1 -0
- package/dist/db/tables/automation_template.table.d.ts +11 -0
- package/dist/db/tables/automation_template.table.js +52 -0
- package/dist/db/tables/automation_template.table.js.map +1 -0
- package/dist/db/tables/index.d.ts +5 -0
- package/dist/db/tables/index.js +22 -0
- package/dist/db/tables/index.js.map +1 -0
- package/dist/db/tables/procedure.table.d.ts +10 -0
- package/dist/db/tables/procedure.table.js +48 -0
- package/dist/db/tables/procedure.table.js.map +1 -0
- package/dist/db/tables/procedure_run.table.d.ts +13 -0
- package/dist/db/tables/procedure_run.table.js +57 -0
- package/dist/db/tables/procedure_run.table.js.map +1 -0
- package/dist/db/tables/trigger_type_config.table.d.ts +7 -0
- package/dist/db/tables/trigger_type_config.table.js +35 -0
- package/dist/db/tables/trigger_type_config.table.js.map +1 -0
- package/dist/implementations/dms-automation/index.d.ts +19 -0
- package/dist/implementations/dms-automation/index.js +50 -0
- package/dist/implementations/dms-automation/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +121 -0
- package/dist/index.js.map +1 -0
- package/dist/pages/builder.d.ts +10 -0
- package/dist/pages/builder.js +28 -0
- package/dist/pages/builder.js.map +1 -0
- package/dist/pages/category.d.ts +1 -0
- package/dist/pages/category.js +10 -0
- package/dist/pages/category.js.map +1 -0
- package/dist/pages/index.d.ts +6 -0
- package/dist/pages/index.js +9 -0
- package/dist/pages/index.js.map +1 -0
- package/dist/pages/library.d.ts +10 -0
- package/dist/pages/library.js +28 -0
- package/dist/pages/library.js.map +1 -0
- package/dist/pages/overview.d.ts +10 -0
- package/dist/pages/overview.js +28 -0
- package/dist/pages/overview.js.map +1 -0
- package/dist/pages/procedures.d.ts +10 -0
- package/dist/pages/procedures.js +72 -0
- package/dist/pages/procedures.js.map +1 -0
- package/dist/pages/runs.d.ts +11 -0
- package/dist/pages/runs.js +61 -0
- package/dist/pages/runs.js.map +1 -0
- package/dist/routes/action-types.d.ts +23 -0
- package/dist/routes/action-types.js +80 -0
- package/dist/routes/action-types.js.map +1 -0
- package/dist/routes/data-node-types.d.ts +6 -0
- package/dist/routes/data-node-types.js +36 -0
- package/dist/routes/data-node-types.js.map +1 -0
- package/dist/routes/graphSchema.d.ts +92 -0
- package/dist/routes/graphSchema.js +60 -0
- package/dist/routes/graphSchema.js.map +1 -0
- package/dist/routes/index.d.ts +8 -0
- package/dist/routes/index.js +11 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/node-kinds.d.ts +9 -0
- package/dist/routes/node-kinds.js +35 -0
- package/dist/routes/node-kinds.js.map +1 -0
- package/dist/routes/procedures.d.ts +23 -0
- package/dist/routes/procedures.js +181 -0
- package/dist/routes/procedures.js.map +1 -0
- package/dist/routes/runs.d.ts +7 -0
- package/dist/routes/runs.js +81 -0
- package/dist/routes/runs.js.map +1 -0
- package/dist/routes/stats.d.ts +6 -0
- package/dist/routes/stats.js +35 -0
- package/dist/routes/stats.js.map +1 -0
- package/dist/routes/templates.d.ts +20 -0
- package/dist/routes/templates.js +243 -0
- package/dist/routes/templates.js.map +1 -0
- package/dist/routes/trigger-types.d.ts +24 -0
- package/dist/routes/trigger-types.js +80 -0
- package/dist/routes/trigger-types.js.map +1 -0
- package/dist/routes/typeConfigShared.d.ts +22 -0
- package/dist/routes/typeConfigShared.js +37 -0
- package/dist/routes/typeConfigShared.js.map +1 -0
- package/dist/runtime/builtins/actions/http.d.ts +13 -0
- package/dist/runtime/builtins/actions/http.js +47 -0
- package/dist/runtime/builtins/actions/http.js.map +1 -0
- package/dist/runtime/builtins/actions/index.d.ts +2 -0
- package/dist/runtime/builtins/actions/index.js +7 -0
- package/dist/runtime/builtins/actions/index.js.map +1 -0
- package/dist/runtime/builtins/actions/log.d.ts +13 -0
- package/dist/runtime/builtins/actions/log.js +49 -0
- package/dist/runtime/builtins/actions/log.js.map +1 -0
- package/dist/runtime/builtins/data/boolean.d.ts +2 -0
- package/dist/runtime/builtins/data/boolean.js +39 -0
- package/dist/runtime/builtins/data/boolean.js.map +1 -0
- package/dist/runtime/builtins/data/compare.d.ts +2 -0
- package/dist/runtime/builtins/data/compare.js +29 -0
- package/dist/runtime/builtins/data/compare.js.map +1 -0
- package/dist/runtime/builtins/data/constant.d.ts +2 -0
- package/dist/runtime/builtins/data/constant.js +29 -0
- package/dist/runtime/builtins/data/constant.js.map +1 -0
- package/dist/runtime/builtins/data/index.d.ts +2 -0
- package/dist/runtime/builtins/data/index.js +20 -0
- package/dist/runtime/builtins/data/index.js.map +1 -0
- package/dist/runtime/builtins/data/json.d.ts +2 -0
- package/dist/runtime/builtins/data/json.js +46 -0
- package/dist/runtime/builtins/data/json.js.map +1 -0
- package/dist/runtime/builtins/data/math.d.ts +2 -0
- package/dist/runtime/builtins/data/math.js +53 -0
- package/dist/runtime/builtins/data/math.js.map +1 -0
- package/dist/runtime/builtins/data/object.d.ts +2 -0
- package/dist/runtime/builtins/data/object.js +121 -0
- package/dist/runtime/builtins/data/object.js.map +1 -0
- package/dist/runtime/builtins/data/string.d.ts +2 -0
- package/dist/runtime/builtins/data/string.js +143 -0
- package/dist/runtime/builtins/data/string.js.map +1 -0
- package/dist/runtime/builtins/index.d.ts +17 -0
- package/dist/runtime/builtins/index.js +24 -0
- package/dist/runtime/builtins/index.js.map +1 -0
- package/dist/runtime/builtins/nodes/action.d.ts +1 -0
- package/dist/runtime/builtins/nodes/action.js +36 -0
- package/dist/runtime/builtins/nodes/action.js.map +1 -0
- package/dist/runtime/builtins/nodes/data.d.ts +1 -0
- package/dist/runtime/builtins/nodes/data.js +28 -0
- package/dist/runtime/builtins/nodes/data.js.map +1 -0
- package/dist/runtime/builtins/nodes/delay.d.ts +1 -0
- package/dist/runtime/builtins/nodes/delay.js +41 -0
- package/dist/runtime/builtins/nodes/delay.js.map +1 -0
- package/dist/runtime/builtins/nodes/forRange.d.ts +1 -0
- package/dist/runtime/builtins/nodes/forRange.js +52 -0
- package/dist/runtime/builtins/nodes/forRange.js.map +1 -0
- package/dist/runtime/builtins/nodes/foreach.d.ts +1 -0
- package/dist/runtime/builtins/nodes/foreach.js +31 -0
- package/dist/runtime/builtins/nodes/foreach.js.map +1 -0
- package/dist/runtime/builtins/nodes/groupInput.d.ts +1 -0
- package/dist/runtime/builtins/nodes/groupInput.js +11 -0
- package/dist/runtime/builtins/nodes/groupInput.js.map +1 -0
- package/dist/runtime/builtins/nodes/groupOutput.d.ts +1 -0
- package/dist/runtime/builtins/nodes/groupOutput.js +11 -0
- package/dist/runtime/builtins/nodes/groupOutput.js.map +1 -0
- package/dist/runtime/builtins/nodes/if.d.ts +1 -0
- package/dist/runtime/builtins/nodes/if.js +23 -0
- package/dist/runtime/builtins/nodes/if.js.map +1 -0
- package/dist/runtime/builtins/nodes/parallel.d.ts +1 -0
- package/dist/runtime/builtins/nodes/parallel.js +34 -0
- package/dist/runtime/builtins/nodes/parallel.js.map +1 -0
- package/dist/runtime/builtins/nodes/retry.d.ts +1 -0
- package/dist/runtime/builtins/nodes/retry.js +46 -0
- package/dist/runtime/builtins/nodes/retry.js.map +1 -0
- package/dist/runtime/builtins/nodes/setVariable.d.ts +1 -0
- package/dist/runtime/builtins/nodes/setVariable.js +31 -0
- package/dist/runtime/builtins/nodes/setVariable.js.map +1 -0
- package/dist/runtime/builtins/nodes/switch.d.ts +1 -0
- package/dist/runtime/builtins/nodes/switch.js +33 -0
- package/dist/runtime/builtins/nodes/switch.js.map +1 -0
- package/dist/runtime/builtins/nodes/trigger.d.ts +1 -0
- package/dist/runtime/builtins/nodes/trigger.js +25 -0
- package/dist/runtime/builtins/nodes/trigger.js.map +1 -0
- package/dist/runtime/builtins/nodes/tryCatch.d.ts +1 -0
- package/dist/runtime/builtins/nodes/tryCatch.js +35 -0
- package/dist/runtime/builtins/nodes/tryCatch.js.map +1 -0
- package/dist/runtime/builtins/triggers/cron.d.ts +9 -0
- package/dist/runtime/builtins/triggers/cron.js +52 -0
- package/dist/runtime/builtins/triggers/cron.js.map +1 -0
- package/dist/runtime/builtins/triggers/index.d.ts +2 -0
- package/dist/runtime/builtins/triggers/index.js +12 -0
- package/dist/runtime/builtins/triggers/index.js.map +1 -0
- package/dist/runtime/builtins/triggers/manual.d.ts +3 -0
- package/dist/runtime/builtins/triggers/manual.js +20 -0
- package/dist/runtime/builtins/triggers/manual.js.map +1 -0
- package/dist/runtime/builtins/triggers/webhook.d.ts +14 -0
- package/dist/runtime/builtins/triggers/webhook.js +90 -0
- package/dist/runtime/builtins/triggers/webhook.js.map +1 -0
- package/dist/runtime/executionResult.d.ts +6 -0
- package/dist/runtime/executionResult.js +3 -0
- package/dist/runtime/executionResult.js.map +1 -0
- package/dist/runtime/executor.d.ts +9 -0
- package/dist/runtime/executor.js +300 -0
- package/dist/runtime/executor.js.map +1 -0
- package/dist/runtime/flatten.d.ts +3 -0
- package/dist/runtime/flatten.js +188 -0
- package/dist/runtime/flatten.js.map +1 -0
- package/dist/runtime/nodeKinds.d.ts +64 -0
- package/dist/runtime/nodeKinds.js +43 -0
- package/dist/runtime/nodeKinds.js.map +1 -0
- package/dist/runtime/reconcileDiff.d.ts +9 -0
- package/dist/runtime/reconcileDiff.js +26 -0
- package/dist/runtime/reconcileDiff.js.map +1 -0
- package/dist/runtime/registry.d.ts +19 -0
- package/dist/runtime/registry.js +47 -0
- package/dist/runtime/registry.js.map +1 -0
- package/dist/runtime/resolveInputs.d.ts +14 -0
- package/dist/runtime/resolveInputs.js +64 -0
- package/dist/runtime/resolveInputs.js.map +1 -0
- package/dist/runtime/resolveSubgraph.d.ts +7 -0
- package/dist/runtime/resolveSubgraph.js +15 -0
- package/dist/runtime/resolveSubgraph.js.map +1 -0
- package/dist/runtime/subscriptions.d.ts +16 -0
- package/dist/runtime/subscriptions.js +419 -0
- package/dist/runtime/subscriptions.js.map +1 -0
- package/dist/runtime/subwalk.d.ts +17 -0
- package/dist/runtime/subwalk.js +74 -0
- package/dist/runtime/subwalk.js.map +1 -0
- package/dist/runtime/topo.d.ts +3 -0
- package/dist/runtime/topo.js +75 -0
- package/dist/runtime/topo.js.map +1 -0
- package/dist/runtime/typeOrigin.d.ts +7 -0
- package/dist/runtime/typeOrigin.js +24 -0
- package/dist/runtime/typeOrigin.js.map +1 -0
- package/dist/runtime/validate.d.ts +10 -0
- package/dist/runtime/validate.js +185 -0
- package/dist/runtime/validate.js.map +1 -0
- package/dist/runtime/validateTemplate.d.ts +3 -0
- package/dist/runtime/validateTemplate.js +54 -0
- package/dist/runtime/validateTemplate.js.map +1 -0
- package/dist/types/constants.d.ts +6 -0
- package/dist/types/constants.js +10 -0
- package/dist/types/constants.js.map +1 -0
- package/dist/types/graph.d.ts +51 -0
- package/dist/types/graph.js +23 -0
- package/dist/types/graph.js.map +1 -0
- package/dist/types/pagination.d.ts +8 -0
- package/dist/types/pagination.js +3 -0
- package/dist/types/pagination.js.map +1 -0
- package/dist/types/runLog.d.ts +26 -0
- package/dist/types/runLog.js +3 -0
- package/dist/types/runLog.js.map +1 -0
- package/frontend-vue/.prettierignore +4 -0
- package/frontend-vue/app/components/Actions.vue +141 -0
- package/frontend-vue/app/components/Catalog/TypeConfigForm.vue +166 -0
- package/frontend-vue/app/components/Catalog/TypeList.vue +198 -0
- package/frontend-vue/app/components/DataNodes.vue +259 -0
- package/frontend-vue/app/components/Editor/BuilderPalette.vue +234 -0
- package/frontend-vue/app/components/Editor/GraphCanvas.vue +994 -0
- package/frontend-vue/app/components/Editor/GroupPortEditor.vue +289 -0
- package/frontend-vue/app/components/Editor/JsonField.vue +63 -0
- package/frontend-vue/app/components/Editor/NodeInspector.vue +356 -0
- package/frontend-vue/app/components/Editor/ProcedureList.vue +156 -0
- package/frontend-vue/app/components/Editor/RunsDrawer.vue +312 -0
- package/frontend-vue/app/components/Editor/WiredField.vue +42 -0
- package/frontend-vue/app/components/Editor/nodes/GenericNode.vue +161 -0
- package/frontend-vue/app/components/Editor/nodes/GroupInputNode.vue +19 -0
- package/frontend-vue/app/components/Editor/nodes/GroupNode.vue +200 -0
- package/frontend-vue/app/components/Editor/nodes/GroupOutputNode.vue +19 -0
- package/frontend-vue/app/components/Editor/nodes/GroupSentinel.vue +161 -0
- package/frontend-vue/app/components/Editor/nodes/styles.ts +53 -0
- package/frontend-vue/app/components/Editor.vue +2504 -0
- package/frontend-vue/app/components/Library.vue +67 -0
- package/frontend-vue/app/components/Overview.vue +503 -0
- package/frontend-vue/app/components/RunDetailDrawer.vue +165 -0
- package/frontend-vue/app/components/RunFireTree.vue +170 -0
- package/frontend-vue/app/components/RunTraceDrawer.vue +59 -0
- package/frontend-vue/app/components/RunsStats.vue +126 -0
- package/frontend-vue/app/components/RunsTimelineDisplay.vue +287 -0
- package/frontend-vue/app/components/Templates.vue +663 -0
- package/frontend-vue/app/components/Triggers.vue +137 -0
- package/frontend-vue/app/composables/useAutomationNodeKinds.ts +48 -0
- package/frontend-vue/app/composables/useAutomationRuns.ts +75 -0
- package/frontend-vue/app/composables/useGraphConnect.ts +542 -0
- package/frontend-vue/app/plugins/runs-timeline-display.client.ts +16 -0
- package/frontend-vue/app/utils/automation.ts +51 -0
- package/frontend-vue/dms.frontend.ts +26 -0
- package/frontend-vue/eslint.config.mjs +24 -0
- package/frontend-vue/i18n/locales/dms-automation-en-GB.json +369 -0
- package/frontend-vue/i18n/locales/dms-automation-fr-FR.json +369 -0
- package/frontend-vue/package.json +33 -0
- package/frontend-vue/pnpm-lock.yaml +6483 -0
- package/frontend-vue/pnpm-workspace.yaml +2 -0
- 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>
|