@miragon/pulse-analytics-connector 0.0.0-bootstrap.0 → 0.21.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.
- package/LICENSE +21 -0
- package/README.md +86 -3
- package/dist/bpmn-name-lookup.d.ts +72 -0
- package/dist/bpmn-name-lookup.d.ts.map +1 -0
- package/dist/bpmn-name-lookup.js +123 -0
- package/dist/bpmn-name-lookup.js.map +1 -0
- package/dist/bpmn-name-lookup.test.d.ts +2 -0
- package/dist/bpmn-name-lookup.test.d.ts.map +1 -0
- package/dist/bpmn-name-lookup.test.js +287 -0
- package/dist/bpmn-name-lookup.test.js.map +1 -0
- package/dist/bpmn-names.d.ts +38 -0
- package/dist/bpmn-names.d.ts.map +1 -0
- package/dist/bpmn-names.js +22 -0
- package/dist/bpmn-names.js.map +1 -0
- package/dist/brand-gates.test.d.ts +2 -0
- package/dist/brand-gates.test.d.ts.map +1 -0
- package/dist/brand-gates.test.js +58 -0
- package/dist/brand-gates.test.js.map +1 -0
- package/dist/builder-texts.d.ts +4 -0
- package/dist/builder-texts.d.ts.map +1 -0
- package/dist/builder-texts.js +14 -0
- package/dist/builder-texts.js.map +1 -0
- package/dist/builder-texts.test.d.ts +2 -0
- package/dist/builder-texts.test.d.ts.map +1 -0
- package/dist/builder-texts.test.js +32 -0
- package/dist/builder-texts.test.js.map +1 -0
- package/dist/definition.d.ts +3 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +151 -0
- package/dist/definition.js.map +1 -0
- package/dist/engine-ids.d.ts +42 -0
- package/dist/engine-ids.d.ts.map +1 -0
- package/dist/engine-ids.js +50 -0
- package/dist/engine-ids.js.map +1 -0
- package/dist/engine-scope.test.d.ts +2 -0
- package/dist/engine-scope.test.d.ts.map +1 -0
- package/dist/engine-scope.test.js +260 -0
- package/dist/engine-scope.test.js.map +1 -0
- package/dist/heatmap-data.d.ts +15 -0
- package/dist/heatmap-data.d.ts.map +1 -0
- package/dist/heatmap-data.js +2 -0
- package/dist/heatmap-data.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/instructions.d.ts +8 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +33 -0
- package/dist/instructions.js.map +1 -0
- package/dist/messages/de.ask-ai.d.ts +9 -0
- package/dist/messages/de.ask-ai.d.ts.map +1 -0
- package/dist/messages/de.ask-ai.js +18 -0
- package/dist/messages/de.ask-ai.js.map +1 -0
- package/dist/messages/de.builder.d.ts +12 -0
- package/dist/messages/de.builder.d.ts.map +1 -0
- package/dist/messages/de.builder.js +53 -0
- package/dist/messages/de.builder.js.map +1 -0
- package/dist/messages/de.server.d.ts +8 -0
- package/dist/messages/de.server.d.ts.map +1 -0
- package/dist/messages/de.server.js +26 -0
- package/dist/messages/de.server.js.map +1 -0
- package/dist/messages/de.sweep.d.ts +10 -0
- package/dist/messages/de.sweep.d.ts.map +1 -0
- package/dist/messages/de.sweep.js +203 -0
- package/dist/messages/de.sweep.js.map +1 -0
- package/dist/messages/en.ask-ai.d.ts +25 -0
- package/dist/messages/en.ask-ai.d.ts.map +1 -0
- package/dist/messages/en.ask-ai.js +24 -0
- package/dist/messages/en.ask-ai.js.map +1 -0
- package/dist/messages/en.builder.d.ts +12 -0
- package/dist/messages/en.builder.d.ts.map +1 -0
- package/dist/messages/en.builder.js +53 -0
- package/dist/messages/en.builder.js.map +1 -0
- package/dist/messages/en.server.d.ts +8 -0
- package/dist/messages/en.server.d.ts.map +1 -0
- package/dist/messages/en.server.js +26 -0
- package/dist/messages/en.server.js.map +1 -0
- package/dist/messages/en.sweep.d.ts +11 -0
- package/dist/messages/en.sweep.d.ts.map +1 -0
- package/dist/messages/en.sweep.js +204 -0
- package/dist/messages/en.sweep.js.map +1 -0
- package/dist/messages/glossary.d.ts +26 -0
- package/dist/messages/glossary.d.ts.map +1 -0
- package/dist/messages/glossary.js +37 -0
- package/dist/messages/glossary.js.map +1 -0
- package/dist/messages/glossary.test.d.ts +2 -0
- package/dist/messages/glossary.test.d.ts.map +1 -0
- package/dist/messages/glossary.test.js +109 -0
- package/dist/messages/glossary.test.js.map +1 -0
- package/dist/messages/index.d.ts +12 -0
- package/dist/messages/index.d.ts.map +1 -0
- package/dist/messages/index.js +23 -0
- package/dist/messages/index.js.map +1 -0
- package/dist/messages/plural.d.ts +10 -0
- package/dist/messages/plural.d.ts.map +1 -0
- package/dist/messages/plural.js +17 -0
- package/dist/messages/plural.js.map +1 -0
- package/dist/messages/use-t.d.ts +10 -0
- package/dist/messages/use-t.d.ts.map +1 -0
- package/dist/messages/use-t.js +10 -0
- package/dist/messages/use-t.js.map +1 -0
- package/dist/module.d.ts +48 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +174 -0
- package/dist/module.js.map +1 -0
- package/dist/module.test.d.ts +2 -0
- package/dist/module.test.d.ts.map +1 -0
- package/dist/module.test.js +230 -0
- package/dist/module.test.js.map +1 -0
- package/dist/named-views.d.ts +27 -0
- package/dist/named-views.d.ts.map +1 -0
- package/dist/named-views.js +73 -0
- package/dist/named-views.js.map +1 -0
- package/dist/named-views.test.d.ts +2 -0
- package/dist/named-views.test.d.ts.map +1 -0
- package/dist/named-views.test.js +110 -0
- package/dist/named-views.test.js.map +1 -0
- package/dist/plugin.d.ts +44 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +52 -0
- package/dist/plugin.js.map +1 -0
- package/dist/prometheus-wiring.test.d.ts +2 -0
- package/dist/prometheus-wiring.test.d.ts.map +1 -0
- package/dist/prometheus-wiring.test.js +142 -0
- package/dist/prometheus-wiring.test.js.map +1 -0
- package/dist/server-locale.d.ts +48 -0
- package/dist/server-locale.d.ts.map +1 -0
- package/dist/server-locale.js +33 -0
- package/dist/server-locale.js.map +1 -0
- package/dist/settings-tools.d.ts +23 -0
- package/dist/settings-tools.d.ts.map +1 -0
- package/dist/settings-tools.js +100 -0
- package/dist/settings-tools.js.map +1 -0
- package/dist/settings.d.ts +77 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +99 -0
- package/dist/settings.js.map +1 -0
- package/dist/settings.test.d.ts +2 -0
- package/dist/settings.test.d.ts.map +1 -0
- package/dist/settings.test.js +299 -0
- package/dist/settings.test.js.map +1 -0
- package/dist/steps/app-config.d.ts +30 -0
- package/dist/steps/app-config.d.ts.map +1 -0
- package/dist/steps/app-config.js +46 -0
- package/dist/steps/app-config.js.map +1 -0
- package/dist/steps/dashboard.d.ts +12 -0
- package/dist/steps/dashboard.d.ts.map +1 -0
- package/dist/steps/dashboard.js +58 -0
- package/dist/steps/dashboard.js.map +1 -0
- package/dist/steps/failure-dashboard.d.ts +10 -0
- package/dist/steps/failure-dashboard.d.ts.map +1 -0
- package/dist/steps/failure-dashboard.js +32 -0
- package/dist/steps/failure-dashboard.js.map +1 -0
- package/dist/steps/index.d.ts +3 -0
- package/dist/steps/index.d.ts.map +1 -0
- package/dist/steps/index.js +3 -0
- package/dist/steps/index.js.map +1 -0
- package/dist/steps/steps.test.d.ts +2 -0
- package/dist/steps/steps.test.d.ts.map +1 -0
- package/dist/steps/steps.test.js +153 -0
- package/dist/steps/steps.test.js.map +1 -0
- package/dist/tool-names.d.ts +13 -0
- package/dist/tool-names.d.ts.map +1 -0
- package/dist/tool-names.js +27 -0
- package/dist/tool-names.js.map +1 -0
- package/dist/tools/cluster-compare.d.ts +8 -0
- package/dist/tools/cluster-compare.d.ts.map +1 -0
- package/dist/tools/cluster-compare.js +17 -0
- package/dist/tools/cluster-compare.js.map +1 -0
- package/dist/tools/element.d.ts +8 -0
- package/dist/tools/element.d.ts.map +1 -0
- package/dist/tools/element.js +25 -0
- package/dist/tools/element.js.map +1 -0
- package/dist/tools/engine-compare.d.ts +8 -0
- package/dist/tools/engine-compare.d.ts.map +1 -0
- package/dist/tools/engine-compare.js +18 -0
- package/dist/tools/engine-compare.js.map +1 -0
- package/dist/tools/engine-compare.test.d.ts +2 -0
- package/dist/tools/engine-compare.test.d.ts.map +1 -0
- package/dist/tools/engine-compare.test.js +166 -0
- package/dist/tools/engine-compare.test.js.map +1 -0
- package/dist/tools/engine-landscape.d.ts +7 -0
- package/dist/tools/engine-landscape.d.ts.map +1 -0
- package/dist/tools/engine-landscape.js +13 -0
- package/dist/tools/engine-landscape.js.map +1 -0
- package/dist/tools/engine-landscape.test.d.ts +2 -0
- package/dist/tools/engine-landscape.test.d.ts.map +1 -0
- package/dist/tools/engine-landscape.test.js +110 -0
- package/dist/tools/engine-landscape.test.js.map +1 -0
- package/dist/tools/failures.d.ts +7 -0
- package/dist/tools/failures.d.ts.map +1 -0
- package/dist/tools/failures.js +13 -0
- package/dist/tools/failures.js.map +1 -0
- package/dist/tools/health.d.ts +7 -0
- package/dist/tools/health.d.ts.map +1 -0
- package/dist/tools/health.js +23 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/health.test.d.ts +2 -0
- package/dist/tools/health.test.d.ts.map +1 -0
- package/dist/tools/health.test.js +119 -0
- package/dist/tools/health.test.js.map +1 -0
- package/dist/tools/index.d.ts +18 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +35 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/performance.d.ts +8 -0
- package/dist/tools/performance.d.ts.map +1 -0
- package/dist/tools/performance.js +25 -0
- package/dist/tools/performance.js.map +1 -0
- package/dist/tools/performance.test.d.ts +2 -0
- package/dist/tools/performance.test.d.ts.map +1 -0
- package/dist/tools/performance.test.js +176 -0
- package/dist/tools/performance.test.js.map +1 -0
- package/dist/tools/version-compare.d.ts +8 -0
- package/dist/tools/version-compare.d.ts.map +1 -0
- package/dist/tools/version-compare.js +17 -0
- package/dist/tools/version-compare.js.map +1 -0
- package/dist/tools/version-compare.test.d.ts +2 -0
- package/dist/tools/version-compare.test.d.ts.map +1 -0
- package/dist/tools/version-compare.test.js +82 -0
- package/dist/tools/version-compare.test.js.map +1 -0
- package/dist/toolsets.d.ts +17 -0
- package/dist/toolsets.d.ts.map +1 -0
- package/dist/toolsets.js +47 -0
- package/dist/toolsets.js.map +1 -0
- package/dist/toolsets.test.d.ts +2 -0
- package/dist/toolsets.test.d.ts.map +1 -0
- package/dist/toolsets.test.js +91 -0
- package/dist/toolsets.test.js.map +1 -0
- package/dist/view-title.test.d.ts +2 -0
- package/dist/view-title.test.d.ts.map +1 -0
- package/dist/view-title.test.js +91 -0
- package/dist/view-title.test.js.map +1 -0
- package/dist/widget-tools/comparisons.d.ts +13 -0
- package/dist/widget-tools/comparisons.d.ts.map +1 -0
- package/dist/widget-tools/comparisons.js +177 -0
- package/dist/widget-tools/comparisons.js.map +1 -0
- package/dist/widget-tools/comparisons.test.d.ts +2 -0
- package/dist/widget-tools/comparisons.test.d.ts.map +1 -0
- package/dist/widget-tools/comparisons.test.js +123 -0
- package/dist/widget-tools/comparisons.test.js.map +1 -0
- package/dist/widget-tools/shared.d.ts +36 -0
- package/dist/widget-tools/shared.d.ts.map +1 -0
- package/dist/widget-tools/shared.js +39 -0
- package/dist/widget-tools/shared.js.map +1 -0
- package/dist/widget-tools.d.ts +25 -0
- package/dist/widget-tools.d.ts.map +1 -0
- package/dist/widget-tools.js +269 -0
- package/dist/widget-tools.js.map +1 -0
- package/dist/widget-tools.test.d.ts +2 -0
- package/dist/widget-tools.test.d.ts.map +1 -0
- package/dist/widget-tools.test.js +272 -0
- package/dist/widget-tools.test.js.map +1 -0
- package/package.json +91 -5
- package/src/bpmn-name-lookup.test.ts +333 -0
- package/src/bpmn-name-lookup.ts +197 -0
- package/src/bpmn-names.ts +51 -0
- package/src/brand-gates.test.ts +69 -0
- package/src/builder-texts.test.ts +40 -0
- package/src/builder-texts.ts +15 -0
- package/src/definition.ts +181 -0
- package/src/engine-ids.ts +85 -0
- package/src/engine-scope.test.ts +324 -0
- package/src/heatmap-data.ts +14 -0
- package/src/index.ts +4 -0
- package/src/instructions.ts +33 -0
- package/src/messages/GLOSSARY.md +88 -0
- package/src/messages/de.ask-ai.ts +28 -0
- package/src/messages/de.builder.ts +77 -0
- package/src/messages/de.server.ts +39 -0
- package/src/messages/de.sweep.ts +244 -0
- package/src/messages/en.ask-ai.ts +34 -0
- package/src/messages/en.builder.ts +76 -0
- package/src/messages/en.server.ts +39 -0
- package/src/messages/en.sweep.ts +240 -0
- package/src/messages/glossary.test.ts +122 -0
- package/src/messages/glossary.ts +62 -0
- package/src/messages/index.ts +24 -0
- package/src/messages/plural.ts +18 -0
- package/src/messages/use-t.ts +12 -0
- package/src/module.test.ts +319 -0
- package/src/module.ts +211 -0
- package/src/named-views.test.ts +141 -0
- package/src/named-views.ts +97 -0
- package/src/plugin.ts +94 -0
- package/src/prometheus-wiring.test.ts +174 -0
- package/src/server-locale.ts +59 -0
- package/src/settings-tools.ts +170 -0
- package/src/settings.test.ts +402 -0
- package/src/settings.ts +114 -0
- package/src/steps/app-config.ts +75 -0
- package/src/steps/dashboard.ts +73 -0
- package/src/steps/failure-dashboard.ts +38 -0
- package/src/steps/index.ts +2 -0
- package/src/steps/steps.test.ts +216 -0
- package/src/tool-names.ts +26 -0
- package/src/tools/cluster-compare.ts +28 -0
- package/src/tools/element.ts +35 -0
- package/src/tools/engine-compare.test.ts +210 -0
- package/src/tools/engine-compare.ts +30 -0
- package/src/tools/engine-landscape.test.ts +138 -0
- package/src/tools/engine-landscape.ts +21 -0
- package/src/tools/failures.ts +19 -0
- package/src/tools/health.test.ts +156 -0
- package/src/tools/health.ts +29 -0
- package/src/tools/index.ts +44 -0
- package/src/tools/performance.test.ts +210 -0
- package/src/tools/performance.ts +38 -0
- package/src/tools/version-compare.test.ts +97 -0
- package/src/tools/version-compare.ts +28 -0
- package/src/toolsets.test.ts +107 -0
- package/src/toolsets.ts +56 -0
- package/src/view-title.test.ts +123 -0
- package/src/widget-tools/comparisons.test.ts +170 -0
- package/src/widget-tools/comparisons.ts +229 -0
- package/src/widget-tools/shared.ts +67 -0
- package/src/widget-tools.test.ts +306 -0
- package/src/widget-tools.ts +375 -0
- package/src/widgets/analytics-dashboard/activity-bottleneck-table.tsx +179 -0
- package/src/widgets/analytics-dashboard/definition-breakdown.tsx +108 -0
- package/src/widgets/analytics-dashboard/execution-performance-kpi.tsx +94 -0
- package/src/widgets/analytics-dashboard/execution-summary-kpi.tsx +122 -0
- package/src/widgets/analytics-dashboard/lib.ts +43 -0
- package/src/widgets/bpmn-heatmap.test.tsx +75 -0
- package/src/widgets/bpmn-heatmap.tsx +127 -0
- package/src/widgets/catalogue-sync.test.ts +16 -0
- package/src/widgets/cluster-compare.tsx +148 -0
- package/src/widgets/comparison-shared.test.ts +237 -0
- package/src/widgets/comparison-shared.tsx +371 -0
- package/src/widgets/comparison-widgets.test.tsx +307 -0
- package/src/widgets/dashboard-widgets.test.tsx +397 -0
- package/src/widgets/engine-compare.tsx +87 -0
- package/src/widgets/engine-landscape.tsx +352 -0
- package/src/widgets/failure-dashboard/error-patterns-table.tsx +122 -0
- package/src/widgets/failure-dashboard/failure-rate-table.test.ts +80 -0
- package/src/widgets/failure-dashboard/failure-rate-table.tsx +170 -0
- package/src/widgets/failure-dashboard/lib.ts +32 -0
- package/src/widgets/failure-dashboard/summary-kpi.tsx +122 -0
- package/src/widgets/failure-landscape-widgets.test.tsx +393 -0
- package/src/widgets/figure.tsx +79 -0
- package/src/widgets/hand-off.test.ts +126 -0
- package/src/widgets/hand-off.ts +102 -0
- package/src/widgets/index.ts +124 -0
- package/src/widgets/model-descriptions.dashboard.test.ts +280 -0
- package/src/widgets/model-descriptions.test.ts +251 -0
- package/src/widgets/model-descriptions.ts +428 -0
- package/src/widgets/names.test.ts +32 -0
- package/src/widgets/names.ts +39 -0
- package/src/widgets/query-gate.test.tsx +57 -0
- package/src/widgets/query-gate.tsx +50 -0
- package/src/widgets/settings-section.test.tsx +174 -0
- package/src/widgets/settings-section.tsx +244 -0
- package/src/widgets/version-compare.test.ts +68 -0
- package/src/widgets/version-compare.tsx +96 -0
- package/src/widgets/view-meta.ts +18 -0
- package/src/widgets/widget-render.test-support.tsx +103 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import type { MCPServer } from "mcp-use"
|
|
2
|
+
import {
|
|
3
|
+
appOnly,
|
|
4
|
+
buildComposedView,
|
|
5
|
+
buildDataFeedResult,
|
|
6
|
+
buildSingleWidgetView,
|
|
7
|
+
showToolBinding,
|
|
8
|
+
withToolErrors,
|
|
9
|
+
strictToolInput,
|
|
10
|
+
} from "@miragon/pulse-widget-shell/server"
|
|
11
|
+
import {
|
|
12
|
+
queries,
|
|
13
|
+
schemas,
|
|
14
|
+
withCallerSignal,
|
|
15
|
+
type AnalyticsDashboardData,
|
|
16
|
+
type PrometheusClient,
|
|
17
|
+
} from "@miragon/pulse-analytics-client"
|
|
18
|
+
import {
|
|
19
|
+
ANALYTICS_BPMN_HEATMAP_DATA,
|
|
20
|
+
ANALYTICS_BPMN_XML_DATA,
|
|
21
|
+
ANALYTICS_DASHBOARD_DATA,
|
|
22
|
+
ANALYTICS_FAILURE_DASHBOARD_DATA,
|
|
23
|
+
} from "./tool-names.js"
|
|
24
|
+
import { localizeViewFor, type ProfileSource, type ServerT } from "./server-locale.js"
|
|
25
|
+
import { optionalPeriod, settingsFor } from "./settings.js"
|
|
26
|
+
import { registerComparisonWidgetTools } from "./widget-tools/comparisons.js"
|
|
27
|
+
import { engineScopeSummary } from "./widget-tools/shared.js"
|
|
28
|
+
import { isFleetRequest, withEngineScope, type AnalyticsEngineScope } from "./engine-ids.js"
|
|
29
|
+
import type { BpmnMissingReason } from "./heatmap-data.js"
|
|
30
|
+
import { NO_NAMES, type FetchBpmnXml, type NameLookup } from "./bpmn-name-lookup.js"
|
|
31
|
+
import { namedDashboard, namedFailureDashboard } from "./named-views.js"
|
|
32
|
+
|
|
33
|
+
export type { FetchBpmnXml } from "./bpmn-name-lookup.js"
|
|
34
|
+
|
|
35
|
+
export interface AnalyticsWidgetToolsOptions {
|
|
36
|
+
/** The server's configured engine ids — every query resolves `engine` through it. */
|
|
37
|
+
engineScope: AnalyticsEngineScope
|
|
38
|
+
/** Used by the BPMN heatmap to fetch the diagram XML. Absent → non-diagram fallback. */
|
|
39
|
+
fetchBpmnXml?: FetchBpmnXml
|
|
40
|
+
/**
|
|
41
|
+
* The process and element names behind the ids the dashboards list
|
|
42
|
+
* (`createNameLookup` over the same BPMN lookup). Absent → the ids.
|
|
43
|
+
*/
|
|
44
|
+
namesFor?: NameLookup
|
|
45
|
+
/**
|
|
46
|
+
* Profile store: locale for model-facing summaries plus the caller's saved
|
|
47
|
+
* analytics defaults (`modules.analytics`) — the "explicit arg > saved
|
|
48
|
+
* setting > schema default" resolution for `period`/`minBucketSize`.
|
|
49
|
+
*/
|
|
50
|
+
profileStore?: ProfileSource
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Heatmap inputs shared by `analytics_show_bpmn_heatmap` and its
|
|
55
|
+
* `analytics_bpmn_heatmap_data` feed, composed from the exported client
|
|
56
|
+
* schemas so the describe() texts stay in one place.
|
|
57
|
+
*/
|
|
58
|
+
const heatmapInputShape = {
|
|
59
|
+
processDefinitionKey: schemas.elementBottleneckInput.shape.processDefinitionKey,
|
|
60
|
+
period: optionalPeriod,
|
|
61
|
+
...schemas.engineFilterShape,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Dashboard + its feed: one input shape, so the self-fetch can carry every scope the show tool takes. */
|
|
65
|
+
const dashboardInputShape = {
|
|
66
|
+
processDefinitionKey: schemas.clusterCompareInput.shape.processDefinitionKey,
|
|
67
|
+
period: optionalPeriod,
|
|
68
|
+
...schemas.engineFilterShape,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** "n/a" for a figure that was not measured — never a plausible 0. */
|
|
72
|
+
const orNa = (value: number | null) => value ?? "n/a"
|
|
73
|
+
|
|
74
|
+
/** The model-facing dashboard summary: window flows and the live state, never mixed. */
|
|
75
|
+
function dashboardSummary(t: ServerT, data: AnalyticsDashboardData, fleet: boolean): string {
|
|
76
|
+
return t("aSum.dashboard", {
|
|
77
|
+
scope: data.processDefinitionKey
|
|
78
|
+
? t("aSum.scopeForProcess", { key: data.processDefinitionKey })
|
|
79
|
+
: "",
|
|
80
|
+
period: data.period,
|
|
81
|
+
engines: engineScopeSummary(t, data.engines, fleet),
|
|
82
|
+
totalCount: data.totalCount,
|
|
83
|
+
completedCount: data.completedCount,
|
|
84
|
+
incidentsCreated: data.incidentsCreated,
|
|
85
|
+
incidentRatePct: orNa(data.incidentRatePct),
|
|
86
|
+
runningNow: orNa(data.runningNow),
|
|
87
|
+
openIncidentsNow: orNa(data.openIncidentsNow),
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function registerWidgetTools(
|
|
92
|
+
server: MCPServer,
|
|
93
|
+
ch: PrometheusClient,
|
|
94
|
+
options: AnalyticsWidgetToolsOptions,
|
|
95
|
+
) {
|
|
96
|
+
// Each show tool words its result via `await localizeViewFor(profileStore, ctx)`:
|
|
97
|
+
// `t` for the model-facing `summary` (→ "en" without a store or a caller
|
|
98
|
+
// identity), `title` for the view title, set only in a language the profile
|
|
99
|
+
// names (the kit's one rule for every module, #322 U3).
|
|
100
|
+
const { profileStore, engineScope } = options
|
|
101
|
+
const namesFor = options.namesFor ?? NO_NAMES
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Fetches the latest deployed version's BPMN XML for the heatmap overlay via
|
|
105
|
+
* the injected lookup, from the engines whose heat it shows, in
|
|
106
|
+
* configuration order: the first that has the model draws it, so a process
|
|
107
|
+
* that runs only on a later engine still gets its diagram. Without a
|
|
108
|
+
* lookup, or when every engine fails or finds nothing, the XML is `null`
|
|
109
|
+
* and `bpmnMissing` says which of the two happened, so the widget's
|
|
110
|
+
* fallback names the known cause (`heatmap-data.ts`).
|
|
111
|
+
*/
|
|
112
|
+
async function loadBpmn(
|
|
113
|
+
processDefinitionKey: string,
|
|
114
|
+
engines: readonly string[],
|
|
115
|
+
): Promise<{ bpmnXml: string | null; bpmnMissing?: BpmnMissingReason }> {
|
|
116
|
+
const fetchBpmnXml = options.fetchBpmnXml
|
|
117
|
+
if (!fetchBpmnXml) return { bpmnXml: null, bpmnMissing: "no-camunda7" }
|
|
118
|
+
for (const engine of engines.length > 0 ? engines : [undefined]) {
|
|
119
|
+
const bpmnXml = await fetchBpmnXml(processDefinitionKey, engine).catch(() => null)
|
|
120
|
+
if (bpmnXml) return { bpmnXml }
|
|
121
|
+
}
|
|
122
|
+
return { bpmnXml: null, bpmnMissing: "not-loaded" }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// --- Process Analytics Dashboard ---
|
|
126
|
+
server.tool(
|
|
127
|
+
{
|
|
128
|
+
name: "analytics_show_dashboard",
|
|
129
|
+
title: "Process Analytics Dashboard",
|
|
130
|
+
description:
|
|
131
|
+
"Show process metrics from Prometheus: flows within the period (starts, completions, incidents created/resolved, durations of the instances that ended) and the live state right now (instances running, incidents open), per process definition and per (process, activity).",
|
|
132
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
133
|
+
inputSchema: strictToolInput(dashboardInputShape),
|
|
134
|
+
...showToolBinding("analytics_show_dashboard", "Process Analytics Dashboard"),
|
|
135
|
+
},
|
|
136
|
+
withToolErrors(async (args, ctx) => {
|
|
137
|
+
const { t, title } = await localizeViewFor(profileStore, ctx)
|
|
138
|
+
const settings = await settingsFor(profileStore, ctx)
|
|
139
|
+
const period = args.period ?? settings.defaultPeriod
|
|
140
|
+
const data = await namedDashboard(
|
|
141
|
+
await queries.dashboardData(withCallerSignal(ch, ctx.signal), {
|
|
142
|
+
...withEngineScope(engineScope, args),
|
|
143
|
+
period,
|
|
144
|
+
minBucketSize: settings.minBucketSize,
|
|
145
|
+
}),
|
|
146
|
+
namesFor,
|
|
147
|
+
)
|
|
148
|
+
// The RESOLVED period and the requested scope travel as cell props, so a
|
|
149
|
+
// widget that self-fetches (structuredContent stripped, a saved view)
|
|
150
|
+
// reads the same scope instead of the fleet default.
|
|
151
|
+
const cellProps = {
|
|
152
|
+
period,
|
|
153
|
+
...(args.processDefinitionKey ? { processDefinitionKey: args.processDefinitionKey } : {}),
|
|
154
|
+
...(isFleetRequest(args.engine) ? {} : { engine: args.engine }),
|
|
155
|
+
}
|
|
156
|
+
return buildComposedView({
|
|
157
|
+
app: "analytics",
|
|
158
|
+
// The view title in the language the profile names (the widget heading's
|
|
159
|
+
// own key); none with "system", where the heading names the view.
|
|
160
|
+
title: title("aExecSummary.title"),
|
|
161
|
+
layout: [
|
|
162
|
+
{ row: [{ widget: "analytics:execution-summary-kpi", props: cellProps }] },
|
|
163
|
+
{ row: [{ widget: "analytics:execution-performance-kpi", props: cellProps }] },
|
|
164
|
+
{ row: [{ widget: "analytics:process-definition-breakdown", props: cellProps }] },
|
|
165
|
+
{ row: [{ widget: "analytics:activity-bottleneck-table", props: cellProps }] },
|
|
166
|
+
],
|
|
167
|
+
entries: [{ dataType: "analytics:dashboard", data }],
|
|
168
|
+
summary: dashboardSummary(t, data, isFleetRequest(args.engine)),
|
|
169
|
+
})
|
|
170
|
+
}),
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
// --- Failure Dashboard ---
|
|
174
|
+
server.tool(
|
|
175
|
+
{
|
|
176
|
+
name: "analytics_show_failure_dashboard",
|
|
177
|
+
title: "Failure Analysis Dashboard",
|
|
178
|
+
description:
|
|
179
|
+
"Show the incidents open right now from Prometheus, grouped by incident type and process definition (no activity, message or timestamps), with each affected process's running instances and dead jobs. Use to show the metric view to the user; for one engine's live incident clusters use camunda7_show_engine_health.",
|
|
180
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
181
|
+
inputSchema: strictToolInput({
|
|
182
|
+
...schemas.engineFilterShape,
|
|
183
|
+
}),
|
|
184
|
+
...showToolBinding("analytics_show_failure_dashboard", "Failure Analysis Dashboard"),
|
|
185
|
+
},
|
|
186
|
+
withToolErrors(async (args, ctx) => {
|
|
187
|
+
const { t, title } = await localizeViewFor(profileStore, ctx)
|
|
188
|
+
const data = await namedFailureDashboard(
|
|
189
|
+
await queries.failureDashboardData(
|
|
190
|
+
withCallerSignal(ch, ctx.signal),
|
|
191
|
+
withEngineScope(engineScope, args),
|
|
192
|
+
),
|
|
193
|
+
namesFor,
|
|
194
|
+
)
|
|
195
|
+
// The requested scope travels as cell props so a self-fetch keeps it.
|
|
196
|
+
const cellProps = isFleetRequest(args.engine) ? undefined : { engine: args.engine }
|
|
197
|
+
return buildComposedView({
|
|
198
|
+
app: "analytics",
|
|
199
|
+
title: title("aFailureSummary.title"),
|
|
200
|
+
layout: [
|
|
201
|
+
{ row: [{ widget: "analytics:failure-summary-kpi", props: cellProps }] },
|
|
202
|
+
{ row: [{ widget: "analytics:error-patterns-table", props: cellProps }] },
|
|
203
|
+
{ row: [{ widget: "analytics:failure-rate-table", props: cellProps }] },
|
|
204
|
+
],
|
|
205
|
+
entries: [{ dataType: "analytics:failureDashboard", data }],
|
|
206
|
+
summary: t("aSum.failureDashboard", {
|
|
207
|
+
engines: engineScopeSummary(t, data.engines, isFleetRequest(args.engine)),
|
|
208
|
+
totalIncidents: data.totalIncidents,
|
|
209
|
+
uniqueErrorPatterns: data.uniqueErrorPatterns,
|
|
210
|
+
mostAffected: data.mostAffectedProcess
|
|
211
|
+
? t("aSum.mostAffectedProcess", { key: data.mostAffectedProcess })
|
|
212
|
+
: "",
|
|
213
|
+
}),
|
|
214
|
+
})
|
|
215
|
+
}),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
registerComparisonWidgetTools({ server, ch, engineScope, profileStore, namesFor })
|
|
219
|
+
|
|
220
|
+
// --- BPMN Heatmap (per-element frequency + duration on the diagram) ---
|
|
221
|
+
server.tool(
|
|
222
|
+
{
|
|
223
|
+
name: "analytics_show_bpmn_heatmap",
|
|
224
|
+
title: "BPMN Heatmap",
|
|
225
|
+
description:
|
|
226
|
+
"Render a process definition's BPMN diagram with a per-element heat overlay from metrics, with a Frequency↔Duration toggle (traversal count vs average duration per element). Node-level only — sequence-flow/edge heat is not available from metrics — and rendered on the latest deployed version's diagram (activity metrics carry no version label). Needs the camunda7 client to fetch the BPMN XML; otherwise the widget shows a fallback.",
|
|
227
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
228
|
+
inputSchema: strictToolInput(heatmapInputShape),
|
|
229
|
+
...showToolBinding("analytics_show_bpmn_heatmap", "BPMN Heatmap"),
|
|
230
|
+
},
|
|
231
|
+
withToolErrors(async (args, ctx) => {
|
|
232
|
+
const { t, title } = await localizeViewFor(profileStore, ctx)
|
|
233
|
+
const period = args.period ?? (await settingsFor(profileStore, ctx)).defaultPeriod
|
|
234
|
+
const scoped = withEngineScope(engineScope, args)
|
|
235
|
+
const heat = await queries.elementHeat(withCallerSignal(ch, ctx.signal), {
|
|
236
|
+
...scoped,
|
|
237
|
+
period,
|
|
238
|
+
})
|
|
239
|
+
// Read here only to know whether a diagram exists (and why not); the
|
|
240
|
+
// XML stays out of the payload — the widget reads it from
|
|
241
|
+
// `analytics_bpmn_xml_data` (cached by the camunda7 lookup).
|
|
242
|
+
const bpmn = await loadBpmn(args.processDefinitionKey, scoped.engine)
|
|
243
|
+
return buildSingleWidgetView({
|
|
244
|
+
widget: "analytics:bpmn-heatmap",
|
|
245
|
+
app: "analytics",
|
|
246
|
+
dataType: "analytics:bpmnHeatmap",
|
|
247
|
+
// `AnalyticsBpmnHeatmapData` (widgets/bpmn-heatmap.tsx): the engines
|
|
248
|
+
// travel with the heat, so the model description names them.
|
|
249
|
+
data: {
|
|
250
|
+
processDefinitionKey: args.processDefinitionKey,
|
|
251
|
+
period,
|
|
252
|
+
engines: scoped.engine,
|
|
253
|
+
...(bpmn.bpmnMissing ? { bpmnMissing: bpmn.bpmnMissing } : {}),
|
|
254
|
+
frequency: heat.frequency,
|
|
255
|
+
durationSec: heat.durationSec,
|
|
256
|
+
asOf: heat.asOf,
|
|
257
|
+
},
|
|
258
|
+
title: title("aHeatmap.title"),
|
|
259
|
+
summary: t("aSum.bpmnHeatmap", {
|
|
260
|
+
key: args.processDefinitionKey,
|
|
261
|
+
period,
|
|
262
|
+
engines: engineScopeSummary(t, scoped.engine, isFleetRequest(args.engine)),
|
|
263
|
+
elementCount: Object.keys(heat.frequency).length,
|
|
264
|
+
fallbackNote: bpmn.bpmnXml ? "" : t("aSum.bpmnHeatmapNoXml"),
|
|
265
|
+
}),
|
|
266
|
+
})
|
|
267
|
+
}),
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
server.tool(
|
|
271
|
+
{
|
|
272
|
+
name: ANALYTICS_BPMN_HEATMAP_DATA,
|
|
273
|
+
title: "BPMN heatmap data (internal)",
|
|
274
|
+
description:
|
|
275
|
+
"Internal JSON feed (no UI) for the BPMN heatmap — per-element execution frequency + average duration over a window. Lets another widget (e.g. the CIB Seven cockpit) paint the heat on its own diagram. Prefer analytics_show_bpmn_heatmap for a standalone view.",
|
|
276
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
277
|
+
inputSchema: strictToolInput(heatmapInputShape),
|
|
278
|
+
...appOnly,
|
|
279
|
+
},
|
|
280
|
+
withToolErrors(async (args, ctx) => {
|
|
281
|
+
const period = args.period ?? (await settingsFor(profileStore, ctx)).defaultPeriod
|
|
282
|
+
const scoped = withEngineScope(engineScope, args)
|
|
283
|
+
const heat = await queries.elementHeat(withCallerSignal(ch, ctx.signal), {
|
|
284
|
+
...scoped,
|
|
285
|
+
period,
|
|
286
|
+
})
|
|
287
|
+
const data = {
|
|
288
|
+
processDefinitionKey: args.processDefinitionKey,
|
|
289
|
+
period,
|
|
290
|
+
engines: scoped.engine,
|
|
291
|
+
frequency: heat.frequency,
|
|
292
|
+
durationSec: heat.durationSec,
|
|
293
|
+
asOf: heat.asOf,
|
|
294
|
+
}
|
|
295
|
+
return buildDataFeedResult(data)
|
|
296
|
+
}),
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
server.tool(
|
|
300
|
+
{
|
|
301
|
+
name: ANALYTICS_BPMN_XML_DATA,
|
|
302
|
+
title: "BPMN heatmap diagram (internal)",
|
|
303
|
+
description:
|
|
304
|
+
"Internal JSON feed (no UI): the latest deployed BPMN XML of a process key on the engines in scope, for the heatmap view's diagram.",
|
|
305
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
306
|
+
inputSchema: strictToolInput({
|
|
307
|
+
processDefinitionKey: heatmapInputShape.processDefinitionKey,
|
|
308
|
+
...schemas.engineFilterShape,
|
|
309
|
+
}),
|
|
310
|
+
...appOnly,
|
|
311
|
+
},
|
|
312
|
+
withToolErrors(async (args) => {
|
|
313
|
+
const scoped = withEngineScope(engineScope, args)
|
|
314
|
+
return buildDataFeedResult({
|
|
315
|
+
processDefinitionKey: args.processDefinitionKey,
|
|
316
|
+
...(await loadBpmn(args.processDefinitionKey, scoped.engine)),
|
|
317
|
+
})
|
|
318
|
+
}),
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
// ── Per-view data feeds (plain, no UI) ──────────────────────────────────
|
|
322
|
+
// Backing the dashboard widgets' self-fetch. Self-fetching a show_* tool
|
|
323
|
+
// instead would be host-defined behavior: hosts honoring
|
|
324
|
+
// `resultCanProduceWidget` may render a second widget per refresh, and the
|
|
325
|
+
// show tool's localized model summary is generated for a call the model
|
|
326
|
+
// never sees.
|
|
327
|
+
|
|
328
|
+
server.tool(
|
|
329
|
+
{
|
|
330
|
+
name: ANALYTICS_DASHBOARD_DATA,
|
|
331
|
+
title: "Analytics dashboard data (internal)",
|
|
332
|
+
description:
|
|
333
|
+
"Internal JSON feed (no UI) for the analytics dashboard widgets' self-fetch. Prefer analytics_show_dashboard.",
|
|
334
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
335
|
+
inputSchema: strictToolInput(dashboardInputShape),
|
|
336
|
+
...appOnly,
|
|
337
|
+
},
|
|
338
|
+
withToolErrors(async (args, ctx) => {
|
|
339
|
+
const settings = await settingsFor(profileStore, ctx)
|
|
340
|
+
const data = await namedDashboard(
|
|
341
|
+
await queries.dashboardData(withCallerSignal(ch, ctx.signal), {
|
|
342
|
+
...withEngineScope(engineScope, args),
|
|
343
|
+
period: args.period ?? settings.defaultPeriod,
|
|
344
|
+
minBucketSize: settings.minBucketSize,
|
|
345
|
+
}),
|
|
346
|
+
namesFor,
|
|
347
|
+
)
|
|
348
|
+
return buildDataFeedResult({ ...data })
|
|
349
|
+
}),
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
server.tool(
|
|
353
|
+
{
|
|
354
|
+
name: ANALYTICS_FAILURE_DASHBOARD_DATA,
|
|
355
|
+
title: "Failure dashboard data (internal)",
|
|
356
|
+
description:
|
|
357
|
+
"Internal JSON feed (no UI) for the failure dashboard widgets' self-fetch. Prefer analytics_show_failure_dashboard.",
|
|
358
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
359
|
+
inputSchema: strictToolInput({
|
|
360
|
+
...schemas.engineFilterShape,
|
|
361
|
+
}),
|
|
362
|
+
...appOnly,
|
|
363
|
+
},
|
|
364
|
+
withToolErrors(async (args, ctx) => {
|
|
365
|
+
const data = await namedFailureDashboard(
|
|
366
|
+
await queries.failureDashboardData(
|
|
367
|
+
withCallerSignal(ch, ctx.signal),
|
|
368
|
+
withEngineScope(engineScope, args),
|
|
369
|
+
),
|
|
370
|
+
namesFor,
|
|
371
|
+
)
|
|
372
|
+
return buildDataFeedResult({ ...data })
|
|
373
|
+
}),
|
|
374
|
+
)
|
|
375
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HandOffButton,
|
|
3
|
+
ListTable,
|
|
4
|
+
NamedId,
|
|
5
|
+
Section,
|
|
6
|
+
TableEmptyState,
|
|
7
|
+
TableSkeleton,
|
|
8
|
+
Td,
|
|
9
|
+
WidgetShell,
|
|
10
|
+
formatDuration,
|
|
11
|
+
formatNumber,
|
|
12
|
+
type ListTableColumn,
|
|
13
|
+
} from "@miragon/pulse-widget-shell/widgets"
|
|
14
|
+
import type { AnalyticsDashboardData } from "@miragon/pulse-analytics-client"
|
|
15
|
+
import { useDashboardSelfFetch, type DashboardScopeProps, type DashboardView } from "./lib.js"
|
|
16
|
+
import { QueryGate } from "../query-gate.js"
|
|
17
|
+
import { useT, type T } from "../../messages/use-t.js"
|
|
18
|
+
import { engineIdsOf, useHandOff, type HandOff } from "../hand-off.js"
|
|
19
|
+
import { elementNameOf, processNameOf } from "../../bpmn-names.js"
|
|
20
|
+
import { activityTypeLabel } from "../names.js"
|
|
21
|
+
import { Figure, FigureLegend } from "../figure.js"
|
|
22
|
+
|
|
23
|
+
type ActivityRow = AnalyticsDashboardData["activityBreakdown"][number]
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Why ONE activity of one process is a bottleneck — over the scope of the
|
|
27
|
+
* data on screen (its `engines` and `period` echo), so the follow-up reads
|
|
28
|
+
* exactly the numbers shown.
|
|
29
|
+
*/
|
|
30
|
+
export function activityBottleneckHandOff(
|
|
31
|
+
act: ActivityRow,
|
|
32
|
+
data: Pick<AnalyticsDashboardData, "engines" | "period">,
|
|
33
|
+
): HandOff {
|
|
34
|
+
return {
|
|
35
|
+
intent: "askAi.activityBottleneck",
|
|
36
|
+
ids: {
|
|
37
|
+
engine: engineIdsOf(data.engines),
|
|
38
|
+
processDefinitionKey: act.processDefinitionKey,
|
|
39
|
+
period: data.period,
|
|
40
|
+
},
|
|
41
|
+
// Durations are null when nothing ended in the window — left out, never 0.
|
|
42
|
+
facts: {
|
|
43
|
+
activityId: act.activityId,
|
|
44
|
+
activityType: act.activityType,
|
|
45
|
+
executionCount: act.executionCount,
|
|
46
|
+
avgDurationMs: act.avgDurationMs,
|
|
47
|
+
p95DurationMs: act.p95DurationMs,
|
|
48
|
+
totalTimeMs: act.totalTimeMs,
|
|
49
|
+
},
|
|
50
|
+
tools: ["analytics_element_bottleneck", "analytics_analyze_process_performance"],
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The columns by priority (U2): the activity (named, with its id, type and
|
|
56
|
+
* process below), then the sort key right after it, the executions, and the
|
|
57
|
+
* two duration columns, which give way only below 640 px (the 760 px inline
|
|
58
|
+
* view shows every one). The chat hand-off is pinned at the right edge.
|
|
59
|
+
*/
|
|
60
|
+
function columns(t: T): ListTableColumn[] {
|
|
61
|
+
return [
|
|
62
|
+
{ label: t("aBottleneck.colActivity") },
|
|
63
|
+
{ label: t("aBottleneck.colTotalTime"), align: "right", sort: "descending" },
|
|
64
|
+
{ label: t("aBottleneck.colExecutions"), align: "right" },
|
|
65
|
+
{ label: t("aBottleneck.colAvg"), align: "right", hideBelow: "sm" },
|
|
66
|
+
{ label: t("aBottleneck.colP95"), align: "right", hideBelow: "sm" },
|
|
67
|
+
{ plain: true, sticky: true },
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* One row per (process, activity): BPMN ids are only unique within one model,
|
|
73
|
+
* so the same id in two processes is two rows — the second line says which
|
|
74
|
+
* process when the dashboard spans several. The list is the top of the
|
|
75
|
+
* breakdown by total time; its heading says so when it is cut.
|
|
76
|
+
*/
|
|
77
|
+
export function ActivityBottleneckTable({
|
|
78
|
+
data: initialData,
|
|
79
|
+
processDefinitionKey,
|
|
80
|
+
period,
|
|
81
|
+
engine,
|
|
82
|
+
}: { data: DashboardView | null } & DashboardScopeProps) {
|
|
83
|
+
const t = useT()
|
|
84
|
+
const { ask } = useHandOff()
|
|
85
|
+
const fallbackQuery = useDashboardSelfFetch(initialData, { processDefinitionKey, period, engine })
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<QueryGate initialData={initialData} query={fallbackQuery} skeleton={<TableSkeleton />}>
|
|
89
|
+
{(data) => {
|
|
90
|
+
const rows = data.activityBreakdown
|
|
91
|
+
if (rows.length === 0) {
|
|
92
|
+
return (
|
|
93
|
+
<WidgetShell>
|
|
94
|
+
<TableEmptyState description={t("aBottleneck.emptyHint")}>
|
|
95
|
+
{t("aBottleneck.emptyState")}
|
|
96
|
+
</TableEmptyState>
|
|
97
|
+
</WidgetShell>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
const cut = data.activityCount > rows.length
|
|
101
|
+
const fleet = data.processDefinitionKey === null
|
|
102
|
+
return (
|
|
103
|
+
<WidgetShell>
|
|
104
|
+
<Section
|
|
105
|
+
title={
|
|
106
|
+
cut
|
|
107
|
+
? t("aBottleneck.headingTop", { count: formatNumber(rows.length) })
|
|
108
|
+
: t("aBottleneck.heading")
|
|
109
|
+
}
|
|
110
|
+
count={cut ? undefined : rows.length}
|
|
111
|
+
defaultOpen
|
|
112
|
+
>
|
|
113
|
+
{cut && (
|
|
114
|
+
<p className="text-muted-foreground text-small mb-2">
|
|
115
|
+
{t("aBottleneck.topNote", {
|
|
116
|
+
count: formatNumber(rows.length),
|
|
117
|
+
total: formatNumber(data.activityCount),
|
|
118
|
+
})}
|
|
119
|
+
</p>
|
|
120
|
+
)}
|
|
121
|
+
<ListTable ariaLabel={t("aBottleneck.tableLabel")} columns={columns(t)}>
|
|
122
|
+
{rows.map((act) => (
|
|
123
|
+
<tr key={`${act.processDefinitionKey}:${act.activityId}`}>
|
|
124
|
+
<Td>
|
|
125
|
+
<NamedId
|
|
126
|
+
name={elementNameOf(data.names, act.processDefinitionKey, act.activityId)}
|
|
127
|
+
id={act.activityId}
|
|
128
|
+
detail={[
|
|
129
|
+
activityTypeLabel(t, act.activityType),
|
|
130
|
+
fleet
|
|
131
|
+
? (processNameOf(data.names, act.processDefinitionKey) ??
|
|
132
|
+
act.processDefinitionKey)
|
|
133
|
+
: null,
|
|
134
|
+
]}
|
|
135
|
+
/>
|
|
136
|
+
</Td>
|
|
137
|
+
<Td align="right" className="font-medium whitespace-nowrap tabular-nums">
|
|
138
|
+
{formatDuration(act.totalTimeMs)}
|
|
139
|
+
</Td>
|
|
140
|
+
<Td align="right" className="tabular-nums">
|
|
141
|
+
{formatNumber(act.executionCount)}
|
|
142
|
+
</Td>
|
|
143
|
+
<Td align="right" className="whitespace-nowrap tabular-nums">
|
|
144
|
+
<Figure
|
|
145
|
+
value={act.avgDurationMs}
|
|
146
|
+
format={formatDuration}
|
|
147
|
+
why={t("aFigure.nothingEnded")}
|
|
148
|
+
/>
|
|
149
|
+
</Td>
|
|
150
|
+
<Td align="right" className="whitespace-nowrap tabular-nums">
|
|
151
|
+
<Figure
|
|
152
|
+
value={act.p95DurationMs}
|
|
153
|
+
format={formatDuration}
|
|
154
|
+
why={t("aFigure.nothingEnded")}
|
|
155
|
+
/>
|
|
156
|
+
</Td>
|
|
157
|
+
<Td align="right">
|
|
158
|
+
<HandOffButton
|
|
159
|
+
action="explainBottleneck"
|
|
160
|
+
variant="icon"
|
|
161
|
+
prompt={ask(activityBottleneckHandOff(act, data))}
|
|
162
|
+
/>
|
|
163
|
+
</Td>
|
|
164
|
+
</tr>
|
|
165
|
+
))}
|
|
166
|
+
</ListTable>
|
|
167
|
+
<FigureLegend
|
|
168
|
+
reasons={[
|
|
169
|
+
rows.some((a) => a.avgDurationMs === null || a.p95DurationMs === null) &&
|
|
170
|
+
t("aFigure.nothingEnded"),
|
|
171
|
+
]}
|
|
172
|
+
/>
|
|
173
|
+
</Section>
|
|
174
|
+
</WidgetShell>
|
|
175
|
+
)
|
|
176
|
+
}}
|
|
177
|
+
</QueryGate>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ListTable,
|
|
3
|
+
NamedId,
|
|
4
|
+
Section,
|
|
5
|
+
TableEmptyState,
|
|
6
|
+
TableSkeleton,
|
|
7
|
+
Td,
|
|
8
|
+
WidgetShell,
|
|
9
|
+
formatDuration,
|
|
10
|
+
formatNumber,
|
|
11
|
+
type ListTableColumn,
|
|
12
|
+
} from "@miragon/pulse-widget-shell/widgets"
|
|
13
|
+
import { useDashboardSelfFetch, type DashboardScopeProps, type DashboardView } from "./lib.js"
|
|
14
|
+
import { QueryGate } from "../query-gate.js"
|
|
15
|
+
import { useT, type T } from "../../messages/use-t.js"
|
|
16
|
+
import { processNameOf } from "../../bpmn-names.js"
|
|
17
|
+
import { Figure, FigureLegend } from "../figure.js"
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The figures side by side (U17): one row per process, the counts in
|
|
21
|
+
* right-aligned tabular columns, so the processes compare at a glance. The
|
|
22
|
+
* average duration gives way below 640 px. Every count here is a volume of
|
|
23
|
+
* the period or a live count without a state, new incidents included, so
|
|
24
|
+
* none carries a tone (U14): the summary above shows them the same way.
|
|
25
|
+
*/
|
|
26
|
+
function columns(t: T): ListTableColumn[] {
|
|
27
|
+
return [
|
|
28
|
+
{ label: t("aDefBreakdown.colProcess") },
|
|
29
|
+
{ label: t("aDefBreakdown.colStarted"), align: "right" },
|
|
30
|
+
{ label: t("aDefBreakdown.colCompleted"), align: "right" },
|
|
31
|
+
{ label: t("aDefBreakdown.colRunningNow"), align: "right" },
|
|
32
|
+
{ label: t("aDefBreakdown.colIncidents"), align: "right" },
|
|
33
|
+
{ label: t("aDefBreakdown.colAvgDuration"), align: "right", hideBelow: "sm" },
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function ProcessDefinitionBreakdown({
|
|
38
|
+
data: initialData,
|
|
39
|
+
processDefinitionKey,
|
|
40
|
+
period,
|
|
41
|
+
engine,
|
|
42
|
+
}: { data: DashboardView | null } & DashboardScopeProps) {
|
|
43
|
+
const t = useT()
|
|
44
|
+
const fallbackQuery = useDashboardSelfFetch(initialData, { processDefinitionKey, period, engine })
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<QueryGate initialData={initialData} query={fallbackQuery} skeleton={<TableSkeleton />}>
|
|
48
|
+
{(data) =>
|
|
49
|
+
data.definitionBreakdown.length === 0 ? (
|
|
50
|
+
<WidgetShell>
|
|
51
|
+
<TableEmptyState description={t("aDefBreakdown.emptyHint")}>
|
|
52
|
+
{t("aDefBreakdown.emptyState")}
|
|
53
|
+
</TableEmptyState>
|
|
54
|
+
</WidgetShell>
|
|
55
|
+
) : (
|
|
56
|
+
<WidgetShell>
|
|
57
|
+
<Section
|
|
58
|
+
title={t("aDefBreakdown.heading")}
|
|
59
|
+
count={data.definitionBreakdown.length}
|
|
60
|
+
defaultOpen
|
|
61
|
+
>
|
|
62
|
+
<ListTable ariaLabel={t("aDefBreakdown.tableLabel")} columns={columns(t)}>
|
|
63
|
+
{data.definitionBreakdown.map((def) => (
|
|
64
|
+
<tr key={def.processDefinitionKey}>
|
|
65
|
+
<Td>
|
|
66
|
+
<NamedId
|
|
67
|
+
name={processNameOf(data.names, def.processDefinitionKey)}
|
|
68
|
+
id={def.processDefinitionKey}
|
|
69
|
+
/>
|
|
70
|
+
</Td>
|
|
71
|
+
<Td align="right" className="tabular-nums">
|
|
72
|
+
{formatNumber(def.totalInstances)}
|
|
73
|
+
</Td>
|
|
74
|
+
<Td align="right" className="tabular-nums">
|
|
75
|
+
{formatNumber(def.completed)}
|
|
76
|
+
</Td>
|
|
77
|
+
{/* Not reported (no engine in scope sends its gauges): "—", never 0. */}
|
|
78
|
+
<Td align="right" className="tabular-nums">
|
|
79
|
+
<Figure value={def.runningNow} why={t("aFigure.notReported")} />
|
|
80
|
+
</Td>
|
|
81
|
+
<Td align="right" className="tabular-nums">
|
|
82
|
+
{formatNumber(def.incidentsCreated)}
|
|
83
|
+
</Td>
|
|
84
|
+
<Td align="right" className="whitespace-nowrap tabular-nums">
|
|
85
|
+
<Figure
|
|
86
|
+
value={def.avgDurationMs}
|
|
87
|
+
format={formatDuration}
|
|
88
|
+
why={t("aFigure.nothingEnded")}
|
|
89
|
+
/>
|
|
90
|
+
</Td>
|
|
91
|
+
</tr>
|
|
92
|
+
))}
|
|
93
|
+
</ListTable>
|
|
94
|
+
<FigureLegend
|
|
95
|
+
reasons={[
|
|
96
|
+
data.definitionBreakdown.some((d) => d.runningNow === null) &&
|
|
97
|
+
t("aFigure.notReported"),
|
|
98
|
+
data.definitionBreakdown.some((d) => d.avgDurationMs === null) &&
|
|
99
|
+
t("aFigure.nothingEnded"),
|
|
100
|
+
]}
|
|
101
|
+
/>
|
|
102
|
+
</Section>
|
|
103
|
+
</WidgetShell>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
</QueryGate>
|
|
107
|
+
)
|
|
108
|
+
}
|