@acmekit/dashboard 2.13.35 → 2.13.36
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/package.json +9 -9
- package/src/components/layout/main-layout/main-layout.tsx +1 -28
- package/src/dashboard-app/routes/get-route.map.tsx +0 -71
- package/src/hooks/api/workflow-executions.tsx +1 -145
- package/src/i18n/translations/$schema.json +4 -534
- package/src/i18n/translations/en.json +3 -132
- package/src/routes/workflow-executions/constants.ts +0 -16
- package/src/routes/workflow-executions/utils.ts +14 -170
- package/src/routes/workflow-executions/workflow-execution-detail/breadcrumb.tsx +1 -7
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-history-section/workflow-execution-history-section.tsx +6 -157
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-payload-section/workflow-execution-payload-section.tsx +6 -122
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-timeline-section/workflow-execution-timeline-section.tsx +1 -7
- package/src/routes/workflow-executions/workflow-execution-detail/workflow-detail.tsx +1 -46
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/use-workflow-execution-table-columns.tsx +0 -7
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/use-workflow-execution-table-filters.tsx +1 -7
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/use-workflow-execution-table-query.tsx +2 -4
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-list-table.tsx +1 -17
- package/src/routes/workflow-executions/workflow-execution-list/workflow-execution-list.tsx +1 -1
- package/src/hooks/api/workflow-definitions.tsx +0 -79
- package/src/hooks/api/workflow-metrics.tsx +0 -48
- package/src/hooks/use-workflow-sse.tsx +0 -78
- package/src/routes/workflow-analytics/workflow-analytics.tsx +0 -167
- package/src/routes/workflow-definitions/workflow-definition-detail/workflow-definition-detail.tsx +0 -98
- package/src/routes/workflow-definitions/workflow-definition-list/components/workflow-definition-list-table/use-workflow-definition-table-columns.tsx +0 -78
- package/src/routes/workflow-definitions/workflow-definition-list/components/workflow-definition-list-table/workflow-definition-list-table.tsx +0 -65
- package/src/routes/workflow-definitions/workflow-definition-list/workflow-definition-list.tsx +0 -15
- package/src/routes/workflow-executions/workflow-execution-complete-step/workflow-execution-complete-step.tsx +0 -270
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-action-bar/index.ts +0 -1
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-action-bar/workflow-execution-action-bar.tsx +0 -212
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-error-card/index.ts +0 -1
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-error-card/workflow-execution-error-card.tsx +0 -59
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-waiting-banner/index.ts +0 -1
- package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-waiting-banner/workflow-execution-waiting-banner.tsx +0 -63
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-auto-refresh.tsx +0 -73
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-row-actions.tsx +0 -116
- package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-saved-views.tsx +0 -84
- package/src/routes/workflow-executions/workflow-execution-rerun/workflow-execution-rerun.tsx +0 -159
- package/src/routes/workflow-executions/workflow-execution-run/workflow-execution-run.tsx +0 -139
- package/src/routes/workflow-scheduled/workflow-scheduled-list.tsx +0 -269
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { ArrowPathMini, EllipsisHorizontal, XMark } from "@acmekit/icons"
|
|
2
|
-
import { HttpTypes } from "@acmekit/types"
|
|
3
|
-
import {
|
|
4
|
-
Badge,
|
|
5
|
-
Button,
|
|
6
|
-
Container,
|
|
7
|
-
Copy,
|
|
8
|
-
StatusBadge,
|
|
9
|
-
toast,
|
|
10
|
-
usePrompt,
|
|
11
|
-
} from "@acmekit/ui"
|
|
12
|
-
import { useTranslation } from "react-i18next"
|
|
13
|
-
import { Link } from "react-router-dom"
|
|
14
|
-
import { ActionMenu } from "../../../../../components/common/action-menu"
|
|
15
|
-
import {
|
|
16
|
-
useCancelWorkflowExecution,
|
|
17
|
-
useRunWorkflow,
|
|
18
|
-
} from "../../../../../hooks/api/workflow-executions"
|
|
19
|
-
import {
|
|
20
|
-
TRANSACTION_ERROR_STATES,
|
|
21
|
-
TRANSACTION_IN_PROGRESS_STATES,
|
|
22
|
-
} from "../../../constants"
|
|
23
|
-
import { TransactionState } from "../../../types"
|
|
24
|
-
import {
|
|
25
|
-
getTransactionState,
|
|
26
|
-
getTransactionStateColor,
|
|
27
|
-
getWaitingSteps,
|
|
28
|
-
} from "../../../utils"
|
|
29
|
-
|
|
30
|
-
type WorkflowExecutionActionBarProps = {
|
|
31
|
-
execution: HttpTypes.AdminWorkflowExecution
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const WorkflowExecutionActionBar = ({
|
|
35
|
-
execution,
|
|
36
|
-
}: WorkflowExecutionActionBarProps) => {
|
|
37
|
-
const { t } = useTranslation()
|
|
38
|
-
const prompt = usePrompt()
|
|
39
|
-
|
|
40
|
-
const state = execution.state as TransactionState
|
|
41
|
-
const stateColor = getTransactionStateColor(state)
|
|
42
|
-
const translatedState = getTransactionState(t, state)
|
|
43
|
-
|
|
44
|
-
const workflowId = execution.workflow_id
|
|
45
|
-
const transactionId = execution.transaction_id
|
|
46
|
-
|
|
47
|
-
const isFailed = TRANSACTION_ERROR_STATES.includes(state)
|
|
48
|
-
const isRunning = TRANSACTION_IN_PROGRESS_STATES.includes(state)
|
|
49
|
-
const waitingSteps = getWaitingSteps(execution)
|
|
50
|
-
const hasWaitingSteps = waitingSteps.length > 0
|
|
51
|
-
|
|
52
|
-
const { mutateAsync: runAsync, isPending: isRunning_ } =
|
|
53
|
-
useRunWorkflow(workflowId)
|
|
54
|
-
const { mutateAsync: cancelAsync, isPending: isCancelling } =
|
|
55
|
-
useCancelWorkflowExecution(workflowId, transactionId)
|
|
56
|
-
|
|
57
|
-
const handleRetry = async () => {
|
|
58
|
-
const payload = (execution as any)?.context?.data?.payload
|
|
59
|
-
const input =
|
|
60
|
-
payload && typeof payload === "object"
|
|
61
|
-
? (payload as Record<string, unknown>)
|
|
62
|
-
: {}
|
|
63
|
-
await runAsync(
|
|
64
|
-
{ input },
|
|
65
|
-
{
|
|
66
|
-
onSuccess: () => {
|
|
67
|
-
toast.success(t("workflowExecutions.actions.retrySuccess"))
|
|
68
|
-
},
|
|
69
|
-
onError: (err) => {
|
|
70
|
-
toast.error(err.message)
|
|
71
|
-
},
|
|
72
|
-
}
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const handleCancel = async () => {
|
|
77
|
-
const res = await prompt({
|
|
78
|
-
title: t("workflowExecutions.actions.cancelConfirmTitle"),
|
|
79
|
-
description: t("workflowExecutions.actions.cancelConfirmDescription", {
|
|
80
|
-
workflow_id: workflowId,
|
|
81
|
-
}),
|
|
82
|
-
confirmText: t("workflowExecutions.actions.cancel"),
|
|
83
|
-
cancelText: t("actions.cancel"),
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
if (!res) return
|
|
87
|
-
|
|
88
|
-
await cancelAsync(undefined, {
|
|
89
|
-
onSuccess: () => {
|
|
90
|
-
toast.success(t("workflowExecutions.actions.cancelSuccess"))
|
|
91
|
-
},
|
|
92
|
-
onError: (err) => {
|
|
93
|
-
toast.error(err.message)
|
|
94
|
-
},
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const handleExportJson = () => {
|
|
99
|
-
const data = JSON.stringify(execution, null, 2)
|
|
100
|
-
const blob = new Blob([data], { type: "application/json" })
|
|
101
|
-
const url = URL.createObjectURL(blob)
|
|
102
|
-
const a = document.createElement("a")
|
|
103
|
-
a.href = url
|
|
104
|
-
a.download = `workflow-${workflowId}-${execution.id}.json`
|
|
105
|
-
a.click()
|
|
106
|
-
URL.revokeObjectURL(url)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const handleExportCsv = () => {
|
|
110
|
-
const steps = execution?.execution?.steps || {}
|
|
111
|
-
const rows = [["Step", "State", "Duration"]]
|
|
112
|
-
for (const [stepId, step] of Object.entries(steps)) {
|
|
113
|
-
if (stepId === "_root") continue
|
|
114
|
-
const invokeState = (step as any)?.invoke?.state || "unknown"
|
|
115
|
-
rows.push([stepId, invokeState, ""])
|
|
116
|
-
}
|
|
117
|
-
const csv = rows.map((r) => r.join(",")).join("\n")
|
|
118
|
-
const blob = new Blob([csv], { type: "text/csv" })
|
|
119
|
-
const url = URL.createObjectURL(blob)
|
|
120
|
-
const a = document.createElement("a")
|
|
121
|
-
a.href = url
|
|
122
|
-
a.download = `workflow-${workflowId}-${execution.id}.csv`
|
|
123
|
-
a.click()
|
|
124
|
-
URL.revokeObjectURL(url)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const cleanId = execution.id.replace("wf_exec_", "")
|
|
128
|
-
|
|
129
|
-
return (
|
|
130
|
-
<Container className="sticky top-14 z-10 p-0">
|
|
131
|
-
<div className="flex items-center justify-between px-6 py-3">
|
|
132
|
-
<div className="flex items-center gap-x-3">
|
|
133
|
-
<Copy content={workflowId} asChild>
|
|
134
|
-
<Badge size="2xsmall" className="cursor-pointer">
|
|
135
|
-
{workflowId}
|
|
136
|
-
</Badge>
|
|
137
|
-
</Copy>
|
|
138
|
-
<StatusBadge color={stateColor}>{translatedState}</StatusBadge>
|
|
139
|
-
<Copy content={execution.id} asChild>
|
|
140
|
-
<Badge
|
|
141
|
-
size="2xsmall"
|
|
142
|
-
color="grey"
|
|
143
|
-
className="cursor-pointer text-ui-fg-muted"
|
|
144
|
-
>
|
|
145
|
-
{cleanId}
|
|
146
|
-
</Badge>
|
|
147
|
-
</Copy>
|
|
148
|
-
</div>
|
|
149
|
-
<div className="flex items-center gap-x-2">
|
|
150
|
-
{isFailed && (
|
|
151
|
-
<Button
|
|
152
|
-
variant="secondary"
|
|
153
|
-
size="small"
|
|
154
|
-
onClick={handleRetry}
|
|
155
|
-
isLoading={isRunning_}
|
|
156
|
-
>
|
|
157
|
-
<ArrowPathMini className="mr-1" />
|
|
158
|
-
{t("workflowExecutions.actions.retry")}
|
|
159
|
-
</Button>
|
|
160
|
-
)}
|
|
161
|
-
{isRunning && (
|
|
162
|
-
<Button
|
|
163
|
-
variant="danger"
|
|
164
|
-
size="small"
|
|
165
|
-
onClick={handleCancel}
|
|
166
|
-
isLoading={isCancelling}
|
|
167
|
-
>
|
|
168
|
-
<XMark className="mr-1" />
|
|
169
|
-
{t("workflowExecutions.actions.cancel")}
|
|
170
|
-
</Button>
|
|
171
|
-
)}
|
|
172
|
-
{hasWaitingSteps && (
|
|
173
|
-
<Button variant="secondary" size="small" asChild>
|
|
174
|
-
<Link to="complete-step">
|
|
175
|
-
{t("workflowExecutions.actions.completeStep")}
|
|
176
|
-
</Link>
|
|
177
|
-
</Button>
|
|
178
|
-
)}
|
|
179
|
-
<ActionMenu
|
|
180
|
-
groups={[
|
|
181
|
-
{
|
|
182
|
-
actions: [
|
|
183
|
-
...(isFailed
|
|
184
|
-
? [
|
|
185
|
-
{
|
|
186
|
-
label: t(
|
|
187
|
-
"workflowExecutions.actions.retryWithNewInput"
|
|
188
|
-
),
|
|
189
|
-
icon: <ArrowPathMini />,
|
|
190
|
-
to: "rerun",
|
|
191
|
-
},
|
|
192
|
-
]
|
|
193
|
-
: []),
|
|
194
|
-
{
|
|
195
|
-
label: t("workflowExecutions.actions.exportJson"),
|
|
196
|
-
icon: <EllipsisHorizontal />,
|
|
197
|
-
onClick: handleExportJson,
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
label: t("workflowExecutions.actions.exportCsv"),
|
|
201
|
-
icon: <EllipsisHorizontal />,
|
|
202
|
-
onClick: handleExportCsv,
|
|
203
|
-
},
|
|
204
|
-
],
|
|
205
|
-
},
|
|
206
|
-
]}
|
|
207
|
-
/>
|
|
208
|
-
</div>
|
|
209
|
-
</div>
|
|
210
|
-
</Container>
|
|
211
|
-
)
|
|
212
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { WorkflowExecutionErrorCard } from "./workflow-execution-error-card"
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { XCircle } from "@acmekit/icons"
|
|
2
|
-
import { HttpTypes } from "@acmekit/types"
|
|
3
|
-
import { Container, Heading, Text } from "@acmekit/ui"
|
|
4
|
-
import { useTranslation } from "react-i18next"
|
|
5
|
-
import { TransactionState } from "../../../types"
|
|
6
|
-
import { getFailedSteps } from "../../../utils"
|
|
7
|
-
|
|
8
|
-
type WorkflowExecutionErrorCardProps = {
|
|
9
|
-
execution: HttpTypes.AdminWorkflowExecution
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const WorkflowExecutionErrorCard = ({
|
|
13
|
-
execution,
|
|
14
|
-
}: WorkflowExecutionErrorCardProps) => {
|
|
15
|
-
const { t } = useTranslation()
|
|
16
|
-
|
|
17
|
-
if ((execution.state as TransactionState) !== TransactionState.FAILED) {
|
|
18
|
-
return null
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const failedSteps = getFailedSteps(execution)
|
|
22
|
-
if (failedSteps.length === 0) return null
|
|
23
|
-
|
|
24
|
-
const firstFailure = failedSteps[0]
|
|
25
|
-
const errorMessage =
|
|
26
|
-
firstFailure.error?.error?.message ||
|
|
27
|
-
firstFailure.error?.error ||
|
|
28
|
-
"Unknown error"
|
|
29
|
-
const handlerType = firstFailure.error?.handlerType || "invoke"
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<Container className="border border-ui-tag-red-border bg-ui-tag-red-bg p-0">
|
|
33
|
-
<div className="flex items-start gap-x-3 px-6 py-4">
|
|
34
|
-
<div className="mt-0.5">
|
|
35
|
-
<XCircle className="text-ui-tag-red-icon" />
|
|
36
|
-
</div>
|
|
37
|
-
<div className="flex flex-1 flex-col gap-y-1">
|
|
38
|
-
<Heading level="h3">
|
|
39
|
-
{t("workflowExecutions.error.failureAtStep", {
|
|
40
|
-
step: firstFailure.stepId,
|
|
41
|
-
})}
|
|
42
|
-
</Heading>
|
|
43
|
-
<Text size="small" className="text-ui-fg-subtle">
|
|
44
|
-
{String(errorMessage)}
|
|
45
|
-
</Text>
|
|
46
|
-
<Text size="xsmall" className="text-ui-fg-muted">
|
|
47
|
-
{handlerType === "compensate" ? "Compensation" : "Invoke"} failure
|
|
48
|
-
</Text>
|
|
49
|
-
</div>
|
|
50
|
-
<a
|
|
51
|
-
href={`#${firstFailure.stepId}`}
|
|
52
|
-
className="text-ui-fg-interactive txt-compact-small-plus shrink-0 hover:underline"
|
|
53
|
-
>
|
|
54
|
-
{t("workflowExecutions.error.jumpToStep")}
|
|
55
|
-
</a>
|
|
56
|
-
</div>
|
|
57
|
-
</Container>
|
|
58
|
-
)
|
|
59
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { WorkflowExecutionWaitingBanner } from "./workflow-execution-waiting-banner"
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { ClockSolidMini } from "@acmekit/icons"
|
|
2
|
-
import { HttpTypes } from "@acmekit/types"
|
|
3
|
-
import { Container, Copy, Heading, Text } from "@acmekit/ui"
|
|
4
|
-
import { useTranslation } from "react-i18next"
|
|
5
|
-
import { Link } from "react-router-dom"
|
|
6
|
-
import { computeIdempotencyKey, getWaitingSteps } from "../../../utils"
|
|
7
|
-
|
|
8
|
-
type WorkflowExecutionWaitingBannerProps = {
|
|
9
|
-
execution: HttpTypes.AdminWorkflowExecution
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const WorkflowExecutionWaitingBanner = ({
|
|
13
|
-
execution,
|
|
14
|
-
}: WorkflowExecutionWaitingBannerProps) => {
|
|
15
|
-
const { t } = useTranslation()
|
|
16
|
-
const waitingSteps = getWaitingSteps(execution)
|
|
17
|
-
|
|
18
|
-
if (waitingSteps.length === 0) return null
|
|
19
|
-
|
|
20
|
-
const firstStep = waitingSteps[0]
|
|
21
|
-
const idempotencyKey = computeIdempotencyKey(
|
|
22
|
-
execution.workflow_id,
|
|
23
|
-
execution.transaction_id,
|
|
24
|
-
firstStep.stepId,
|
|
25
|
-
"invoke"
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<Container className="border border-ui-tag-purple-border bg-ui-tag-purple-bg p-0">
|
|
30
|
-
<div className="flex items-start gap-x-3 px-6 py-4">
|
|
31
|
-
<div className="mt-0.5">
|
|
32
|
-
<ClockSolidMini className="text-ui-tag-purple-icon" />
|
|
33
|
-
</div>
|
|
34
|
-
<div className="flex flex-1 flex-col gap-y-1">
|
|
35
|
-
<Heading level="h3">
|
|
36
|
-
{t("workflowExecutions.asyncStep.waitingBanner")}
|
|
37
|
-
</Heading>
|
|
38
|
-
<Text size="small" className="text-ui-fg-subtle">
|
|
39
|
-
{t("workflowExecutions.asyncStep.waitingDescription", {
|
|
40
|
-
step: firstStep.stepId,
|
|
41
|
-
})}
|
|
42
|
-
</Text>
|
|
43
|
-
<div className="mt-1 flex items-center gap-x-2">
|
|
44
|
-
<Text size="xsmall" className="text-ui-fg-muted">
|
|
45
|
-
{t("workflowExecutions.asyncStep.idempotencyKey")}:
|
|
46
|
-
</Text>
|
|
47
|
-
<Copy content={idempotencyKey} asChild>
|
|
48
|
-
<code className="bg-ui-bg-base txt-compact-xsmall cursor-pointer rounded px-1.5 py-0.5">
|
|
49
|
-
{idempotencyKey}
|
|
50
|
-
</code>
|
|
51
|
-
</Copy>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
<Link
|
|
55
|
-
to="complete-step"
|
|
56
|
-
className="text-ui-fg-interactive txt-compact-small-plus shrink-0 hover:underline"
|
|
57
|
-
>
|
|
58
|
-
{t("workflowExecutions.actions.completeStep")}
|
|
59
|
-
</Link>
|
|
60
|
-
</div>
|
|
61
|
-
</Container>
|
|
62
|
-
)
|
|
63
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { ArrowPathMini } from "@acmekit/icons"
|
|
2
|
-
import { DropdownMenu, IconButton } from "@acmekit/ui"
|
|
3
|
-
import { useQueryClient } from "@tanstack/react-query"
|
|
4
|
-
import { useCallback, useEffect, useRef, useState } from "react"
|
|
5
|
-
import { workflowExecutionsQueryKeys } from "../../../../../hooks/api/workflow-executions"
|
|
6
|
-
|
|
7
|
-
const INTERVALS = [
|
|
8
|
-
{ label: "Off", value: 0 },
|
|
9
|
-
{ label: "5s", value: 5000 },
|
|
10
|
-
{ label: "10s", value: 10000 },
|
|
11
|
-
{ label: "30s", value: 30000 },
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
export const WorkflowExecutionAutoRefresh = () => {
|
|
15
|
-
const queryClient = useQueryClient()
|
|
16
|
-
const [interval, setInterval_] = useState(0)
|
|
17
|
-
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
|
18
|
-
|
|
19
|
-
const refresh = useCallback(() => {
|
|
20
|
-
queryClient.invalidateQueries({
|
|
21
|
-
queryKey: workflowExecutionsQueryKeys.lists(),
|
|
22
|
-
})
|
|
23
|
-
}, [queryClient])
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (intervalRef.current) {
|
|
27
|
-
clearInterval(intervalRef.current)
|
|
28
|
-
intervalRef.current = null
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (interval > 0) {
|
|
32
|
-
intervalRef.current = setInterval(refresh, interval)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return () => {
|
|
36
|
-
if (intervalRef.current) {
|
|
37
|
-
clearInterval(intervalRef.current)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}, [interval, refresh])
|
|
41
|
-
|
|
42
|
-
const activeLabel = INTERVALS.find((i) => i.value === interval)?.label
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<div className="flex items-center gap-x-1">
|
|
46
|
-
<IconButton
|
|
47
|
-
size="small"
|
|
48
|
-
variant="transparent"
|
|
49
|
-
onClick={refresh}
|
|
50
|
-
>
|
|
51
|
-
<ArrowPathMini />
|
|
52
|
-
</IconButton>
|
|
53
|
-
<DropdownMenu>
|
|
54
|
-
<DropdownMenu.Trigger asChild>
|
|
55
|
-
<button className="txt-compact-small text-ui-fg-muted hover:text-ui-fg-subtle rounded px-1.5 py-0.5 transition-colors">
|
|
56
|
-
{activeLabel}
|
|
57
|
-
</button>
|
|
58
|
-
</DropdownMenu.Trigger>
|
|
59
|
-
<DropdownMenu.Content align="end">
|
|
60
|
-
{INTERVALS.map((opt) => (
|
|
61
|
-
<DropdownMenu.Item
|
|
62
|
-
key={opt.value}
|
|
63
|
-
onClick={() => setInterval_(opt.value)}
|
|
64
|
-
className={interval === opt.value ? "font-medium" : ""}
|
|
65
|
-
>
|
|
66
|
-
{opt.label}
|
|
67
|
-
</DropdownMenu.Item>
|
|
68
|
-
))}
|
|
69
|
-
</DropdownMenu.Content>
|
|
70
|
-
</DropdownMenu>
|
|
71
|
-
</div>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { ArrowPathMini, Eye, XMark } from "@acmekit/icons"
|
|
2
|
-
import { HttpTypes } from "@acmekit/types"
|
|
3
|
-
import { toast, usePrompt } from "@acmekit/ui"
|
|
4
|
-
import { useTranslation } from "react-i18next"
|
|
5
|
-
import {
|
|
6
|
-
Action,
|
|
7
|
-
ActionMenu,
|
|
8
|
-
} from "../../../../../components/common/action-menu"
|
|
9
|
-
import {
|
|
10
|
-
useCancelWorkflowExecution,
|
|
11
|
-
useRunWorkflow,
|
|
12
|
-
} from "../../../../../hooks/api/workflow-executions"
|
|
13
|
-
import {
|
|
14
|
-
TRANSACTION_ERROR_STATES,
|
|
15
|
-
TRANSACTION_IN_PROGRESS_STATES,
|
|
16
|
-
} from "../../../constants"
|
|
17
|
-
import { TransactionState } from "../../../types"
|
|
18
|
-
|
|
19
|
-
type WorkflowExecutionRowActionsProps = {
|
|
20
|
-
execution: HttpTypes.AdminWorkflowExecutionResponse["workflow_execution"]
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const WorkflowExecutionRowActions = ({
|
|
24
|
-
execution,
|
|
25
|
-
}: WorkflowExecutionRowActionsProps) => {
|
|
26
|
-
const { t } = useTranslation()
|
|
27
|
-
const prompt = usePrompt()
|
|
28
|
-
|
|
29
|
-
const state = execution.state as TransactionState
|
|
30
|
-
const isFailed = TRANSACTION_ERROR_STATES.includes(state)
|
|
31
|
-
const isRunning = TRANSACTION_IN_PROGRESS_STATES.includes(state)
|
|
32
|
-
|
|
33
|
-
const workflowId = execution.workflow_id
|
|
34
|
-
const transactionId = execution.transaction_id
|
|
35
|
-
|
|
36
|
-
const { mutateAsync: runAsync } = useRunWorkflow(workflowId)
|
|
37
|
-
const { mutateAsync: cancelAsync } = useCancelWorkflowExecution(
|
|
38
|
-
workflowId,
|
|
39
|
-
transactionId
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
const handleRetry = async () => {
|
|
43
|
-
const payload = (execution as any)?.context?.data?.payload
|
|
44
|
-
const input =
|
|
45
|
-
payload && typeof payload === "object"
|
|
46
|
-
? (payload as Record<string, unknown>)
|
|
47
|
-
: {}
|
|
48
|
-
await runAsync(
|
|
49
|
-
{ input },
|
|
50
|
-
{
|
|
51
|
-
onSuccess: () => {
|
|
52
|
-
toast.success(t("workflowExecutions.actions.retrySuccess"))
|
|
53
|
-
},
|
|
54
|
-
onError: (err) => {
|
|
55
|
-
toast.error(err.message)
|
|
56
|
-
},
|
|
57
|
-
}
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const handleCancel = async () => {
|
|
62
|
-
const res = await prompt({
|
|
63
|
-
title: t("workflowExecutions.actions.cancelConfirmTitle"),
|
|
64
|
-
description: t(
|
|
65
|
-
"workflowExecutions.actions.cancelConfirmDescription",
|
|
66
|
-
{ workflow_id: workflowId }
|
|
67
|
-
),
|
|
68
|
-
confirmText: t("workflowExecutions.actions.cancel"),
|
|
69
|
-
cancelText: t("actions.cancel"),
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
if (!res) return
|
|
73
|
-
|
|
74
|
-
await cancelAsync(undefined, {
|
|
75
|
-
onSuccess: () => {
|
|
76
|
-
toast.success(t("workflowExecutions.actions.cancelSuccess"))
|
|
77
|
-
},
|
|
78
|
-
onError: (err) => {
|
|
79
|
-
toast.error(err.message)
|
|
80
|
-
},
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const secondaryActions: Action[] = []
|
|
85
|
-
if (isFailed) {
|
|
86
|
-
secondaryActions.push({
|
|
87
|
-
icon: <ArrowPathMini />,
|
|
88
|
-
label: t("workflowExecutions.actions.retry"),
|
|
89
|
-
onClick: handleRetry,
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
if (isRunning) {
|
|
93
|
-
secondaryActions.push({
|
|
94
|
-
icon: <XMark />,
|
|
95
|
-
label: t("workflowExecutions.actions.cancel"),
|
|
96
|
-
onClick: handleCancel,
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const groups = [
|
|
101
|
-
{
|
|
102
|
-
actions: [
|
|
103
|
-
{
|
|
104
|
-
icon: <Eye />,
|
|
105
|
-
label: t("workflowExecutions.actions.viewDetails"),
|
|
106
|
-
to: `${execution.id}`,
|
|
107
|
-
} as Action,
|
|
108
|
-
],
|
|
109
|
-
},
|
|
110
|
-
...(secondaryActions.length > 0
|
|
111
|
-
? [{ actions: secondaryActions }]
|
|
112
|
-
: []),
|
|
113
|
-
]
|
|
114
|
-
|
|
115
|
-
return <ActionMenu groups={groups} />
|
|
116
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { Button, clx } from "@acmekit/ui"
|
|
2
|
-
import { useCallback, useMemo } from "react"
|
|
3
|
-
import { useTranslation } from "react-i18next"
|
|
4
|
-
import { useSearchParams } from "react-router-dom"
|
|
5
|
-
|
|
6
|
-
type SavedView = {
|
|
7
|
-
label: string
|
|
8
|
-
params: Record<string, string>
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const WorkflowExecutionSavedViews = () => {
|
|
12
|
-
const { t } = useTranslation()
|
|
13
|
-
const [searchParams, setSearchParams] = useSearchParams()
|
|
14
|
-
|
|
15
|
-
const views: SavedView[] = useMemo(
|
|
16
|
-
() => [
|
|
17
|
-
{
|
|
18
|
-
label: t("workflowExecutions.filters.allExecutions"),
|
|
19
|
-
params: {} as Record<string, string>,
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
label: t("workflowExecutions.filters.running"),
|
|
23
|
-
params: { state: "invoking" } as Record<string, string>,
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
label: t("workflowExecutions.filters.failedToday"),
|
|
27
|
-
params: {
|
|
28
|
-
state: "failed",
|
|
29
|
-
created_at: JSON.stringify({
|
|
30
|
-
$gte: new Date(
|
|
31
|
-
new Date().setHours(0, 0, 0, 0)
|
|
32
|
-
).toISOString(),
|
|
33
|
-
}),
|
|
34
|
-
} as Record<string, string>,
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
[t]
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
const isActive = useCallback(
|
|
41
|
-
(view: SavedView) => {
|
|
42
|
-
const viewKeys = Object.keys(view.params)
|
|
43
|
-
if (viewKeys.length === 0) {
|
|
44
|
-
return !searchParams.has("state") && !searchParams.has("created_at")
|
|
45
|
-
}
|
|
46
|
-
return viewKeys.every(
|
|
47
|
-
(key) => searchParams.get(key) === view.params[key]
|
|
48
|
-
)
|
|
49
|
-
},
|
|
50
|
-
[searchParams]
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
const handleClick = useCallback(
|
|
54
|
-
(view: SavedView) => {
|
|
55
|
-
const newParams = new URLSearchParams()
|
|
56
|
-
for (const [key, value] of Object.entries(view.params)) {
|
|
57
|
-
newParams.set(key, value)
|
|
58
|
-
}
|
|
59
|
-
setSearchParams(newParams)
|
|
60
|
-
},
|
|
61
|
-
[setSearchParams]
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<div className="flex items-center gap-x-1">
|
|
66
|
-
{views.map((view) => (
|
|
67
|
-
<Button
|
|
68
|
-
key={view.label}
|
|
69
|
-
variant="transparent"
|
|
70
|
-
size="small"
|
|
71
|
-
onClick={() => handleClick(view)}
|
|
72
|
-
className={clx(
|
|
73
|
-
"txt-compact-small-plus",
|
|
74
|
-
isActive(view)
|
|
75
|
-
? "bg-ui-bg-base shadow-elevation-card-rest text-ui-fg-base"
|
|
76
|
-
: "text-ui-fg-muted"
|
|
77
|
-
)}
|
|
78
|
-
>
|
|
79
|
-
{view.label}
|
|
80
|
-
</Button>
|
|
81
|
-
))}
|
|
82
|
-
</div>
|
|
83
|
-
)
|
|
84
|
-
}
|