@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.
Files changed (39) hide show
  1. package/package.json +9 -9
  2. package/src/components/layout/main-layout/main-layout.tsx +1 -28
  3. package/src/dashboard-app/routes/get-route.map.tsx +0 -71
  4. package/src/hooks/api/workflow-executions.tsx +1 -145
  5. package/src/i18n/translations/$schema.json +4 -534
  6. package/src/i18n/translations/en.json +3 -132
  7. package/src/routes/workflow-executions/constants.ts +0 -16
  8. package/src/routes/workflow-executions/utils.ts +14 -170
  9. package/src/routes/workflow-executions/workflow-execution-detail/breadcrumb.tsx +1 -7
  10. package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-history-section/workflow-execution-history-section.tsx +6 -157
  11. package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-payload-section/workflow-execution-payload-section.tsx +6 -122
  12. package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-timeline-section/workflow-execution-timeline-section.tsx +1 -7
  13. package/src/routes/workflow-executions/workflow-execution-detail/workflow-detail.tsx +1 -46
  14. package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/use-workflow-execution-table-columns.tsx +0 -7
  15. package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/use-workflow-execution-table-filters.tsx +1 -7
  16. package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/use-workflow-execution-table-query.tsx +2 -4
  17. package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-list-table.tsx +1 -17
  18. package/src/routes/workflow-executions/workflow-execution-list/workflow-execution-list.tsx +1 -1
  19. package/src/hooks/api/workflow-definitions.tsx +0 -79
  20. package/src/hooks/api/workflow-metrics.tsx +0 -48
  21. package/src/hooks/use-workflow-sse.tsx +0 -78
  22. package/src/routes/workflow-analytics/workflow-analytics.tsx +0 -167
  23. package/src/routes/workflow-definitions/workflow-definition-detail/workflow-definition-detail.tsx +0 -98
  24. package/src/routes/workflow-definitions/workflow-definition-list/components/workflow-definition-list-table/use-workflow-definition-table-columns.tsx +0 -78
  25. package/src/routes/workflow-definitions/workflow-definition-list/components/workflow-definition-list-table/workflow-definition-list-table.tsx +0 -65
  26. package/src/routes/workflow-definitions/workflow-definition-list/workflow-definition-list.tsx +0 -15
  27. package/src/routes/workflow-executions/workflow-execution-complete-step/workflow-execution-complete-step.tsx +0 -270
  28. package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-action-bar/index.ts +0 -1
  29. package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-action-bar/workflow-execution-action-bar.tsx +0 -212
  30. package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-error-card/index.ts +0 -1
  31. package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-error-card/workflow-execution-error-card.tsx +0 -59
  32. package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-waiting-banner/index.ts +0 -1
  33. package/src/routes/workflow-executions/workflow-execution-detail/components/workflow-execution-waiting-banner/workflow-execution-waiting-banner.tsx +0 -63
  34. package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-auto-refresh.tsx +0 -73
  35. package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-row-actions.tsx +0 -116
  36. package/src/routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-saved-views.tsx +0 -84
  37. package/src/routes/workflow-executions/workflow-execution-rerun/workflow-execution-rerun.tsx +0 -159
  38. package/src/routes/workflow-executions/workflow-execution-run/workflow-execution-run.tsx +0 -139
  39. package/src/routes/workflow-scheduled/workflow-scheduled-list.tsx +0 -269
