@voyantjs/workflows-react 0.106.0 → 0.107.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +43 -0
  2. package/dist/client.d.ts +2 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +1 -0
  5. package/dist/components/common.d.ts +30 -0
  6. package/dist/components/common.d.ts.map +1 -0
  7. package/dist/components/common.js +108 -0
  8. package/dist/components/workflow-run-actions.d.ts +7 -0
  9. package/dist/components/workflow-run-actions.d.ts.map +1 -0
  10. package/dist/components/workflow-run-actions.js +72 -0
  11. package/dist/components/workflow-run-detail-page.d.ts +10 -0
  12. package/dist/components/workflow-run-detail-page.d.ts.map +1 -0
  13. package/dist/components/workflow-run-detail-page.js +96 -0
  14. package/dist/components/workflow-runs-filters.d.ts +32 -0
  15. package/dist/components/workflow-runs-filters.d.ts.map +1 -0
  16. package/dist/components/workflow-runs-filters.js +97 -0
  17. package/dist/components/workflow-runs-page.d.ts +12 -0
  18. package/dist/components/workflow-runs-page.d.ts.map +1 -0
  19. package/dist/components/workflow-runs-page.js +132 -0
  20. package/dist/components/workflow-schedules-page.d.ts +21 -0
  21. package/dist/components/workflow-schedules-page.d.ts.map +1 -0
  22. package/dist/components/workflow-schedules-page.js +144 -0
  23. package/dist/i18n/en.d.ts +4 -0
  24. package/dist/i18n/en.d.ts.map +1 -0
  25. package/dist/i18n/en.js +135 -0
  26. package/dist/i18n/index.d.ts +5 -0
  27. package/dist/i18n/index.d.ts.map +1 -0
  28. package/dist/i18n/index.js +3 -0
  29. package/dist/i18n/messages.d.ts +125 -0
  30. package/dist/i18n/messages.d.ts.map +1 -0
  31. package/dist/i18n/messages.js +1 -0
  32. package/dist/i18n/provider.d.ts +26 -0
  33. package/dist/i18n/provider.d.ts.map +1 -0
  34. package/dist/i18n/provider.js +44 -0
  35. package/dist/i18n/ro.d.ts +4 -0
  36. package/dist/i18n/ro.d.ts.map +1 -0
  37. package/dist/i18n/ro.js +137 -0
  38. package/dist/schedules-client.d.ts +2 -0
  39. package/dist/schedules-client.d.ts.map +1 -0
  40. package/dist/schedules-client.js +1 -0
  41. package/dist/types.d.ts +2 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +1 -0
  44. package/dist/ui.d.ts +9 -0
  45. package/dist/ui.d.ts.map +1 -0
  46. package/dist/ui.js +6 -0
  47. package/package.json +69 -21
  48. package/src/client.ts +4 -0
  49. package/src/components/common.tsx +182 -0
  50. package/src/components/workflow-run-actions.tsx +160 -0
  51. package/src/components/workflow-run-detail-page.tsx +393 -0
  52. package/src/components/workflow-runs-filters.tsx +349 -0
  53. package/src/components/workflow-runs-page.tsx +357 -0
  54. package/src/components/workflow-schedules-page.tsx +398 -0
  55. package/src/i18n/en.ts +142 -0
  56. package/src/i18n/index.ts +26 -0
  57. package/src/i18n/messages.ts +125 -0
  58. package/src/i18n/provider.tsx +96 -0
  59. package/src/i18n/ro.ts +144 -0
  60. package/src/schedules-client.ts +8 -0
  61. package/src/styles.css +11 -0
  62. package/src/types.ts +14 -0
  63. package/src/ui.ts +63 -0
