@acmekit/dashboard 2.13.33 → 2.13.35
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/dist/{api-key-management-create-4AG76FJV.mjs → api-key-management-create-U37VC624.mjs} +3 -3
- package/dist/{api-key-management-detail-T2TB4KST.mjs → api-key-management-detail-ZYKL4ATI.mjs} +10 -10
- package/dist/{api-key-management-edit-R44OHS7B.mjs → api-key-management-edit-TSZGMIBL.mjs} +3 -3
- package/dist/{api-key-management-list-QK4Q7Y5I.mjs → api-key-management-list-HCJFJWWB.mjs} +3 -3
- package/dist/app.css +31 -0
- package/dist/app.js +3726 -1386
- package/dist/app.mjs +240 -38
- package/dist/{chunk-GBFVWROS.mjs → chunk-5IEHCYJO.mjs} +1 -1
- package/dist/{chunk-DQCEH3X2.mjs → chunk-7F3CWXUH.mjs} +1 -1
- package/dist/chunk-A7ULKHDE.mjs +126 -0
- package/dist/{chunk-DN3MIYQH.mjs → chunk-FKTMBR44.mjs} +1 -1
- package/dist/chunk-GBPAZAJK.mjs +34 -0
- package/dist/{chunk-YRWSG3YM.mjs → chunk-HHPPTD3B.mjs} +1 -1
- package/dist/chunk-LP6CPB7N.mjs +213 -0
- package/dist/{chunk-EFRMWHRX.mjs → chunk-PFZQYK7R.mjs} +1 -1
- package/dist/{chunk-XIM7X4FB.mjs → chunk-SYACY6AL.mjs} +1 -1
- package/dist/{chunk-2U3RK3JG.mjs → chunk-VEI6HW6L.mjs} +3 -5
- package/dist/{chunk-ST2YB7JN.mjs → chunk-WLRJXEKL.mjs} +1 -1
- package/dist/{chunk-ULSPL3DR.mjs → chunk-XIP35KXF.mjs} +1 -1
- package/dist/{chunk-DTY37DDZ.mjs → chunk-YKIWIMJX.mjs} +1 -0
- package/dist/en.json +132 -3
- package/dist/{invite-XGPZZBUP.mjs → invite-3JSNOA2B.mjs} +3 -3
- package/dist/{login-GNP3QIPI.mjs → login-BEJ5EFGE.mjs} +9 -9
- package/dist/{profile-detail-YX27F7N6.mjs → profile-detail-QVTJC4JC.mjs} +3 -3
- package/dist/{profile-edit-2VRDU75O.mjs → profile-edit-MIO62TWH.mjs} +3 -3
- package/dist/{reset-password-TWRNZO6Z.mjs → reset-password-BN4KAJQL.mjs} +2 -2
- package/dist/{settings-3XWLL5LG.mjs → settings-GH5IWXHE.mjs} +3 -3
- package/dist/{translation-list-CCEQJNED.mjs → translation-list-JA22BUKN.mjs} +10 -10
- package/dist/{translations-edit-E57GVUFV.mjs → translations-edit-STTMANVT.mjs} +11 -11
- package/dist/{user-detail-KUSRRVNX.mjs → user-detail-WCXBFRGS.mjs} +3 -3
- package/dist/{user-edit-HTN3ZGCL.mjs → user-edit-XDVMJOS4.mjs} +3 -3
- package/dist/{user-invite-E3FAAU3V.mjs → user-invite-73ZDSDFC.mjs} +3 -3
- package/dist/{user-list-KNJ5S3IM.mjs → user-list-MPJXE3CA.mjs} +5 -5
- package/dist/{user-metadata-5GQK75DT.mjs → user-metadata-ADNTL3LT.mjs} +10 -10
- package/dist/workflow-analytics-4WCI4ODQ.mjs +152 -0
- package/dist/workflow-definition-detail-GI6CFBMG.mjs +94 -0
- package/dist/workflow-definition-list-GF3XAEPS.mjs +142 -0
- package/dist/workflow-execution-complete-step-WSRLO572.mjs +245 -0
- package/dist/workflow-execution-detail-3RH6EQSS.mjs +1411 -0
- package/dist/workflow-execution-list-AQEGAME4.mjs +596 -0
- package/dist/workflow-execution-rerun-WCYLYL3Q.mjs +138 -0
- package/dist/workflow-execution-run-MWN5KWNY.mjs +135 -0
- package/dist/workflow-scheduled-list-ZPXR7CZM.mjs +174 -0
- package/package.json +9 -9
- package/src/components/layout/main-layout/main-layout.tsx +28 -1
- package/src/dashboard-app/routes/get-route.map.tsx +71 -0
- package/src/hooks/api/workflow-definitions.tsx +79 -0
- package/src/hooks/api/workflow-executions.tsx +145 -1
- package/src/hooks/api/workflow-metrics.tsx +48 -0
- package/src/hooks/use-workflow-sse.tsx +78 -0
- package/src/i18n/translations/$schema.json +534 -4
- package/src/i18n/translations/en.json +132 -3
- package/src/routes/workflow-analytics/workflow-analytics.tsx +167 -0
- package/src/routes/workflow-definitions/workflow-definition-detail/workflow-definition-detail.tsx +98 -0
- package/src/routes/workflow-definitions/workflow-definition-list/components/workflow-definition-list-table/use-workflow-definition-table-columns.tsx +78 -0
- package/src/routes/workflow-definitions/workflow-definition-list/components/workflow-definition-list-table/workflow-definition-list-table.tsx +65 -0
- package/src/routes/workflow-definitions/workflow-definition-list/workflow-definition-list.tsx +15 -0
- package/src/routes/workflow-executions/constants.ts +16 -0
- package/src/routes/workflow-executions/utils.ts +170 -14
- package/src/routes/workflow-executions/workflow-execution-complete-step/workflow-execution-complete-step.tsx +270 -0
- package/src/routes/workflow-executions/workflow-execution-detail/breadcrumb.tsx +7 -1
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-action-bar/index.ts +1 -0
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-action-bar/workflow-execution-action-bar.tsx +212 -0
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-error-card/index.ts +1 -0
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-error-card/workflow-execution-error-card.tsx +59 -0
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-history-section/workflow-execution-history-section.tsx +157 -6
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-payload-section/workflow-execution-payload-section.tsx +122 -6
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-timeline-section/workflow-execution-timeline-section.tsx +7 -1
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-waiting-banner/index.ts +1 -0
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-waiting-banner/workflow-execution-waiting-banner.tsx +63 -0
- package/src/routes/workflow-executions/workflow-execution-detail/workflow-detail.tsx +46 -1
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/use-workflow-execution-table-columns.tsx +7 -0
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/use-workflow-execution-table-filters.tsx +7 -1
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/use-workflow-execution-table-query.tsx +4 -2
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-auto-refresh.tsx +73 -0
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-list-table.tsx +17 -1
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-row-actions.tsx +116 -0
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-saved-views.tsx +84 -0
- package/src/routes/workflow-executions/workflow-execution-list/workflow-execution-list.tsx +1 -1
- package/src/routes/workflow-executions/workflow-execution-rerun/workflow-execution-rerun.tsx +159 -0
- package/src/routes/workflow-executions/workflow-execution-run/workflow-execution-run.tsx +139 -0
- package/src/routes/workflow-scheduled/workflow-scheduled-list.tsx +269 -0
- package/dist/chunk-LKWTBYYC.mjs +0 -35
- package/dist/chunk-RPAL6FHW.mjs +0 -73
- package/dist/workflow-execution-detail-5O5VCXL3.mjs +0 -870
- package/dist/workflow-execution-list-DETG4MRT.mjs +0 -347
- /package/dist/{chunk-22YYMH6M.mjs → chunk-RISX76YT.mjs} +0 -0
|
@@ -2975,7 +2975,10 @@
|
|
|
2975
2975
|
"stepsCompletedLabel_one": "{{completed}} of {{count}} step",
|
|
2976
2976
|
"stepsCompletedLabel_other": "{{completed}} of {{count}} steps",
|
|
2977
2977
|
"list": {
|
|
2978
|
-
"noRecordsMessage": "No workflows have been executed, yet."
|
|
2978
|
+
"noRecordsMessage": "No workflows have been executed, yet.",
|
|
2979
|
+
"noFilterResults": "No executions match your current filters.",
|
|
2980
|
+
"clearFilters": "Clear filters",
|
|
2981
|
+
"viewAll": "View all executions"
|
|
2979
2982
|
},
|
|
2980
2983
|
"history": {
|
|
2981
2984
|
"sectionTitle": "History",
|
|
@@ -2988,7 +2991,11 @@
|
|
|
2988
2991
|
"outputLabel": "Output",
|
|
2989
2992
|
"compensateInputLabel": "Compensate input",
|
|
2990
2993
|
"revertedLabel": "Reverted",
|
|
2991
|
-
"errorLabel": "Error"
|
|
2994
|
+
"errorLabel": "Error",
|
|
2995
|
+
"idempotencyKeyLabel": "Idempotency key",
|
|
2996
|
+
"showCompensation": "Show compensation",
|
|
2997
|
+
"showInvoke": "Show invoke",
|
|
2998
|
+
"retryFromStep": "Retry from this step"
|
|
2992
2999
|
},
|
|
2993
3000
|
"state": {
|
|
2994
3001
|
"done": "Done",
|
|
@@ -2996,7 +3003,8 @@
|
|
|
2996
3003
|
"reverted": "Reverted",
|
|
2997
3004
|
"invoking": "Invoking",
|
|
2998
3005
|
"compensating": "Compensating",
|
|
2999
|
-
"notStarted": "Not started"
|
|
3006
|
+
"notStarted": "Not started",
|
|
3007
|
+
"waitingResponse": "Waiting for response"
|
|
3000
3008
|
},
|
|
3001
3009
|
"transaction": {
|
|
3002
3010
|
"state": {
|
|
@@ -3010,6 +3018,127 @@
|
|
|
3010
3018
|
"dormant": "Dormant",
|
|
3011
3019
|
"timeout": "Timeout"
|
|
3012
3020
|
}
|
|
3021
|
+
},
|
|
3022
|
+
"actions": {
|
|
3023
|
+
"retry": "Retry",
|
|
3024
|
+
"retryWithSameInput": "Retry with same input",
|
|
3025
|
+
"retryWithNewInput": "Retry with new input...",
|
|
3026
|
+
"cancel": "Cancel execution",
|
|
3027
|
+
"cancelConfirmTitle": "Cancel workflow execution?",
|
|
3028
|
+
"cancelConfirmDescription": "This will stop {{workflow_id}} and run compensation for all completed steps.",
|
|
3029
|
+
"cancelReasonLabel": "Reason (optional)",
|
|
3030
|
+
"cancelSuccess": "Workflow execution cancelled successfully",
|
|
3031
|
+
"retrySuccess": "Workflow re-run started successfully",
|
|
3032
|
+
"retryStepSuccess": "Step retry initiated successfully",
|
|
3033
|
+
"runWorkflow": "Run workflow",
|
|
3034
|
+
"exportJson": "Export as JSON",
|
|
3035
|
+
"exportCsv": "Export as CSV",
|
|
3036
|
+
"completeStep": "Complete step",
|
|
3037
|
+
"markAsSuccessful": "Mark as successful",
|
|
3038
|
+
"markAsFailed": "Mark as failed",
|
|
3039
|
+
"viewDetails": "View details"
|
|
3040
|
+
},
|
|
3041
|
+
"rerun": {
|
|
3042
|
+
"title": "Re-run workflow",
|
|
3043
|
+
"inputLabel": "Input (JSON)",
|
|
3044
|
+
"transactionIdLabel": "Transaction ID (optional)",
|
|
3045
|
+
"submit": "Run workflow",
|
|
3046
|
+
"autoGenerated": "auto-generated",
|
|
3047
|
+
"success": "Workflow re-run started successfully"
|
|
3048
|
+
},
|
|
3049
|
+
"error": {
|
|
3050
|
+
"failureAtStep": "Failure at step: {{step}}",
|
|
3051
|
+
"jumpToStep": "Jump to step",
|
|
3052
|
+
"attemptInfo": "Attempt {{attempt}} of {{maxAttempts}}",
|
|
3053
|
+
"failedAt": "Failed at {{time}}"
|
|
3054
|
+
},
|
|
3055
|
+
"filters": {
|
|
3056
|
+
"savedViews": "Saved views",
|
|
3057
|
+
"saveCurrentFilter": "Save current filter",
|
|
3058
|
+
"savedViewName": "View name",
|
|
3059
|
+
"autoRefresh": "Auto-refresh",
|
|
3060
|
+
"refreshNow": "Refresh now",
|
|
3061
|
+
"everyNSeconds": "Every {{n}}s",
|
|
3062
|
+
"off": "Off",
|
|
3063
|
+
"hasErrors": "Has errors",
|
|
3064
|
+
"all": "All",
|
|
3065
|
+
"allExecutions": "All executions",
|
|
3066
|
+
"running": "Running",
|
|
3067
|
+
"failedToday": "Failed today"
|
|
3068
|
+
},
|
|
3069
|
+
"definitions": {
|
|
3070
|
+
"domain": "Definitions",
|
|
3071
|
+
"subtitle": "Browse all registered workflow definitions.",
|
|
3072
|
+
"name": "Name",
|
|
3073
|
+
"steps": "Steps",
|
|
3074
|
+
"stepCount": "Steps",
|
|
3075
|
+
"handlers": "Handlers",
|
|
3076
|
+
"lastRun": "Last run",
|
|
3077
|
+
"nextRun": "Next run",
|
|
3078
|
+
"scheduled": "Scheduled",
|
|
3079
|
+
"runThisWorkflow": "Run this workflow",
|
|
3080
|
+
"noRecordsMessage": "No workflow definitions found."
|
|
3081
|
+
},
|
|
3082
|
+
"scheduled": {
|
|
3083
|
+
"domain": "Scheduled",
|
|
3084
|
+
"subtitle": "View and manage scheduled workflow executions.",
|
|
3085
|
+
"schedule": "Schedule",
|
|
3086
|
+
"nextRun": "Next run",
|
|
3087
|
+
"lastRun": "Last run",
|
|
3088
|
+
"pause": "Pause",
|
|
3089
|
+
"resume": "Resume",
|
|
3090
|
+
"runNow": "Run now",
|
|
3091
|
+
"noRecordsMessage": "No scheduled workflows found."
|
|
3092
|
+
},
|
|
3093
|
+
"analytics": {
|
|
3094
|
+
"domain": "Analytics",
|
|
3095
|
+
"subtitle": "Workflow execution metrics and trends.",
|
|
3096
|
+
"executions24h": "Executions (24h)",
|
|
3097
|
+
"successRate": "Success rate",
|
|
3098
|
+
"avgDuration": "Avg duration",
|
|
3099
|
+
"runningNow": "Running now",
|
|
3100
|
+
"executionsOverTime": "Executions over time",
|
|
3101
|
+
"perWorkflowBreakdown": "Per-workflow breakdown",
|
|
3102
|
+
"slowestSteps": "Slowest steps",
|
|
3103
|
+
"noDataMessage": "No metrics data available yet."
|
|
3104
|
+
},
|
|
3105
|
+
"asyncStep": {
|
|
3106
|
+
"waitingBanner": "Waiting for external response",
|
|
3107
|
+
"waitingDescription": "Step {{step}} is awaiting a webhook callback.",
|
|
3108
|
+
"idempotencyKey": "Idempotency key",
|
|
3109
|
+
"completeTitle": "Complete step: {{step}}",
|
|
3110
|
+
"responsePayload": "Response payload (JSON)",
|
|
3111
|
+
"expiresIn": "Expires in {{time}}",
|
|
3112
|
+
"stepLabel": "Step",
|
|
3113
|
+
"outcomeLabel": "Outcome",
|
|
3114
|
+
"success": "Success",
|
|
3115
|
+
"failure": "Failure",
|
|
3116
|
+
"markSuccess": "Mark as success",
|
|
3117
|
+
"markFailure": "Mark as failure",
|
|
3118
|
+
"completedSuccess": "Step completed successfully"
|
|
3119
|
+
},
|
|
3120
|
+
"tabs": {
|
|
3121
|
+
"executions": "Executions",
|
|
3122
|
+
"definitions": "Definitions",
|
|
3123
|
+
"scheduled": "Scheduled",
|
|
3124
|
+
"analytics": "Analytics"
|
|
3125
|
+
},
|
|
3126
|
+
"payload": {
|
|
3127
|
+
"inputPayload": "Input payload",
|
|
3128
|
+
"fullCheckpoint": "Full checkpoint",
|
|
3129
|
+
"stepOutputs": "Step outputs",
|
|
3130
|
+
"errors": "Errors",
|
|
3131
|
+
"copyAll": "Copy all",
|
|
3132
|
+
"downloadJson": "Download as JSON"
|
|
3133
|
+
},
|
|
3134
|
+
"codeSnippet": {
|
|
3135
|
+
"curl": "cURL",
|
|
3136
|
+
"sdk": "SDK",
|
|
3137
|
+
"replicateCall": "Replicate this call"
|
|
3138
|
+
},
|
|
3139
|
+
"compare": {
|
|
3140
|
+
"title": "Compare executions",
|
|
3141
|
+
"selectExecution": "Select execution to compare"
|
|
3013
3142
|
}
|
|
3014
3143
|
},
|
|
3015
3144
|
"shippingOptionTypes": {
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Badge,
|
|
3
|
+
Container,
|
|
4
|
+
Heading,
|
|
5
|
+
Text,
|
|
6
|
+
} from "@acmekit/ui"
|
|
7
|
+
import { useTranslation } from "react-i18next"
|
|
8
|
+
import { SingleColumnPage } from "../../components/layout/pages"
|
|
9
|
+
import { useWorkflowMetrics } from "../../hooks/api/workflow-metrics"
|
|
10
|
+
|
|
11
|
+
export const WorkflowAnalytics = () => {
|
|
12
|
+
const { t } = useTranslation()
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
total_24h,
|
|
16
|
+
success_rate_24h,
|
|
17
|
+
avg_duration_ms_24h,
|
|
18
|
+
running_now,
|
|
19
|
+
per_workflow,
|
|
20
|
+
isPending: isLoading,
|
|
21
|
+
} = useWorkflowMetrics()
|
|
22
|
+
|
|
23
|
+
const formatDuration = (ms: number) => {
|
|
24
|
+
if (ms < 1000) return `${ms}ms`
|
|
25
|
+
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`
|
|
26
|
+
const min = Math.floor(ms / 60000)
|
|
27
|
+
const sec = Math.round((ms % 60000) / 1000)
|
|
28
|
+
return `${min}m ${sec}s`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const formatRate = (rate: number) => `${(rate * 100).toFixed(1)}%`
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<SingleColumnPage widgets={{ before: [], after: [] }}>
|
|
35
|
+
<Container className="divide-y p-0">
|
|
36
|
+
<div className="px-6 py-4">
|
|
37
|
+
<Heading>{t("workflowExecutions.analytics.domain")}</Heading>
|
|
38
|
+
<Text className="text-ui-fg-subtle" size="small">
|
|
39
|
+
{t("workflowExecutions.analytics.subtitle")}
|
|
40
|
+
</Text>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div className="grid grid-cols-4 gap-px bg-ui-border-base">
|
|
44
|
+
<MetricCard
|
|
45
|
+
label={t("workflowExecutions.analytics.executions24h")}
|
|
46
|
+
value={isLoading ? "-" : String(total_24h ?? 0)}
|
|
47
|
+
subtitle="Last 24h"
|
|
48
|
+
/>
|
|
49
|
+
<MetricCard
|
|
50
|
+
label={t("workflowExecutions.analytics.successRate")}
|
|
51
|
+
value={isLoading ? "-" : formatRate(success_rate_24h ?? 0)}
|
|
52
|
+
subtitle="Last 24h"
|
|
53
|
+
color={
|
|
54
|
+
(success_rate_24h ?? 1) >= 0.9
|
|
55
|
+
? "green"
|
|
56
|
+
: (success_rate_24h ?? 1) >= 0.7
|
|
57
|
+
? "orange"
|
|
58
|
+
: "red"
|
|
59
|
+
}
|
|
60
|
+
/>
|
|
61
|
+
<MetricCard
|
|
62
|
+
label={t("workflowExecutions.analytics.avgDuration")}
|
|
63
|
+
value={
|
|
64
|
+
isLoading ? "-" : formatDuration(avg_duration_ms_24h ?? 0)
|
|
65
|
+
}
|
|
66
|
+
subtitle="Last 24h"
|
|
67
|
+
/>
|
|
68
|
+
<MetricCard
|
|
69
|
+
label={t("workflowExecutions.analytics.runningNow")}
|
|
70
|
+
value={isLoading ? "-" : String(running_now ?? 0)}
|
|
71
|
+
color={(running_now ?? 0) > 0 ? "blue" : undefined}
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
</Container>
|
|
75
|
+
|
|
76
|
+
{per_workflow && per_workflow.length > 0 && (
|
|
77
|
+
<Container className="divide-y p-0">
|
|
78
|
+
<div className="px-6 py-4">
|
|
79
|
+
<Heading level="h2">
|
|
80
|
+
{t("workflowExecutions.analytics.perWorkflowBreakdown")}
|
|
81
|
+
</Heading>
|
|
82
|
+
</div>
|
|
83
|
+
<div className="overflow-x-auto">
|
|
84
|
+
<table className="w-full">
|
|
85
|
+
<thead>
|
|
86
|
+
<tr className="border-ui-border-base border-b">
|
|
87
|
+
<th className="text-ui-fg-subtle txt-compact-small-plus px-6 py-2 text-left">
|
|
88
|
+
Workflow
|
|
89
|
+
</th>
|
|
90
|
+
<th className="text-ui-fg-subtle txt-compact-small-plus px-6 py-2 text-right">
|
|
91
|
+
Runs
|
|
92
|
+
</th>
|
|
93
|
+
<th className="text-ui-fg-subtle txt-compact-small-plus px-6 py-2 text-right">
|
|
94
|
+
Success Rate
|
|
95
|
+
</th>
|
|
96
|
+
<th className="text-ui-fg-subtle txt-compact-small-plus px-6 py-2 text-right">
|
|
97
|
+
Avg Duration
|
|
98
|
+
</th>
|
|
99
|
+
</tr>
|
|
100
|
+
</thead>
|
|
101
|
+
<tbody>
|
|
102
|
+
{per_workflow.map((wf) => (
|
|
103
|
+
<tr
|
|
104
|
+
key={wf.workflow_id}
|
|
105
|
+
className="border-ui-border-base border-b last:border-b-0"
|
|
106
|
+
>
|
|
107
|
+
<td className="px-6 py-3">
|
|
108
|
+
<Badge size="2xsmall">{wf.workflow_id}</Badge>
|
|
109
|
+
</td>
|
|
110
|
+
<td className="txt-compact-small text-ui-fg-subtle px-6 py-3 text-right">
|
|
111
|
+
{wf.runs}
|
|
112
|
+
</td>
|
|
113
|
+
<td className="txt-compact-small text-ui-fg-subtle px-6 py-3 text-right">
|
|
114
|
+
{formatRate(wf.success_rate)}
|
|
115
|
+
</td>
|
|
116
|
+
<td className="txt-compact-small text-ui-fg-subtle px-6 py-3 text-right">
|
|
117
|
+
{formatDuration(wf.avg_duration_ms)}
|
|
118
|
+
</td>
|
|
119
|
+
</tr>
|
|
120
|
+
))}
|
|
121
|
+
</tbody>
|
|
122
|
+
</table>
|
|
123
|
+
</div>
|
|
124
|
+
</Container>
|
|
125
|
+
)}
|
|
126
|
+
</SingleColumnPage>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const MetricCard = ({
|
|
131
|
+
label,
|
|
132
|
+
value,
|
|
133
|
+
subtitle,
|
|
134
|
+
color,
|
|
135
|
+
}: {
|
|
136
|
+
label: string
|
|
137
|
+
value: string
|
|
138
|
+
subtitle?: string
|
|
139
|
+
color?: "green" | "orange" | "red" | "blue"
|
|
140
|
+
}) => {
|
|
141
|
+
const colorClass = color
|
|
142
|
+
? {
|
|
143
|
+
green: "text-ui-tag-green-icon",
|
|
144
|
+
orange: "text-ui-tag-orange-icon",
|
|
145
|
+
red: "text-ui-tag-red-icon",
|
|
146
|
+
blue: "text-ui-tag-blue-icon",
|
|
147
|
+
}[color]
|
|
148
|
+
: "text-ui-fg-base"
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<div className="bg-ui-bg-base flex flex-col gap-y-1 px-6 py-4">
|
|
152
|
+
<Text size="small" className="text-ui-fg-subtle">
|
|
153
|
+
{label}
|
|
154
|
+
</Text>
|
|
155
|
+
<Text size="xlarge" weight="plus" className={colorClass}>
|
|
156
|
+
{value}
|
|
157
|
+
</Text>
|
|
158
|
+
{subtitle && (
|
|
159
|
+
<Text size="xsmall" className="text-ui-fg-muted">
|
|
160
|
+
{subtitle}
|
|
161
|
+
</Text>
|
|
162
|
+
)}
|
|
163
|
+
</div>
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export const Component = WorkflowAnalytics
|
package/src/routes/workflow-definitions/workflow-definition-detail/workflow-definition-detail.tsx
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { ArrowPathMini } from "@acmekit/icons"
|
|
2
|
+
import {
|
|
3
|
+
Badge,
|
|
4
|
+
Button,
|
|
5
|
+
Container,
|
|
6
|
+
Copy,
|
|
7
|
+
Heading,
|
|
8
|
+
Text,
|
|
9
|
+
} from "@acmekit/ui"
|
|
10
|
+
import { useTranslation } from "react-i18next"
|
|
11
|
+
import { Link, useParams } from "react-router-dom"
|
|
12
|
+
import { SingleColumnPageSkeleton } from "../../../components/common/skeleton"
|
|
13
|
+
import { SingleColumnPage } from "../../../components/layout/pages"
|
|
14
|
+
import { useWorkflowDefinition } from "../../../hooks/api/workflow-definitions"
|
|
15
|
+
|
|
16
|
+
export const WorkflowDefinitionDetail = () => {
|
|
17
|
+
const { id } = useParams()
|
|
18
|
+
const { t } = useTranslation()
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
workflow_definition: definition,
|
|
22
|
+
isPending: isLoading,
|
|
23
|
+
isError,
|
|
24
|
+
error,
|
|
25
|
+
} = useWorkflowDefinition(id!)
|
|
26
|
+
|
|
27
|
+
if (isLoading) {
|
|
28
|
+
return <SingleColumnPageSkeleton sections={2} showJSON />
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isError) {
|
|
32
|
+
throw error
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!definition) {
|
|
36
|
+
return <SingleColumnPageSkeleton sections={2} showJSON />
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<SingleColumnPage
|
|
41
|
+
widgets={{ before: [], after: [] }}
|
|
42
|
+
data={definition}
|
|
43
|
+
showJSON
|
|
44
|
+
hasOutlet
|
|
45
|
+
>
|
|
46
|
+
<Container className="divide-y p-0">
|
|
47
|
+
<div className="flex items-center justify-between px-6 py-4">
|
|
48
|
+
<div className="flex items-center gap-x-3">
|
|
49
|
+
<Copy content={definition.id} asChild>
|
|
50
|
+
<Badge size="2xsmall" className="cursor-pointer">
|
|
51
|
+
{definition.id}
|
|
52
|
+
</Badge>
|
|
53
|
+
</Copy>
|
|
54
|
+
</div>
|
|
55
|
+
<Button variant="secondary" size="small" asChild>
|
|
56
|
+
<Link to="run">
|
|
57
|
+
<ArrowPathMini className="mr-1" />
|
|
58
|
+
{t("workflowExecutions.actions.runWorkflow")}
|
|
59
|
+
</Link>
|
|
60
|
+
</Button>
|
|
61
|
+
</div>
|
|
62
|
+
</Container>
|
|
63
|
+
|
|
64
|
+
<Container className="divide-y p-0">
|
|
65
|
+
<div className="px-6 py-4">
|
|
66
|
+
<Heading level="h2">
|
|
67
|
+
{t("workflowExecutions.definitions.steps")}
|
|
68
|
+
</Heading>
|
|
69
|
+
<Text size="small" className="text-ui-fg-subtle mt-1">
|
|
70
|
+
{t("workflowExecutions.definitions.stepCount")}:{" "}
|
|
71
|
+
{definition.steps?.length || 0}
|
|
72
|
+
</Text>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="divide-y">
|
|
75
|
+
{(definition.steps || []).map((step, idx) => (
|
|
76
|
+
<div
|
|
77
|
+
key={`${step.action}-${idx}`}
|
|
78
|
+
className="flex items-center justify-between px-6 py-3"
|
|
79
|
+
style={{ paddingLeft: `${1.5 + step.depth * 1.5}rem` }}
|
|
80
|
+
>
|
|
81
|
+
<div className="flex items-center gap-x-2">
|
|
82
|
+
<span className="text-ui-fg-muted txt-compact-xsmall">
|
|
83
|
+
{step.depth > 0 ? "└" : "●"}
|
|
84
|
+
</span>
|
|
85
|
+
<Badge size="2xsmall">{step.action}</Badge>
|
|
86
|
+
</div>
|
|
87
|
+
<span className="text-ui-fg-muted txt-compact-xsmall">
|
|
88
|
+
{step.noCompensation ? "no compensation" : ""}
|
|
89
|
+
</span>
|
|
90
|
+
</div>
|
|
91
|
+
))}
|
|
92
|
+
</div>
|
|
93
|
+
</Container>
|
|
94
|
+
</SingleColumnPage>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const Component = WorkflowDefinitionDetail
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Badge, Copy } from "@acmekit/ui"
|
|
2
|
+
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
|
|
3
|
+
import { useMemo } from "react"
|
|
4
|
+
import { useTranslation } from "react-i18next"
|
|
5
|
+
|
|
6
|
+
type WorkflowDefinition = {
|
|
7
|
+
id: string
|
|
8
|
+
handler_count: number
|
|
9
|
+
steps: Array<{ action: string; depth: number; noCompensation: boolean }>
|
|
10
|
+
options: Record<string, unknown>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const columnHelper = createColumnHelper<WorkflowDefinition>()
|
|
14
|
+
|
|
15
|
+
export const useWorkflowDefinitionTableColumns = (): ColumnDef<
|
|
16
|
+
WorkflowDefinition,
|
|
17
|
+
any
|
|
18
|
+
>[] => {
|
|
19
|
+
const { t } = useTranslation()
|
|
20
|
+
|
|
21
|
+
return useMemo(
|
|
22
|
+
() => [
|
|
23
|
+
columnHelper.accessor("id", {
|
|
24
|
+
header: t("workflowExecutions.definitions.name"),
|
|
25
|
+
cell: ({ getValue }) => {
|
|
26
|
+
const id = getValue()
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex items-center gap-x-1">
|
|
29
|
+
<Badge size="2xsmall" className="truncate">
|
|
30
|
+
{id}
|
|
31
|
+
</Badge>
|
|
32
|
+
<Copy content={id} className="text-ui-fg-muted" />
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
columnHelper.accessor("steps", {
|
|
38
|
+
header: t("workflowExecutions.definitions.stepCount"),
|
|
39
|
+
cell: ({ getValue }) => {
|
|
40
|
+
const steps = getValue()
|
|
41
|
+
return (
|
|
42
|
+
<span className="text-ui-fg-subtle txt-compact-small">
|
|
43
|
+
{steps?.length || 0} {t("workflowExecutions.definitions.steps")}
|
|
44
|
+
</span>
|
|
45
|
+
)
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
columnHelper.accessor("handler_count", {
|
|
49
|
+
header: t("workflowExecutions.definitions.handlers"),
|
|
50
|
+
cell: ({ getValue }) => {
|
|
51
|
+
return (
|
|
52
|
+
<span className="text-ui-fg-subtle txt-compact-small">
|
|
53
|
+
{getValue() || 0}
|
|
54
|
+
</span>
|
|
55
|
+
)
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
columnHelper.display({
|
|
59
|
+
id: "schedule",
|
|
60
|
+
header: t("workflowExecutions.scheduled.schedule"),
|
|
61
|
+
cell: ({ row }) => {
|
|
62
|
+
const schedule = (row.original.options as any)?.schedule
|
|
63
|
+
if (!schedule) {
|
|
64
|
+
return (
|
|
65
|
+
<span className="text-ui-fg-muted txt-compact-small">-</span>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
return (
|
|
69
|
+
<Badge size="2xsmall" color="purple">
|
|
70
|
+
{String(schedule)}
|
|
71
|
+
</Badge>
|
|
72
|
+
)
|
|
73
|
+
},
|
|
74
|
+
}),
|
|
75
|
+
],
|
|
76
|
+
[t]
|
|
77
|
+
)
|
|
78
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Container, Heading, Text } from "@acmekit/ui"
|
|
2
|
+
import { keepPreviousData } from "@tanstack/react-query"
|
|
3
|
+
import { useTranslation } from "react-i18next"
|
|
4
|
+
import { _DataTable } from "../../../../../components/table/data-table"
|
|
5
|
+
import { useWorkflowDefinitions } from "../../../../../hooks/api/workflow-definitions"
|
|
6
|
+
import { useDataTable } from "../../../../../hooks/use-data-table"
|
|
7
|
+
import { useWorkflowDefinitionTableColumns } from "./use-workflow-definition-table-columns"
|
|
8
|
+
|
|
9
|
+
const PAGE_SIZE = 20
|
|
10
|
+
|
|
11
|
+
export const WorkflowDefinitionListTable = () => {
|
|
12
|
+
const { t } = useTranslation()
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
workflow_definitions,
|
|
16
|
+
count,
|
|
17
|
+
isPending: isLoading,
|
|
18
|
+
isError,
|
|
19
|
+
error,
|
|
20
|
+
} = useWorkflowDefinitions(undefined, {
|
|
21
|
+
placeholderData: keepPreviousData,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const definitions = workflow_definitions || []
|
|
25
|
+
|
|
26
|
+
const columns = useWorkflowDefinitionTableColumns()
|
|
27
|
+
|
|
28
|
+
const { table } = useDataTable({
|
|
29
|
+
data: definitions,
|
|
30
|
+
columns,
|
|
31
|
+
count,
|
|
32
|
+
pageSize: PAGE_SIZE,
|
|
33
|
+
enablePagination: true,
|
|
34
|
+
getRowId: (row: any) => row.id,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
if (isError) {
|
|
38
|
+
throw error
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Container className="divide-y p-0">
|
|
43
|
+
<div className="flex items-center justify-between px-6 py-4">
|
|
44
|
+
<div>
|
|
45
|
+
<Heading>
|
|
46
|
+
{t("workflowExecutions.definitions.domain")}
|
|
47
|
+
</Heading>
|
|
48
|
+
<Text className="text-ui-fg-subtle" size="small">
|
|
49
|
+
{t("workflowExecutions.definitions.subtitle")}
|
|
50
|
+
</Text>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
<_DataTable
|
|
54
|
+
table={table}
|
|
55
|
+
columns={columns}
|
|
56
|
+
count={count}
|
|
57
|
+
isLoading={isLoading}
|
|
58
|
+
pageSize={PAGE_SIZE}
|
|
59
|
+
navigateTo={(row: any) => row.id}
|
|
60
|
+
search
|
|
61
|
+
pagination
|
|
62
|
+
/>
|
|
63
|
+
</Container>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SingleColumnPage } from "../../../components/layout/pages"
|
|
2
|
+
import { WorkflowDefinitionListTable } from "./components/workflow-definition-list-table/workflow-definition-list-table"
|
|
3
|
+
|
|
4
|
+
export const WorkflowDefinitionList = () => {
|
|
5
|
+
return (
|
|
6
|
+
<SingleColumnPage
|
|
7
|
+
widgets={{ before: [], after: [] }}
|
|
8
|
+
hasOutlet
|
|
9
|
+
>
|
|
10
|
+
<WorkflowDefinitionListTable />
|
|
11
|
+
</SingleColumnPage>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Component = WorkflowDefinitionList
|
|
@@ -36,3 +36,19 @@ export const TRANSACTION_IN_PROGRESS_STATES: HttpTypes.TransactionState[] = [
|
|
|
36
36
|
TransactionState.WAITING_TO_COMPENSATE,
|
|
37
37
|
TransactionState.COMPENSATING,
|
|
38
38
|
]
|
|
39
|
+
|
|
40
|
+
export const TRANSACTION_TERMINAL_STATES: HttpTypes.TransactionState[] = [
|
|
41
|
+
TransactionState.DONE,
|
|
42
|
+
TransactionState.FAILED,
|
|
43
|
+
TransactionState.REVERTED,
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
export const TRANSACTION_ACTIVE_STATES: HttpTypes.TransactionState[] = [
|
|
47
|
+
TransactionState.INVOKING,
|
|
48
|
+
TransactionState.COMPENSATING,
|
|
49
|
+
TransactionState.WAITING_TO_COMPENSATE,
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
export const STEP_WAITING_STATES: HttpTypes.TransactionStepState[] = [
|
|
53
|
+
TransactionStepState.INVOKING, // waiting_response comes through as invoking state in the step
|
|
54
|
+
]
|