@@ -1,159 +0,0 @@
1
- import { zodResolver } from "@hookform/resolvers/zod"
2
- import { Button, Heading, Textarea, toast } from "@acmekit/ui"
3
- import { useForm } from "react-hook-form"
4
- import { useTranslation } from "react-i18next"
5
- import { useParams } from "react-router-dom"
6
- import * as zod from "zod"
7
-
8
- import { Form } from "../../../components/common/form"
9
- import { RouteDrawer, useRouteModal } from "../../../components/modals"
10
- import { KeyboundForm } from "../../../components/utilities/keybound-form"
11
- import {
12
- useRunWorkflow,
13
- useWorkflowExecution,
14
- } from "../../../hooks/api/workflow-executions"
15
-
16
- const RerunSchema = zod.object({
17
- input: zod.string().refine(
18
- (val) => {
19
- try {
20
- JSON.parse(val)
21
- return true
22
- } catch {
23
- return false
24
- }
25
- },
26
- { message: "Must be valid JSON" }
27
- ),
28
- })
29
-
30
- export const WorkflowExecutionRerun = () => {
31
- const { t } = useTranslation()
32
- const { id } = useParams()
33
- const { handleSuccess } = useRouteModal()
34
-
35
- const {
36
- workflow_execution,
37
- isPending: isLoading,
38
- isError,
39
- error,
40
- } = useWorkflowExecution(id!)
41
-
42
- if (isError) {
43
- throw error
44
- }
45
-
46
- if (isLoading || !workflow_execution) {
47
- return (
48
- <RouteDrawer>
49
- <RouteDrawer.Header>
50
- <Heading>{t("workflowExecutions.rerun.title")}</Heading>
51
- </RouteDrawer.Header>
52
- </RouteDrawer>
53
- )
54
- }
55
-
56
- return (
57
- <RouteDrawer>
58
- <RouteDrawer.Header>
59
- <Heading>{t("workflowExecutions.rerun.title")}</Heading>
60
- </RouteDrawer.Header>
61
- <RerunForm
62
- workflowId={workflow_execution.workflow_id}
63
- defaultInput={
64
- (workflow_execution as any)?.context?.data?.payload || {}
65
- }
66
- onSuccess={handleSuccess}
67
- />
68
- </RouteDrawer>
69
- )
70
- }
71
-
72
- const RerunForm = ({
73
- workflowId,
74
- defaultInput,
75
- onSuccess,
76
- }: {
77
- workflowId: string
78
- defaultInput: unknown
79
- onSuccess: () => void
80
- }) => {
81
- const { t } = useTranslation()
82
-
83
- const form = useForm<zod.infer<typeof RerunSchema>>({
84
- defaultValues: {
85
- input: JSON.stringify(defaultInput, null, 2),
86
- },
87
- resolver: zodResolver(RerunSchema),
88
- })
89
-
90
- const { mutateAsync, isPending } = useRunWorkflow(workflowId)
91
-
92
- const handleSubmit = form.handleSubmit(async (values) => {
93
- const parsed = JSON.parse(values.input)
94
- await mutateAsync(
95
- { input: parsed },
96
- {
97
- onSuccess: () => {
98
- toast.success(t("workflowExecutions.rerun.success"))
99
- onSuccess()
100
- },
101
- onError: (err) => {
102
- toast.error(err.message)
103
- },
104
- }
105
- )
106
- })
107
-
108
- return (
109
- <RouteDrawer.Form form={form}>
110
- <KeyboundForm
111
- onSubmit={handleSubmit}
112
- className="flex flex-1 flex-col overflow-hidden"
113
- >
114
- <RouteDrawer.Body className="flex max-w-full flex-1 flex-col gap-y-4 overflow-y-auto">
115
- <div className="bg-ui-bg-field border-ui-border-base rounded-lg border px-3 py-2">
116
- <span className="txt-compact-small text-ui-fg-muted">
117
- {workflowId}
118
- </span>
119
- </div>
120
- <Form.Field
121
- control={form.control}
122
- name="input"
123
- render={({ field }) => {
124
- return (
125
- <Form.Item>
126
- <Form.Label>
127
- {t("workflowExecutions.rerun.inputLabel")}
128
- </Form.Label>
129
- <Form.Control>
130
- <Textarea
131
- {...field}
132
- className="font-mono text-xs"
133
- rows={16}
134
- />
135
- </Form.Control>
136
- <Form.ErrorMessage />
137
- </Form.Item>
138
- )
139
- }}
140
- />
141
- </RouteDrawer.Body>
142
- <RouteDrawer.Footer>
143
- <div className="flex items-center justify-end gap-x-2">
144
- <RouteDrawer.Close asChild>
145
- <Button size="small" variant="secondary">
146
- {t("actions.cancel")}
147
- </Button>
148
- </RouteDrawer.Close>
149
- <Button size="small" type="submit" isLoading={isPending}>
150
- {t("workflowExecutions.rerun.submit")}
151
- </Button>
152
- </div>
153
- </RouteDrawer.Footer>
154
- </KeyboundForm>
155
- </RouteDrawer.Form>
156
- )
157
- }
158
-
159
- export const Component = WorkflowExecutionRerun
@@ -1,139 +0,0 @@
1
- import { zodResolver } from "@hookform/resolvers/zod"
2
- import { Button, Heading, Input, Textarea, toast } from "@acmekit/ui"
3
- import { useForm } from "react-hook-form"
4
- import { useTranslation } from "react-i18next"
5
- import * as zod from "zod"
6
-
7
- import { Form } from "../../../components/common/form"
8
- import { RouteDrawer, useRouteModal } from "../../../components/modals"
9
- import { KeyboundForm } from "../../../components/utilities/keybound-form"
10
- import { useRunWorkflow } from "../../../hooks/api/workflow-executions"
11
-
12
- const RunWorkflowSchema = zod.object({
13
- workflow_id: zod.string().min(1, "Workflow ID is required"),
14
- input: zod.string().refine(
15
- (val) => {
16
- if (!val.trim()) return true
17
- try {
18
- JSON.parse(val)
19
- return true
20
- } catch {
21
- return false
22
- }
23
- },
24
- { message: "Must be valid JSON" }
25
- ),
26
- })
27
-
28
- export const WorkflowExecutionRun = () => {
29
- const { t } = useTranslation()
30
- const { handleSuccess } = useRouteModal()
31
-
32
- return (
33
- <RouteDrawer>
34
- <RouteDrawer.Header>
35
- <Heading>{t("workflowExecutions.actions.runWorkflow")}</Heading>
36
- </RouteDrawer.Header>
37
- <RunWorkflowForm onSuccess={handleSuccess} />
38
- </RouteDrawer>
39
- )
40
- }
41
-
42
- const RunWorkflowForm = ({ onSuccess }: { onSuccess: () => void }) => {
43
- const { t } = useTranslation()
44
-
45
- const form = useForm<zod.infer<typeof RunWorkflowSchema>>({
46
- defaultValues: {
47
- workflow_id: "",
48
- input: "{}",
49
- },
50
- resolver: zodResolver(RunWorkflowSchema),
51
- })
52
-
53
- const workflowId = form.watch("workflow_id")
54
-
55
- const { mutateAsync, isPending } = useRunWorkflow(workflowId || "_placeholder")
56
-
57
- const handleSubmit = form.handleSubmit(async (values) => {
58
- const parsed = values.input.trim() ? JSON.parse(values.input) : {}
59
- await mutateAsync(
60
- { input: parsed },
61
- {
62
- onSuccess: () => {
63
- toast.success(t("workflowExecutions.rerun.success"))
64
- onSuccess()
65
- },
66
- onError: (err) => {
67
- toast.error(err.message)
68
- },
69
- }
70
- )
71
- })
72
-
73
- return (
74
- <RouteDrawer.Form form={form}>
75
- <KeyboundForm
76
- onSubmit={handleSubmit}
77
- className="flex flex-1 flex-col overflow-hidden"
78
- >
79
- <RouteDrawer.Body className="flex max-w-full flex-1 flex-col gap-y-4 overflow-y-auto">
80
- <Form.Field
81
- control={form.control}
82
- name="workflow_id"
83
- render={({ field }) => {
84
- return (
85
- <Form.Item>
86
- <Form.Label>
87
- {t("workflowExecutions.workflowIdLabel")}
88
- </Form.Label>
89
- <Form.Control>
90
- <Input
91
- {...field}
92
- placeholder="my-workflow-id"
93
- />
94
- </Form.Control>
95
- <Form.ErrorMessage />
96
- </Form.Item>
97
- )
98
- }}
99
- />
100
- <Form.Field
101
- control={form.control}
102
- name="input"
103
- render={({ field }) => {
104
- return (
105
- <Form.Item>
106
- <Form.Label>
107
- {t("workflowExecutions.rerun.inputLabel")}
108
- </Form.Label>
109
- <Form.Control>
110
- <Textarea
111
- {...field}
112
- className="font-mono text-xs"
113
- rows={12}
114
- />
115
- </Form.Control>
116
- <Form.ErrorMessage />
117
- </Form.Item>
118
- )
119
- }}
120
- />
121
- </RouteDrawer.Body>
122
- <RouteDrawer.Footer>
123
- <div className="flex items-center justify-end gap-x-2">
124
- <RouteDrawer.Close asChild>
125
- <Button size="small" variant="secondary">
126
- {t("actions.cancel")}
127
- </Button>
128
- </RouteDrawer.Close>
129
- <Button size="small" type="submit" isLoading={isPending}>
130
- {t("workflowExecutions.rerun.submit")}
131
- </Button>
132
- </div>
133
- </RouteDrawer.Footer>
134
- </KeyboundForm>
135
- </RouteDrawer.Form>
136
- )
137
- }
138
-
139
- export const Component = WorkflowExecutionRun
@@ -1,269 +0,0 @@
1
- import { Badge, Button, Container, Heading, Text, toast } from "@acmekit/ui"
2
- import { keepPreviousData } from "@tanstack/react-query"
3
- import { formatDistanceToNow } from "date-fns"
4
- import { useTranslation } from "react-i18next"
5
- import { SingleColumnPage } from "../../components/layout/pages"
6
- import {
7
- useWorkflowDefinitions,
8
- } from "../../hooks/api/workflow-definitions"
9
- import { useRunWorkflow } from "../../hooks/api/workflow-executions"
10
-
11
- /**
12
- * Very small cron → human-readable description utility.
13
- * Handles the most common patterns without an external library.
14
- */
15
- function describeCron(cron: string): string {
16
- const parts = cron.trim().split(/\s+/)
17
- if (parts.length < 5) return cron
18
-
19
- const [minute, hour, dom, month, dow] = parts
20
-
21
- if (
22
- minute === "*" &&
23
- hour === "*" &&
24
- dom === "*" &&
25
- month === "*" &&
26
- dow === "*"
27
- ) {
28
- return "Every minute"
29
- }
30
-
31
- if (
32
- minute !== "*" &&
33
- hour === "*" &&
34
- dom === "*" &&
35
- month === "*" &&
36
- dow === "*"
37
- ) {
38
- return `Every hour at minute ${minute}`
39
- }
40
-
41
- if (
42
- minute !== "*" &&
43
- hour !== "*" &&
44
- dom === "*" &&
45
- month === "*" &&
46
- dow === "*"
47
- ) {
48
- const h = parseInt(hour, 10)
49
- const m = parseInt(minute, 10)
50
- const ampm = h >= 12 ? "PM" : "AM"
51
- const h12 = h % 12 || 12
52
- return `Daily at ${h12}:${String(m).padStart(2, "0")} ${ampm}`
53
- }
54
-
55
- if (
56
- minute !== "*" &&
57
- hour !== "*" &&
58
- dom === "*" &&
59
- month === "*" &&
60
- dow !== "*"
61
- ) {
62
- const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
63
- const dayNames = dow
64
- .split(",")
65
- .map((d) => days[parseInt(d, 10)] ?? d)
66
- .join(", ")
67
- const h = parseInt(hour, 10)
68
- const m = parseInt(minute, 10)
69
- const ampm = h >= 12 ? "PM" : "AM"
70
- const h12 = h % 12 || 12
71
- return `${dayNames} at ${h12}:${String(m).padStart(2, "0")} ${ampm}`
72
- }
73
-
74
- if (
75
- minute !== "*" &&
76
- hour !== "*" &&
77
- dom !== "*" &&
78
- month === "*" &&
79
- dow === "*"
80
- ) {
81
- return `Monthly on day ${dom} at ${hour}:${String(parseInt(minute, 10)).padStart(2, "0")}`
82
- }
83
-
84
- return cron
85
- }
86
-
87
- /**
88
- * Rough next-run estimate using date-fns (not exact; just indicative).
89
- */
90
- function getNextRunLabel(cron: string): string {
91
- try {
92
- const parts = cron.trim().split(/\s+/)
93
- if (parts.length < 5) return "Unknown"
94
-
95
- const [minuteStr, hourStr] = parts
96
- const now = new Date()
97
- const next = new Date(now)
98
-
99
- if (minuteStr === "*") {
100
- // every minute
101
- next.setSeconds(0, 0)
102
- next.setMinutes(now.getMinutes() + 1)
103
- } else if (hourStr === "*") {
104
- // every hour at :MM
105
- const m = parseInt(minuteStr, 10)
106
- next.setMinutes(m, 0, 0)
107
- if (next <= now) next.setHours(now.getHours() + 1)
108
- } else {
109
- // daily at HH:MM (simplified)
110
- const h = parseInt(hourStr, 10)
111
- const m = parseInt(minuteStr, 10)
112
- next.setHours(h, m, 0, 0)
113
- if (next <= now) next.setDate(now.getDate() + 1)
114
- }
115
-
116
- return formatDistanceToNow(next, { addSuffix: true })
117
- } catch {
118
- return "Unknown"
119
- }
120
- }
121
-
122
- type WorkflowDef = {
123
- id: string
124
- handler_count: number
125
- steps: Array<{ action: string; depth: number; noCompensation: boolean }>
126
- options: Record<string, unknown>
127
- }
128
-
129
- const ScheduledRow = ({ def }: { def: WorkflowDef }) => {
130
- const { t } = useTranslation()
131
-
132
- const schedule = def.options?.schedule as
133
- | { cron?: string; interval?: string }
134
- | string
135
- | undefined
136
-
137
- const cronStr =
138
- typeof schedule === "string"
139
- ? schedule
140
- : typeof schedule === "object"
141
- ? schedule?.cron ?? String(schedule?.interval ?? "")
142
- : ""
143
-
144
- const humanSchedule = cronStr ? describeCron(cronStr) : "—"
145
- const nextRun = cronStr ? getNextRunLabel(cronStr) : "—"
146
-
147
- const { mutateAsync: runWorkflow, isPending } = useRunWorkflow(def.id)
148
-
149
- const handleRunNow = async () => {
150
- await runWorkflow(
151
- { input: {} },
152
- {
153
- onSuccess: () => {
154
- toast.success(t("workflowExecutions.actions.retrySuccess"))
155
- },
156
- }
157
- )
158
- }
159
-
160
- return (
161
- <div className="grid grid-cols-[1fr_1fr_1fr_auto] items-center gap-x-4 px-6 py-3">
162
- <div className="flex flex-col gap-y-0.5">
163
- <Text size="small" weight="plus" leading="compact">
164
- {def.id}
165
- </Text>
166
- <Text size="xsmall" className="text-ui-fg-muted">
167
- {def.handler_count}{" "}
168
- {t("workflowExecutions.definitions.handlers")}
169
- </Text>
170
- </div>
171
- <div>
172
- <Badge size="2xsmall" className="font-mono">
173
- {cronStr || "—"}
174
- </Badge>
175
- <Text size="xsmall" className="text-ui-fg-subtle mt-0.5">
176
- {humanSchedule}
177
- </Text>
178
- </div>
179
- <Text size="small" className="text-ui-fg-subtle">
180
- {nextRun}
181
- </Text>
182
- <Button
183
- size="small"
184
- variant="secondary"
185
- isLoading={isPending}
186
- onClick={handleRunNow}
187
- >
188
- {t("workflowExecutions.scheduled.runNow")}
189
- </Button>
190
- </div>
191
- )
192
- }
193
-
194
- export const WorkflowScheduledList = () => {
195
- const { t } = useTranslation()
196
-
197
- const { workflow_definitions, isPending } = useWorkflowDefinitions(
198
- undefined,
199
- { placeholderData: keepPreviousData }
200
- )
201
-
202
- const scheduled = (workflow_definitions ?? []).filter((def) => {
203
- const opts = def.options
204
- return opts && (opts.schedule || opts.cron)
205
- })
206
-
207
- return (
208
- <SingleColumnPage widgets={{ before: [], after: [] }}>
209
- <Container className="divide-y p-0">
210
- <div className="flex items-center justify-between px-6 py-4">
211
- <div>
212
- <Heading>{t("workflowExecutions.scheduled.domain")}</Heading>
213
- <Text className="text-ui-fg-subtle" size="small">
214
- {t("workflowExecutions.scheduled.subtitle")}
215
- </Text>
216
- </div>
217
- </div>
218
-
219
- {/* Table header */}
220
- <div className="grid grid-cols-[1fr_1fr_1fr_auto] gap-x-4 border-b border-ui-border-base px-6 py-2">
221
- <Text
222
- size="xsmall"
223
- weight="plus"
224
- className="text-ui-fg-subtle uppercase"
225
- >
226
- Workflow
227
- </Text>
228
- <Text
229
- size="xsmall"
230
- weight="plus"
231
- className="text-ui-fg-subtle uppercase"
232
- >
233
- {t("workflowExecutions.scheduled.schedule")}
234
- </Text>
235
- <Text
236
- size="xsmall"
237
- weight="plus"
238
- className="text-ui-fg-subtle uppercase"
239
- >
240
- {t("workflowExecutions.scheduled.nextRun")}
241
- </Text>
242
- <div />
243
- </div>
244
-
245
- {isPending && (
246
- <div className="px-6 py-4">
247
- <Text size="small" className="text-ui-fg-muted">
248
- Loading...
249
- </Text>
250
- </div>
251
- )}
252
-
253
- {!isPending && scheduled.length === 0 && (
254
- <div className="px-6 py-8 text-center">
255
- <Text size="small" className="text-ui-fg-muted">
256
- {t("workflowExecutions.scheduled.noRecordsMessage")}
257
- </Text>
258
- </div>
259
- )}
260
-
261
- {scheduled.map((def) => (
262
- <ScheduledRow key={def.id} def={def} />
263
- ))}
264
- </Container>
265
- </SingleColumnPage>
266
- )
267
- }
268
-
269
- export const Component = WorkflowScheduledList