@@ -0,0 +1,398 @@
1
+ "use client"
2
+
3
+ import { Badge } from "@voyantjs/ui/components/badge"
4
+ import { Button } from "@voyantjs/ui/components/button"
5
+ import { Card, CardContent } from "@voyantjs/ui/components/card"
6
+ import { AlertTriangle, CalendarClock, RefreshCw } from "lucide-react"
7
+ import { useCallback, useEffect, useMemo, useState } from "react"
8
+
9
+ import { useWorkflowRunsUiMessagesOrDefault, type WorkflowRunsUiMessages } from "../i18n/index.js"
10
+ import type {
11
+ ListWorkflowSchedulesResponse,
12
+ WorkflowScheduleDecl,
13
+ WorkflowScheduleSummary,
14
+ WorkflowSchedulesApi,
15
+ } from "../schedules-client.js"
16
+ import type { WorkflowRun, WorkflowRunStatus, WorkflowRunsApi } from "../types.js"
17
+ import { formatRelative, StatusIcon } from "./common.js"
18
+
19
+ type SchedulesMessages = WorkflowRunsUiMessages["schedules"]
20
+
21
+ export interface WorkflowSchedulesPageProps {
22
+ /** Schedules API — backed by `/api/schedules/:env`. */
23
+ schedulesApi: WorkflowSchedulesApi
24
+ /** Optional runs API — when provided, the page joins each row with the most recent matching run. */
25
+ runsApi?: WorkflowRunsApi
26
+ /**
27
+ * Optional trigger callback. When provided, each row renders a
28
+ * "Trigger now" button that calls this with the workflow id + the
29
+ * schedule's recorded `input` payload (if any).
30
+ */
31
+ onTriggerNow?: (workflowId: string, input: unknown) => Promise<void>
32
+ /** Manifest environment to inspect. Defaults to "production". */
33
+ environment?: "production" | "preview" | "development"
34
+ /** Auto-refresh interval (ms). Defaults to 30s. Pass 0 to disable. */
35
+ pollIntervalMs?: number
36
+ className?: string
37
+ }
38
+
39
+ export function WorkflowSchedulesPage({
40
+ schedulesApi,
41
+ runsApi,
42
+ onTriggerNow,
43
+ environment = "production",
44
+ pollIntervalMs = 30_000,
45
+ className,
46
+ }: WorkflowSchedulesPageProps) {
47
+ const rootMessages = useWorkflowRunsUiMessagesOrDefault()
48
+ const messages = rootMessages.schedules
49
+ const [response, setResponse] = useState<ListWorkflowSchedulesResponse | null>(null)
50
+ const [lastRuns, setLastRuns] = useState<Record<string, WorkflowRun | null>>({})
51
+ const [loading, setLoading] = useState(false)
52
+ const [error, setError] = useState<string | null>(null)
53
+ const [triggering, setTriggering] = useState<Record<string, boolean>>({})
54
+ const [triggerNotice, setTriggerNotice] = useState<{
55
+ kind: "success" | "error"
56
+ text: string
57
+ } | null>(null)
58
+
59
+ const refresh = useCallback(async () => {
60
+ setLoading(true)
61
+ try {
62
+ const next = await schedulesApi.listSchedules(environment)
63
+ setResponse(next)
64
+ setError(null)
65
+ if (runsApi) {
66
+ const uniqueIds = Array.from(new Set(next.data.map((entry) => entry.workflowId)))
67
+ const results = await Promise.all(
68
+ uniqueIds.map(async (workflowId) => {
69
+ try {
70
+ const runs = await runsApi.listRuns({ workflowName: workflowId, limit: 1 })
71
+ return [workflowId, runs.data[0] ?? null] as const
72
+ } catch {
73
+ return [workflowId, null] as const
74
+ }
75
+ }),
76
+ )
77
+ setLastRuns(Object.fromEntries(results))
78
+ }
79
+ } catch (err) {
80
+ setError(err instanceof Error ? err.message : messages.loadError)
81
+ } finally {
82
+ setLoading(false)
83
+ }
84
+ }, [environment, messages.loadError, runsApi, schedulesApi])
85
+
86
+ useEffect(() => {
87
+ void refresh()
88
+ if (!pollIntervalMs) return
89
+ const interval = setInterval(() => void refresh(), pollIntervalMs)
90
+ return () => clearInterval(interval)
91
+ }, [pollIntervalMs, refresh])
92
+
93
+ const showEnvFlag = response?.schedulesEnabledByEnv !== undefined
94
+ const envFlagOn = response?.schedulesEnabledByEnv === true
95
+
96
+ const triggerRow = useCallback(
97
+ async (entry: WorkflowScheduleSummary) => {
98
+ if (!onTriggerNow) return
99
+ setTriggering((prev) => ({ ...prev, [entry.scheduleId]: true }))
100
+ setTriggerNotice(null)
101
+ try {
102
+ await onTriggerNow(entry.workflowId, entry.schedule.input)
103
+ setTriggerNotice({ kind: "success", text: messages.triggerSuccess })
104
+ } catch (err) {
105
+ setTriggerNotice({
106
+ kind: "error",
107
+ text: err instanceof Error ? err.message : messages.triggerFailed,
108
+ })
109
+ } finally {
110
+ setTriggering((prev) => ({ ...prev, [entry.scheduleId]: false }))
111
+ }
112
+ },
113
+ [messages.triggerFailed, messages.triggerSuccess, onTriggerNow],
114
+ )
115
+
116
+ const rows = useMemo(() => response?.data ?? [], [response])
117
+
118
+ return (
119
+ <div className={`flex min-h-screen flex-col bg-background ${className ?? ""}`}>
120
+ <header className="sticky top-0 z-10 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
121
+ <div className="container mx-auto flex flex-wrap items-center gap-3 px-4 py-3">
122
+ <CalendarClock className="h-5 w-5" />
123
+ <div className="min-w-0">
124
+ <h1 className="font-semibold text-base">{messages.title}</h1>
125
+ <p className="text-muted-foreground text-xs">{messages.subtitle}</p>
126
+ </div>
127
+ <div className="ml-auto flex items-center gap-2 text-muted-foreground text-xs">
128
+ <span>
129
+ {messages.environmentLabel}: <span className="font-mono">{environment}</span>
130
+ </span>
131
+ {response?.versionId ? (
132
+ <span>
133
+ {messages.versionLabel}:{" "}
134
+ <span className="font-mono">{response.versionId.slice(0, 12)}</span>
135
+ </span>
136
+ ) : null}
137
+ <Button
138
+ type="button"
139
+ variant="outline"
140
+ size="sm"
141
+ onClick={() => void refresh()}
142
+ disabled={loading}
143
+ >
144
+ <RefreshCw
145
+ className={`h-3.5 w-3.5 ${loading ? "animate-spin" : ""}`}
146
+ aria-hidden="true"
147
+ />
148
+ {messages.refresh}
149
+ </Button>
150
+ </div>
151
+ </div>
152
+ </header>
153
+
154
+ <main className="container mx-auto flex flex-1 flex-col gap-4 px-4 py-6">
155
+ {showEnvFlag && !envFlagOn ? (
156
+ <Card className="border-amber-500/40 bg-amber-500/5">
157
+ <CardContent className="flex items-center gap-2 pt-4 text-amber-700 text-sm dark:text-amber-300">
158
+ <AlertTriangle className="h-4 w-4" aria-hidden="true" />
159
+ {messages.envFlagOff}
160
+ </CardContent>
161
+ </Card>
162
+ ) : null}
163
+
164
+ {triggerNotice ? (
165
+ <Card
166
+ className={
167
+ triggerNotice.kind === "success"
168
+ ? "border-emerald-500/40 bg-emerald-500/5"
169
+ : "border-destructive/40 bg-destructive/5"
170
+ }
171
+ >
172
+ <CardContent
173
+ className={`pt-4 text-sm ${
174
+ triggerNotice.kind === "success"
175
+ ? "text-emerald-600 dark:text-emerald-300"
176
+ : "text-destructive"
177
+ }`}
178
+ >
179
+ {triggerNotice.text}
180
+ </CardContent>
181
+ </Card>
182
+ ) : null}
183
+
184
+ {error ? (
185
+ <Card className="border-destructive/40">
186
+ <CardContent className="pt-4 text-destructive text-sm">{error}</CardContent>
187
+ </Card>
188
+ ) : null}
189
+
190
+ {!error && rows.length === 0 && !loading ? (
191
+ <Card>
192
+ <CardContent className="pt-4 text-muted-foreground text-sm">
193
+ {messages.empty}
194
+ </CardContent>
195
+ </Card>
196
+ ) : null}
197
+
198
+ {rows.length > 0 ? (
199
+ <div className="overflow-hidden rounded-md border">
200
+ <table className="w-full text-sm">
201
+ <thead className="bg-muted/40 text-left text-xs uppercase">
202
+ <tr>
203
+ <th className="px-3 py-2 font-medium">{messages.workflowColumn}</th>
204
+ <th className="px-3 py-2 font-medium">{messages.scheduleColumn}</th>
205
+ <th className="px-3 py-2 font-medium">{messages.nextRunColumn}</th>
206
+ <th className="px-3 py-2 font-medium">{messages.lastRunColumn}</th>
207
+ <th className="px-3 py-2 font-medium">{messages.statusColumn}</th>
208
+ {onTriggerNow ? (
209
+ <th className="px-3 py-2 font-medium">{messages.actionsColumn}</th>
210
+ ) : null}
211
+ </tr>
212
+ </thead>
213
+ <tbody>
214
+ {rows.map((row) => (
215
+ <ScheduleRow
216
+ key={row.scheduleId}
217
+ row={row}
218
+ lastRun={lastRuns[row.workflowId] ?? null}
219
+ triggering={!!triggering[row.scheduleId]}
220
+ onTriggerNow={onTriggerNow ? () => void triggerRow(row) : undefined}
221
+ envFlagDisabled={showEnvFlag && !envFlagOn}
222
+ rootMessages={rootMessages}
223
+ />
224
+ ))}
225
+ </tbody>
226
+ </table>
227
+ </div>
228
+ ) : null}
229
+ </main>
230
+ </div>
231
+ )
232
+ }
233
+
234
+ function ScheduleRow({
235
+ row,
236
+ lastRun,
237
+ triggering,
238
+ onTriggerNow,
239
+ envFlagDisabled,
240
+ rootMessages,
241
+ }: {
242
+ row: WorkflowScheduleSummary
243
+ lastRun: WorkflowRun | null
244
+ triggering: boolean
245
+ onTriggerNow?: () => void
246
+ envFlagDisabled: boolean
247
+ rootMessages: WorkflowRunsUiMessages
248
+ }) {
249
+ const messages = rootMessages.schedules
250
+
251
+ return (
252
+ <tr className="border-t">
253
+ <td className="px-3 py-2 font-mono text-xs">{row.workflowId}</td>
254
+ <td className="px-3 py-2 font-mono text-xs">{formatScheduleDecl(row.schedule, messages)}</td>
255
+ <td className="px-3 py-2 text-xs text-muted-foreground">
256
+ {formatNextRun(row, messages, rootMessages)}
257
+ </td>
258
+ <td className="px-3 py-2 text-xs">{formatLastRun(lastRun, row, messages, rootMessages)}</td>
259
+ <td className="px-3 py-2">
260
+ <StatusPill row={row} envFlagDisabled={envFlagDisabled} messages={messages} />
261
+ </td>
262
+ {onTriggerNow ? (
263
+ <td className="px-3 py-2">
264
+ <Button
265
+ type="button"
266
+ size="sm"
267
+ variant="outline"
268
+ onClick={onTriggerNow}
269
+ disabled={triggering}
270
+ >
271
+ {triggering ? messages.triggering : messages.triggerNow}
272
+ </Button>
273
+ </td>
274
+ ) : null}
275
+ </tr>
276
+ )
277
+ }
278
+
279
+ function StatusPill({
280
+ row,
281
+ envFlagDisabled,
282
+ messages,
283
+ }: {
284
+ row: WorkflowScheduleSummary
285
+ envFlagDisabled: boolean
286
+ messages: SchedulesMessages
287
+ }) {
288
+ if (envFlagDisabled) {
289
+ return (
290
+ <Badge variant="outline" className="border-muted-foreground/30 text-muted-foreground">
291
+ {messages.disabledByEnvFlag}
292
+ </Badge>
293
+ )
294
+ }
295
+ if (row.disabledReason === "registration_disabled") {
296
+ return (
297
+ <Badge variant="outline" className="border-muted-foreground/30 text-muted-foreground">
298
+ {messages.disabledByRegistration}
299
+ </Badge>
300
+ )
301
+ }
302
+ if (row.disabledReason === "env_filtered") {
303
+ return (
304
+ <Badge variant="outline" className="border-muted-foreground/30 text-muted-foreground">
305
+ {messages.disabledByEnvironment}
306
+ </Badge>
307
+ )
308
+ }
309
+ return (
310
+ <Badge
311
+ variant="outline"
312
+ className="border-emerald-500/40 bg-emerald-500/10 text-emerald-600 dark:text-emerald-300"
313
+ >
314
+ {messages.enabled}
315
+ </Badge>
316
+ )
317
+ }
318
+
319
+ function formatScheduleDecl(decl: WorkflowScheduleDecl, messages: SchedulesMessages): string {
320
+ if (decl.cron) return messages.cron(decl.cron, decl.timezone ?? "UTC")
321
+ if (decl.every !== undefined) return messages.every(String(decl.every))
322
+ if (decl.at) return messages.at(decl.at)
323
+ return messages.eventDriven
324
+ }
325
+
326
+ function formatNextRun(
327
+ row: WorkflowScheduleSummary,
328
+ messages: SchedulesMessages,
329
+ rootMessages: WorkflowRunsUiMessages,
330
+ ): string {
331
+ if (!row.enabled || row.nextRunAt === null) return messages.notScheduled
332
+ const delta = row.nextRunAt - Date.now()
333
+ const relative = formatRelative(new Date(row.nextRunAt).toISOString(), rootMessages)
334
+ return delta >= 0 ? messages.inFuture(relative) : messages.inPast(relative)
335
+ }
336
+
337
+ function formatLastRun(
338
+ lastRun: WorkflowRun | null,
339
+ row: WorkflowScheduleSummary,
340
+ messages: SchedulesMessages,
341
+ rootMessages: WorkflowRunsUiMessages,
342
+ ) {
343
+ if (lastRun) {
344
+ const relative = formatRelative(lastRun.startedAt, rootMessages)
345
+ const label = lastRunLabel(lastRun.status, relative, messages)
346
+ return (
347
+ <span className="inline-flex items-center gap-1.5">
348
+ <StatusIcon status={lastRun.status} />
349
+ {label}
350
+ </span>
351
+ )
352
+ }
353
+ if (row.lastError && row.lastFireAt !== undefined && row.lastFireAt !== null) {
354
+ const relative = formatRelative(new Date(row.lastFireAt).toISOString(), rootMessages)
355
+ return (
356
+ <span className="inline-flex items-center gap-1.5" title={row.lastError}>
357
+ <StatusIcon status="failed" />
358
+ {messages.lastRunFailed(relative)}
359
+ </span>
360
+ )
361
+ }
362
+ if (row.lastSuccessfulRunAt !== undefined && row.lastSuccessfulRunAt !== null) {
363
+ const relative = formatRelative(new Date(row.lastSuccessfulRunAt).toISOString(), rootMessages)
364
+ return (
365
+ <span className="inline-flex items-center gap-1.5">
366
+ <StatusIcon status="succeeded" />
367
+ {messages.lastRunSucceeded(relative)}
368
+ </span>
369
+ )
370
+ }
371
+ if (row.lastFireAt !== undefined && row.lastFireAt !== null) {
372
+ const relative = formatRelative(new Date(row.lastFireAt).toISOString(), rootMessages)
373
+ return (
374
+ <span className="inline-flex items-center gap-1.5">
375
+ <CalendarClock className="h-3.5 w-3.5 text-muted-foreground" aria-hidden="true" />
376
+ {messages.lastFireRecorded(relative)}
377
+ </span>
378
+ )
379
+ }
380
+ return <span className="text-muted-foreground">{messages.lastRunNone}</span>
381
+ }
382
+
383
+ function lastRunLabel(
384
+ status: WorkflowRunStatus,
385
+ relative: string,
386
+ messages: SchedulesMessages,
387
+ ): string {
388
+ switch (status) {
389
+ case "succeeded":
390
+ return messages.lastRunSucceeded(relative)
391
+ case "failed":
392
+ return messages.lastRunFailed(relative)
393
+ case "running":
394
+ return messages.lastRunRunning
395
+ case "cancelled":
396
+ return messages.lastRunCancelled(relative)
397
+ }
398
+ }
package/src/i18n/en.ts ADDED
@@ -0,0 +1,142 @@
1
+ import type { WorkflowRunsUiMessages } from "./messages.js"
2
+
3
+ export const workflowRunsUiEn: WorkflowRunsUiMessages = {
4
+ page: {
5
+ title: "Workflow runs",
6
+ subtitle: "Recent workflow executions and recorded steps.",
7
+ filterTitle: "Filter",
8
+ workflowLabel: "Workflow",
9
+ workflowPlaceholder: "checkout-finalize",
10
+ workflowEmpty: "No workflows in the visible runs.",
11
+ searchLabel: "Search",
12
+ searchPlaceholder: "Correlation, tag, error, input...",
13
+ statusLabel: "Status",
14
+ tagLabel: "Tag",
15
+ tagPlaceholder: "bookingId:bk_...",
16
+ tagEmpty: "No tags in the visible runs.",
17
+ addTag: "Add",
18
+ removeTag: "Remove tag",
19
+ timeRangeLabel: "Started",
20
+ live: "Live",
21
+ clearFilters: "Clear",
22
+ anyStatus: "Any",
23
+ empty: "No runs yet.",
24
+ selectPrompt: "Pick a run from the list to see its steps and payloads.",
25
+ loading: "Loading...",
26
+ loadError: "Could not load workflow runs.",
27
+ runCount: (count) => `${count} ${count === 1 ? "run" : "runs"}`,
28
+ filteredRunCount: (filtered, total) =>
29
+ filtered === total ? `${total} ${total === 1 ? "run" : "runs"}` : `${filtered}/${total} runs`,
30
+ timeRanges: {
31
+ "15m": "15m",
32
+ "1h": "1h",
33
+ "24h": "24h",
34
+ "7d": "7d",
35
+ all: "All",
36
+ },
37
+ },
38
+ status: {
39
+ running: "Running",
40
+ succeeded: "Succeeded",
41
+ failed: "Failed",
42
+ cancelled: "Cancelled",
43
+ },
44
+ stepStatus: {
45
+ running: "Running",
46
+ succeeded: "Succeeded",
47
+ failed: "Failed",
48
+ skipped: "Skipped",
49
+ compensated: "Compensated",
50
+ },
51
+ detail: {
52
+ trigger: "Trigger",
53
+ correlation: "Correlation",
54
+ parent: "Parent",
55
+ triggeredBy: "Triggered by",
56
+ tags: "Tags",
57
+ started: "Started",
58
+ finished: "Finished",
59
+ steps: "Steps",
60
+ noSteps: "No steps recorded.",
61
+ input: "Input",
62
+ output: "Output",
63
+ result: "Result",
64
+ runError: "Run error",
65
+ stackTrace: "Stack trace",
66
+ copy: "Copy",
67
+ copied: "Copied",
68
+ code: "code",
69
+ step: "step",
70
+ reruns: "Reruns of this run",
71
+ resumedAt: (step) => `resumed @ ${step}`,
72
+ durationUnavailable: "-",
73
+ },
74
+ actions: {
75
+ rerun: "Rerun",
76
+ rerunBusy: "Rerunning",
77
+ resume: "Resume from failed step",
78
+ resumeBusy: "Resuming",
79
+ waitForCompletion: "Wait for this run to finish before rerunning.",
80
+ rerunDescription: "Start a new run with the same recorded input.",
81
+ resumeDescription: "Skip already-completed steps and retry from the failed step.",
82
+ resumeUnavailable: "Resume is only available for failed runs.",
83
+ actionFailed: "Action failed.",
84
+ rerunStarted: "Rerun started; opening new run.",
85
+ resumeStarted: (step) => `Resumed from step "${step}"; opening new run.`,
86
+ runnerMissing:
87
+ "No runner is registered for this workflow. Register a WorkflowRunner before rerun is available.",
88
+ rerunBlocked: "Rerun was blocked by the runner safety predicate.",
89
+ incompletePriorStep: "Cannot resume because an earlier step is incomplete.",
90
+ confirmTitle: "Confirm rerun",
91
+ confirmBody:
92
+ "This workflow has side effects that re-fire on rerun. The new run starts from the first step with the same recorded input.",
93
+ confirmTip:
94
+ "If the original failed mid-way, prefer Resume from failed step because it skips completed work.",
95
+ cancel: "Cancel",
96
+ rerunAnyway: "Rerun anyway",
97
+ },
98
+ format: {
99
+ relativeNow: "now",
100
+ },
101
+ schedules: {
102
+ title: "Workflow schedules",
103
+ subtitle: "Registered cron, interval, and one-shot schedules.",
104
+ environmentLabel: "Environment",
105
+ versionLabel: "Manifest",
106
+ workflowColumn: "Workflow",
107
+ scheduleColumn: "Schedule",
108
+ nextRunColumn: "Next run",
109
+ lastRunColumn: "Last run",
110
+ statusColumn: "Status",
111
+ actionsColumn: "Actions",
112
+ enabled: "Enabled",
113
+ disabledByRegistration: "Disabled",
114
+ disabledByEnvironment: "Disabled (env filtered)",
115
+ disabledByEnvFlag: "Disabled (env flag)",
116
+ envFlagOff: "Schedules disabled by environment flag — no schedules will fire.",
117
+ envFlagOn: "Schedules enabled.",
118
+ eventDriven: "event-driven",
119
+ cron: (expr, timezone) => `${expr} (${timezone})`,
120
+ every: (interval) => `every ${interval}`,
121
+ at: (timestamp) => `at ${timestamp}`,
122
+ triggerNow: "Trigger now",
123
+ triggering: "Triggering...",
124
+ triggerSuccess: "Run started.",
125
+ triggerFailed: "Trigger failed.",
126
+ refresh: "Refresh",
127
+ loading: "Loading schedules...",
128
+ loadError: "Could not load workflow schedules.",
129
+ empty: "No schedules registered for this environment.",
130
+ inFuture: (relative) => `in ${relative}`,
131
+ inPast: (relative) => `${relative} ago`,
132
+ notScheduled: "—",
133
+ lastRunSucceeded: (relative) => `succeeded ${relative} ago`,
134
+ lastRunFailed: (relative) => `failed ${relative} ago`,
135
+ lastRunRunning: "running",
136
+ lastRunCancelled: (relative) => `cancelled ${relative} ago`,
137
+ lastFireRecorded: (relative) => `fired ${relative} ago`,
138
+ lastRunNone: "never run",
139
+ },
140
+ }
141
+
142
+ export const workflowsUiEn = workflowRunsUiEn
@@ -0,0 +1,26 @@
1
+ export { workflowRunsUiEn, workflowRunsUiEn as workflowsUiEn } from "./en.js"
2
+ export type {
3
+ WorkflowRunsUiMessages,
4
+ WorkflowRunsUiMessages as WorkflowsUiMessages,
5
+ } from "./messages.js"
6
+ export {
7
+ getWorkflowRunsUiI18n,
8
+ getWorkflowRunsUiI18n as getWorkflowsUiI18n,
9
+ resolveWorkflowRunsUiMessages,
10
+ resolveWorkflowRunsUiMessages as resolveWorkflowsUiMessages,
11
+ useWorkflowRunsUiI18n,
12
+ useWorkflowRunsUiI18n as useWorkflowsUiI18n,
13
+ useWorkflowRunsUiI18nOrDefault,
14
+ useWorkflowRunsUiI18nOrDefault as useWorkflowsUiI18nOrDefault,
15
+ useWorkflowRunsUiMessages,
16
+ useWorkflowRunsUiMessages as useWorkflowsUiMessages,
17
+ useWorkflowRunsUiMessagesOrDefault,
18
+ useWorkflowRunsUiMessagesOrDefault as useWorkflowsUiMessagesOrDefault,
19
+ type WorkflowRunsUiMessageOverrides,
20
+ type WorkflowRunsUiMessageOverrides as WorkflowsUiMessageOverrides,
21
+ WorkflowRunsUiMessagesProvider,
22
+ WorkflowRunsUiMessagesProvider as WorkflowsUiMessagesProvider,
23
+ workflowRunsUiMessageDefinitions,
24
+ workflowRunsUiMessageDefinitions as workflowsUiMessageDefinitions,
25
+ } from "./provider.js"
26
+ export { workflowRunsUiRo, workflowRunsUiRo as workflowsUiRo } from "./ro.js"
@@ -0,0 +1,125 @@
1
+ import type { WorkflowRunStatus, WorkflowRunStepStatus } from "../types.js"
2
+
3
+ export type WorkflowRunsUiMessages = {
4
+ page: {
5
+ title: string
6
+ subtitle: string
7
+ filterTitle: string
8
+ workflowLabel: string
9
+ workflowPlaceholder: string
10
+ workflowEmpty: string
11
+ searchLabel: string
12
+ searchPlaceholder: string
13
+ statusLabel: string
14
+ tagLabel: string
15
+ tagPlaceholder: string
16
+ tagEmpty: string
17
+ addTag: string
18
+ removeTag: string
19
+ timeRangeLabel: string
20
+ live: string
21
+ clearFilters: string
22
+ anyStatus: string
23
+ empty: string
24
+ selectPrompt: string
25
+ loading: string
26
+ loadError: string
27
+ runCount: (count: number) => string
28
+ filteredRunCount: (filtered: number, total: number) => string
29
+ timeRanges: {
30
+ "15m": string
31
+ "1h": string
32
+ "24h": string
33
+ "7d": string
34
+ all: string
35
+ }
36
+ }
37
+ status: Record<WorkflowRunStatus, string>
38
+ stepStatus: Record<WorkflowRunStepStatus, string>
39
+ detail: {
40
+ trigger: string
41
+ correlation: string
42
+ parent: string
43
+ triggeredBy: string
44
+ tags: string
45
+ started: string
46
+ finished: string
47
+ steps: string
48
+ noSteps: string
49
+ input: string
50
+ output: string
51
+ result: string
52
+ runError: string
53
+ stackTrace: string
54
+ copy: string
55
+ copied: string
56
+ code: string
57
+ step: string
58
+ reruns: string
59
+ resumedAt: (step: string) => string
60
+ durationUnavailable: string
61
+ }
62
+ actions: {
63
+ rerun: string
64
+ rerunBusy: string
65
+ resume: string
66
+ resumeBusy: string
67
+ waitForCompletion: string
68
+ rerunDescription: string
69
+ resumeDescription: string
70
+ resumeUnavailable: string
71
+ actionFailed: string
72
+ rerunStarted: string
73
+ resumeStarted: (step: string) => string
74
+ runnerMissing: string
75
+ rerunBlocked: string
76
+ incompletePriorStep: string
77
+ confirmTitle: string
78
+ confirmBody: string
79
+ confirmTip: string
80
+ cancel: string
81
+ rerunAnyway: string
82
+ }
83
+ format: {
84
+ relativeNow: string
85
+ }
86
+ schedules: {
87
+ title: string
88
+ subtitle: string
89
+ environmentLabel: string
90
+ versionLabel: string
91
+ workflowColumn: string
92
+ scheduleColumn: string
93
+ nextRunColumn: string
94
+ lastRunColumn: string
95
+ statusColumn: string
96
+ actionsColumn: string
97
+ enabled: string
98
+ disabledByRegistration: string
99
+ disabledByEnvironment: string
100
+ disabledByEnvFlag: string
101
+ envFlagOff: string
102
+ envFlagOn: string
103
+ eventDriven: string
104
+ cron: (expr: string, timezone: string) => string
105
+ every: (interval: string) => string
106
+ at: (timestamp: string) => string
107
+ triggerNow: string
108
+ triggering: string
109
+ triggerSuccess: string
110
+ triggerFailed: string
111
+ refresh: string
112
+ loading: string
113
+ loadError: string
114
+ empty: string
115
+ inFuture: (relative: string) => string
116
+ inPast: (relative: string) => string
117
+ notScheduled: string
118
+ lastRunSucceeded: (relative: string) => string
119
+ lastRunFailed: (relative: string) => string
120
+ lastRunRunning: string
121
+ lastRunCancelled: (relative: string) => string
122
+ lastFireRecorded: (relative: string) => string
123
+ lastRunNone: string
124
+ }
125
+ }