@shipfox/client-workflows 14.0.0 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/.storybook/preview.tsx +7 -2
  2. package/.turbo/turbo-build.log +1 -1
  3. package/CHANGELOG.md +35 -0
  4. package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -0
  5. package/dist/components/{workflow-run-view → job-detail}/agent-config-failure-callout.js +2 -2
  6. package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -0
  7. package/dist/components/job-detail/job-detail-header.d.ts +8 -0
  8. package/dist/components/job-detail/job-detail-header.d.ts.map +1 -0
  9. package/dist/components/job-detail/job-detail-header.js +111 -0
  10. package/dist/components/job-detail/job-detail-header.js.map +1 -0
  11. package/dist/components/job-detail/job-detail-view.d.ts +16 -0
  12. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -0
  13. package/dist/components/job-detail/job-detail-view.js +405 -0
  14. package/dist/components/job-detail/job-detail-view.js.map +1 -0
  15. package/dist/components/job-detail/job-empty-states.d.ts +10 -0
  16. package/dist/components/job-detail/job-empty-states.d.ts.map +1 -0
  17. package/dist/components/job-detail/job-empty-states.js +195 -0
  18. package/dist/components/job-detail/job-empty-states.js.map +1 -0
  19. package/dist/components/job-detail/job-execution-switcher.d.ts.map +1 -0
  20. package/dist/components/job-detail/job-execution-switcher.js.map +1 -0
  21. package/dist/components/job-detail/job-execution-time-text.d.ts.map +1 -0
  22. package/dist/components/job-detail/job-execution-time-text.js.map +1 -0
  23. package/dist/components/job-detail/job-selection.d.ts +24 -0
  24. package/dist/components/job-detail/job-selection.d.ts.map +1 -0
  25. package/dist/components/job-detail/job-selection.js +66 -0
  26. package/dist/components/job-detail/job-selection.js.map +1 -0
  27. package/dist/components/job-detail/step-attempt-log-panel.d.ts +15 -0
  28. package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -0
  29. package/dist/components/{workflow-run-view → job-detail}/step-attempt-log-panel.js +61 -16
  30. package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -0
  31. package/dist/components/job-graph/job-graph-content.js +1 -1
  32. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  33. package/dist/components/step-list/step-list-model.js +2 -2
  34. package/dist/components/step-list/step-list-model.js.map +1 -1
  35. package/dist/components/step-list/step-list.d.ts +1 -0
  36. package/dist/components/step-list/step-list.d.ts.map +1 -1
  37. package/dist/components/step-list/step-list.js +29 -4
  38. package/dist/components/step-list/step-list.js.map +1 -1
  39. package/dist/components/workflow-run-duration-label.d.ts +9 -4
  40. package/dist/components/workflow-run-duration-label.d.ts.map +1 -1
  41. package/dist/components/workflow-run-duration-label.js +27 -7
  42. package/dist/components/workflow-run-duration-label.js.map +1 -1
  43. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  44. package/dist/components/workflow-run-list/workflow-run-row.js +6 -5
  45. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  46. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  47. package/dist/components/workflow-run-summary/workflow-run-summary.js +68 -64
  48. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  49. package/dist/components/workflow-run-tabs/index.d.ts +0 -3
  50. package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
  51. package/dist/components/workflow-run-tabs/index.js +0 -3
  52. package/dist/components/workflow-run-tabs/index.js.map +1 -1
  53. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +3 -1
  54. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -1
  55. package/dist/components/workflow-run-tabs/run-annotations-empty.js +2 -2
  56. package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -1
  57. package/dist/components/workflow-run-view/index.d.ts +0 -1
  58. package/dist/components/workflow-run-view/index.d.ts.map +1 -1
  59. package/dist/components/workflow-run-view/index.js +0 -1
  60. package/dist/components/workflow-run-view/index.js.map +1 -1
  61. package/dist/components/workflow-run-view/run-workspace-nav.d.ts +20 -0
  62. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -0
  63. package/dist/components/workflow-run-view/run-workspace-nav.js +271 -0
  64. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -0
  65. package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
  66. package/dist/components/workflow-run-view/workflow-run-selection.js +1 -14
  67. package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
  68. package/dist/components/workflow-run-view/workflow-run-view.d.ts +10 -8
  69. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  70. package/dist/components/workflow-run-view/workflow-run-view.js +250 -236
  71. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  72. package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
  73. package/dist/components/workflow-status/status-visuals.js +10 -2
  74. package/dist/components/workflow-status/status-visuals.js.map +1 -1
  75. package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -1
  76. package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
  77. package/dist/components/workflow-status/workflow-status-icon.js +13 -2
  78. package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
  79. package/dist/core/entities/job-execution.d.ts +9 -0
  80. package/dist/core/entities/job-execution.d.ts.map +1 -1
  81. package/dist/core/entities/job-execution.js +13 -3
  82. package/dist/core/entities/job-execution.js.map +1 -1
  83. package/dist/core/entities/job.d.ts.map +1 -1
  84. package/dist/core/entities/step-attempt.d.ts.map +1 -1
  85. package/dist/core/entities/step.d.ts +2 -0
  86. package/dist/core/entities/step.d.ts.map +1 -1
  87. package/dist/core/entities/step.js +13 -0
  88. package/dist/core/entities/step.js.map +1 -1
  89. package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
  90. package/dist/core/entities/workflow-run.d.ts +56 -1
  91. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  92. package/dist/core/entities/workflow-run.js +98 -0
  93. package/dist/core/entities/workflow-run.js.map +1 -1
  94. package/dist/core/workflow-run.d.ts +4 -4
  95. package/dist/core/workflow-run.d.ts.map +1 -1
  96. package/dist/core/workflow-run.js +3 -3
  97. package/dist/core/workflow-run.js.map +1 -1
  98. package/dist/feature.d.ts +20 -16
  99. package/dist/feature.d.ts.map +1 -1
  100. package/dist/feature.js +5 -0
  101. package/dist/feature.js.map +1 -1
  102. package/dist/hooks/api/workflow-runs.d.ts +5 -3
  103. package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
  104. package/dist/hooks/api/workflow-runs.js +5 -4
  105. package/dist/hooks/api/workflow-runs.js.map +1 -1
  106. package/dist/index.d.ts +1 -0
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +1 -0
  109. package/dist/index.js.map +1 -1
  110. package/dist/pages/workflow-job-detail-page.d.ts +11 -0
  111. package/dist/pages/workflow-job-detail-page.d.ts.map +1 -0
  112. package/dist/pages/workflow-job-detail-page.js +66 -0
  113. package/dist/pages/workflow-job-detail-page.js.map +1 -0
  114. package/dist/pages/workflow-run-detail-page.d.ts.map +1 -1
  115. package/dist/pages/workflow-run-detail-page.js +4 -37
  116. package/dist/pages/workflow-run-detail-page.js.map +1 -1
  117. package/dist/routes/inputs.d.ts +47 -34
  118. package/dist/routes/inputs.d.ts.map +1 -1
  119. package/dist/routes/inputs.js +69 -37
  120. package/dist/routes/inputs.js.map +1 -1
  121. package/dist/routes/job-detail.d.ts +10 -0
  122. package/dist/routes/job-detail.d.ts.map +1 -0
  123. package/dist/routes/job-detail.js +27 -0
  124. package/dist/routes/job-detail.js.map +1 -0
  125. package/dist/routes/project-route.d.ts +1 -1
  126. package/dist/routes/run-detail.d.ts.map +1 -1
  127. package/dist/routes/runs.d.ts.map +1 -1
  128. package/dist/routes/workflows.d.ts.map +1 -1
  129. package/dist/tsconfig.test.tsbuildinfo +1 -1
  130. package/package.json +7 -7
  131. package/src/components/{workflow-run-view → job-detail}/agent-config-failure-callout.stories.tsx +1 -1
  132. package/src/components/{workflow-run-view → job-detail}/agent-config-failure-callout.tsx +2 -2
  133. package/src/components/job-detail/job-detail-header.test.tsx +31 -0
  134. package/src/components/job-detail/job-detail-header.tsx +111 -0
  135. package/src/components/job-detail/job-detail-view.stories.tsx +526 -0
  136. package/src/components/job-detail/job-detail-view.tsx +450 -0
  137. package/src/components/job-detail/job-empty-states.test.ts +27 -0
  138. package/src/components/job-detail/job-empty-states.tsx +248 -0
  139. package/src/components/job-detail/job-selection.test.ts +188 -0
  140. package/src/components/job-detail/job-selection.ts +96 -0
  141. package/src/components/{workflow-run-view → job-detail}/step-attempt-log-panel.test.tsx +32 -9
  142. package/src/components/{workflow-run-view → job-detail}/step-attempt-log-panel.tsx +52 -18
  143. package/src/components/job-graph/job-graph-content.tsx +1 -1
  144. package/src/components/step-list/step-list-model.test.ts +1 -1
  145. package/src/components/step-list/step-list-model.ts +2 -2
  146. package/src/components/step-list/step-list.stories.tsx +1 -1
  147. package/src/components/step-list/step-list.test.tsx +33 -0
  148. package/src/components/step-list/step-list.tsx +40 -9
  149. package/src/components/workflow-run-duration-label.tsx +50 -12
  150. package/src/components/workflow-run-list/workflow-run-row.tsx +6 -4
  151. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +38 -0
  152. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +79 -5
  153. package/src/components/workflow-run-summary/workflow-run-summary.tsx +56 -60
  154. package/src/components/workflow-run-tabs/index.ts +0 -3
  155. package/src/components/workflow-run-tabs/run-annotations-empty.tsx +6 -2
  156. package/src/components/workflow-run-view/index.ts +0 -1
  157. package/src/components/workflow-run-view/run-workspace-nav.test.tsx +242 -0
  158. package/src/components/workflow-run-view/run-workspace-nav.tsx +301 -0
  159. package/src/components/workflow-run-view/workflow-run-selection.ts +1 -23
  160. package/src/components/workflow-run-view/workflow-run-view.test.tsx +62 -1136
  161. package/src/components/workflow-run-view/workflow-run-view.tsx +330 -321
  162. package/src/components/workflow-status/status-visuals.ts +5 -2
  163. package/src/components/workflow-status/workflow-status-icon.stories.tsx +1 -0
  164. package/src/components/workflow-status/workflow-status-icon.tsx +26 -1
  165. package/src/core/entities/job-execution.ts +17 -2
  166. package/src/core/entities/step.ts +26 -0
  167. package/src/core/entities/workflow-run-display.test.ts +159 -0
  168. package/src/core/entities/workflow-run.ts +139 -1
  169. package/src/core/workflow-run.ts +16 -1
  170. package/src/feature.ts +5 -0
  171. package/src/hooks/api/workflow-runs.ts +6 -2
  172. package/src/index.ts +1 -0
  173. package/src/page-harness-budget.test.ts +1 -0
  174. package/src/pages/workflow-job-detail-page.test.tsx +539 -0
  175. package/src/pages/workflow-job-detail-page.tsx +84 -0
  176. package/src/pages/workflow-run-detail-page.tsx +3 -43
  177. package/src/pages/workflow-run-pages.test.tsx +77 -332
  178. package/src/routes/inputs.test.ts +56 -6
  179. package/src/routes/inputs.ts +64 -17
  180. package/src/routes/job-detail.tsx +29 -0
  181. package/test/pages.tsx +25 -3
  182. package/test/render.tsx +6 -1
  183. package/tsconfig.build.tsbuildinfo +1 -1
  184. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +0 -18
  185. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +0 -1
  186. package/dist/components/workflow-run-tabs/run-jobs-list.js +0 -107
  187. package/dist/components/workflow-run-tabs/run-jobs-list.js.map +0 -1
  188. package/dist/components/workflow-run-tabs/run-tab-count.d.ts +0 -6
  189. package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +0 -1
  190. package/dist/components/workflow-run-tabs/run-tab-count.js +0 -27
  191. package/dist/components/workflow-run-tabs/run-tab-count.js.map +0 -1
  192. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +0 -8
  193. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +0 -1
  194. package/dist/components/workflow-run-tabs/run-tab-strip.js +0 -82
  195. package/dist/components/workflow-run-tabs/run-tab-strip.js.map +0 -1
  196. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +0 -1
  197. package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +0 -1
  198. package/dist/components/workflow-run-view/job-card.d.ts +0 -17
  199. package/dist/components/workflow-run-view/job-card.d.ts.map +0 -1
  200. package/dist/components/workflow-run-view/job-card.js +0 -496
  201. package/dist/components/workflow-run-view/job-card.js.map +0 -1
  202. package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +0 -1
  203. package/dist/components/workflow-run-view/job-execution-switcher.js.map +0 -1
  204. package/dist/components/workflow-run-view/job-execution-time-text.d.ts.map +0 -1
  205. package/dist/components/workflow-run-view/job-execution-time-text.js.map +0 -1
  206. package/dist/components/workflow-run-view/step-attempt-log-panel.d.ts +0 -9
  207. package/dist/components/workflow-run-view/step-attempt-log-panel.d.ts.map +0 -1
  208. package/dist/components/workflow-run-view/step-attempt-log-panel.js.map +0 -1
  209. package/src/components/workflow-run-tabs/run-jobs-list.tsx +0 -151
  210. package/src/components/workflow-run-tabs/run-tab-count.tsx +0 -28
  211. package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +0 -52
  212. package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +0 -117
  213. package/src/components/workflow-run-tabs/run-tab-strip.tsx +0 -97
  214. package/src/components/workflow-run-view/job-card.stories.tsx +0 -789
  215. package/src/components/workflow-run-view/job-card.test.tsx +0 -115
  216. package/src/components/workflow-run-view/job-card.tsx +0 -614
  217. /package/dist/components/{workflow-run-view → job-detail}/agent-config-failure-callout.d.ts +0 -0
  218. /package/dist/components/{workflow-run-view → job-detail}/job-execution-switcher.d.ts +0 -0
  219. /package/dist/components/{workflow-run-view → job-detail}/job-execution-switcher.js +0 -0
  220. /package/dist/components/{workflow-run-view → job-detail}/job-execution-time-text.d.ts +0 -0
  221. /package/dist/components/{workflow-run-view → job-detail}/job-execution-time-text.js +0 -0
  222. /package/src/components/{workflow-run-view → job-detail}/job-execution-switcher.tsx +0 -0
  223. /package/src/components/{workflow-run-view → job-detail}/job-execution-time-text.tsx +0 -0
@@ -0,0 +1,526 @@
1
+ // biome-ignore-all lint/a11y/noRedundantRoles: the story mirrors the job log region contract.
2
+ // biome-ignore-all lint/a11y/noNoninteractiveTabindex: the story mirrors the focusable log surface.
3
+
4
+ import {type StepLogSnapshot, stepLogsQueryKeys} from '@shipfox/client-logs';
5
+ import {Text} from '@shipfox/react-ui/typography';
6
+ import type {Meta, StoryObj} from '@storybook/react';
7
+ import {QueryClient, QueryClientProvider} from '@tanstack/react-query';
8
+ import {type ReactNode, useState} from 'react';
9
+ import type {Job, JobExecution, WorkflowRunDetail} from '#core/workflow-run.js';
10
+ import type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';
11
+ import {
12
+ workflowJob,
13
+ workflowJobExecutionDto,
14
+ workflowRunAttemptDto,
15
+ workflowRunDetail,
16
+ workflowStepAttemptDto,
17
+ workflowStepDto,
18
+ } from '#test/fixtures/workflow-run.js';
19
+ import {JobDetailView} from './job-detail-view.js';
20
+
21
+ const WORKSPACE_SLUG = 'acme';
22
+ const PROJECT_SLUG = 'platform';
23
+ const RUN_ID = '11111111-1111-4111-8111-111111111111';
24
+ const LOG_RECORDS: StepLogSnapshot['records'] = [
25
+ {
26
+ v: 1,
27
+ ts: Date.parse('2026-06-26T11:58:00.000Z'),
28
+ type: 'output',
29
+ stream: 'stdout',
30
+ data: '$ pnpm test --filter=@shipfox/client-workflows\n',
31
+ },
32
+ {
33
+ v: 1,
34
+ ts: Date.parse('2026-06-26T11:58:04.000Z'),
35
+ type: 'output',
36
+ stream: 'stdout',
37
+ data: '370 tests passed\n',
38
+ },
39
+ ];
40
+
41
+ interface JobDetailStoryArgs {
42
+ run: WorkflowRunDetail;
43
+ jobId: string;
44
+ selectedExecutionId?: string | undefined;
45
+ }
46
+
47
+ type JobDetailQuery = ReturnType<typeof useWorkflowRunAttemptQuery>;
48
+
49
+ const meta = {
50
+ title: 'Workflows/JobDetail',
51
+ parameters: {
52
+ layout: 'fullscreen',
53
+ },
54
+ } satisfies Meta<JobDetailStoryArgs>;
55
+
56
+ export default meta;
57
+ type Story = StoryObj<typeof meta>;
58
+
59
+ export const Playground: Story = {
60
+ render: () => {
61
+ const run = playgroundRun();
62
+ return <JobDetailStoryFrame run={run} jobId={run.jobs[1]?.id ?? ''} />;
63
+ },
64
+ };
65
+
66
+ export const Executions: Story = {
67
+ render: () => {
68
+ const {run, jobId, selectedExecutionId} = executionStory();
69
+ return (
70
+ <JobDetailStoryFrame run={run} jobId={jobId} selectedExecutionId={selectedExecutionId} />
71
+ );
72
+ },
73
+ };
74
+
75
+ export const DataStates: Story = {
76
+ render: () => {
77
+ const partialRun = partialJobRun();
78
+ const emptyRun = emptyJobRun();
79
+ const loadedRun = playgroundRun();
80
+ return (
81
+ <div className="grid max-w-[1120px] gap-16 p-16 md:grid-cols-2">
82
+ <StateExample label="Loading">
83
+ <JobDetailStoryState
84
+ query={makeQuery(undefined, {isPending: true})}
85
+ jobId={loadedRun.jobs[0]?.id ?? ''}
86
+ />
87
+ </StateExample>
88
+ <StateExample label="Empty">
89
+ <JobDetailStoryState query={makeQuery(emptyRun)} jobId={emptyRun.jobs[0]?.id ?? ''} />
90
+ </StateExample>
91
+ <StateExample label="Error">
92
+ <JobDetailStoryState
93
+ query={makeQuery(undefined, {isError: true, error: new Error('Storybook error')})}
94
+ jobId="missing-job"
95
+ />
96
+ </StateExample>
97
+ <StateExample label="Partial">
98
+ <JobDetailStoryState query={makeQuery(partialRun)} jobId={partialRun.jobs[0]?.id ?? ''} />
99
+ </StateExample>
100
+ <StateExample label="Job not found">
101
+ <JobDetailStoryState query={makeQuery(loadedRun)} jobId="missing-job" />
102
+ </StateExample>
103
+ </div>
104
+ );
105
+ },
106
+ };
107
+
108
+ function JobDetailStoryFrame({run, jobId, selectedExecutionId}: JobDetailStoryArgs) {
109
+ const job = run.jobs.find((candidate) => candidate.id === jobId) ?? run.jobs[0];
110
+ const selectedExecution = job?.jobExecutions.find(
111
+ (execution) => execution.id === selectedExecutionId,
112
+ );
113
+ return (
114
+ <div className="flex min-h-screen min-w-0 bg-background-neutral-base">
115
+ <JobDetailStoryState
116
+ query={makeQuery(run)}
117
+ jobId={jobId}
118
+ search={selectedExecution ? {jobExecutionId: selectedExecution.id} : {}}
119
+ />
120
+ </div>
121
+ );
122
+ }
123
+
124
+ function JobDetailStoryState({
125
+ query,
126
+ jobId,
127
+ search = {},
128
+ }: {
129
+ query: JobDetailQuery;
130
+ jobId: string;
131
+ search?: Parameters<typeof JobDetailView>[0]['search'];
132
+ }) {
133
+ const run = query.data;
134
+ return (
135
+ <StoryQueryProvider run={run}>
136
+ <JobDetailView
137
+ workspaceSlug={WORKSPACE_SLUG}
138
+ projectSlug={PROJECT_SLUG}
139
+ workflowRunId={run?.id ?? RUN_ID}
140
+ jobId={jobId}
141
+ search={search}
142
+ query={query}
143
+ onSelectionChange={() => undefined}
144
+ />
145
+ </StoryQueryProvider>
146
+ );
147
+ }
148
+
149
+ function makeQuery(data: WorkflowRunDetail | undefined, overrides: Partial<JobDetailQuery> = {}) {
150
+ return {
151
+ isPending: false,
152
+ isError: false,
153
+ isFetching: false,
154
+ data,
155
+ error: null,
156
+ refetch: () => Promise.resolve({} as never),
157
+ ...overrides,
158
+ } as JobDetailQuery;
159
+ }
160
+
161
+ function StoryQueryProvider({
162
+ run,
163
+ children,
164
+ }: {
165
+ run: WorkflowRunDetail | undefined;
166
+ children: ReactNode;
167
+ }) {
168
+ const [queryClient] = useState(() => {
169
+ const client = new QueryClient({
170
+ defaultOptions: {queries: {staleTime: Number.POSITIVE_INFINITY}},
171
+ });
172
+ if (run) {
173
+ for (const job of run.jobs) {
174
+ for (const execution of job.jobExecutions) {
175
+ for (const step of execution.steps) {
176
+ for (const attempt of step.attempts) {
177
+ client.setQueryData<StepLogSnapshot>(
178
+ stepLogsQueryKeys.detail(step.id, attempt.attempt),
179
+ storyLogSnapshot(),
180
+ );
181
+ }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ return client;
187
+ });
188
+
189
+ return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
190
+ }
191
+
192
+ function storyLogSnapshot(): StepLogSnapshot {
193
+ return {
194
+ records: LOG_RECORDS,
195
+ nextCursor: LOG_RECORDS.length,
196
+ source: 'inline',
197
+ state: 'closed',
198
+ complete: true,
199
+ hasMore: false,
200
+ truncated: false,
201
+ totalBytes: null,
202
+ expiresAt: null,
203
+ };
204
+ }
205
+
206
+ function playgroundRun(): WorkflowRunDetail {
207
+ const setupId = '22222222-2222-4222-8222-222222222222';
208
+ const buildId = '33333333-3333-4333-8333-333333333333';
209
+ const deployId = '44444444-4444-4444-8444-444444444445';
210
+
211
+ return storyRun({
212
+ status: 'running',
213
+ jobs: [
214
+ makeJob({
215
+ id: setupId,
216
+ key: 'setup',
217
+ name: 'setup',
218
+ status: 'succeeded',
219
+ position: 0,
220
+ executions: [
221
+ makeExecution('55555555-5555-4555-8555-555555555555', setupId, 1, 'succeeded'),
222
+ ],
223
+ }),
224
+ makeJob({
225
+ id: buildId,
226
+ key: 'build',
227
+ name: 'build',
228
+ status: 'running',
229
+ position: 1,
230
+ executions: [
231
+ makeExecution('66666666-6666-4666-8666-666666666666', buildId, 1, 'running', 'running'),
232
+ ],
233
+ }),
234
+ makeJob({
235
+ id: deployId,
236
+ key: 'deploy',
237
+ name: 'deploy',
238
+ status: 'pending',
239
+ position: 2,
240
+ dependencies: ['build'],
241
+ executions: [],
242
+ }),
243
+ ],
244
+ });
245
+ }
246
+
247
+ function emptyJobRun(): WorkflowRunDetail {
248
+ return storyRun({
249
+ status: 'succeeded',
250
+ jobs: [
251
+ makeJob({
252
+ id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
253
+ key: 'skipped-job',
254
+ name: 'skipped-job',
255
+ status: 'skipped',
256
+ position: 0,
257
+ executions: [],
258
+ }),
259
+ ],
260
+ });
261
+ }
262
+
263
+ function partialJobRun(): WorkflowRunDetail {
264
+ const jobId = 'cccccccc-cccc-4ccc-8ccc-cccccccccccc';
265
+ return storyRun({
266
+ status: 'running',
267
+ jobs: [
268
+ makeJob({
269
+ id: jobId,
270
+ key: 'build',
271
+ name: 'build',
272
+ status: 'running',
273
+ position: 0,
274
+ executions: [
275
+ makeExecution('dddddddd-dddd-4ddd-8ddd-dddddddddddd', jobId, 1, 'running', 'running'),
276
+ ],
277
+ }),
278
+ ],
279
+ });
280
+ }
281
+
282
+ function executionStory() {
283
+ const jobId = '77777777-7777-4777-8777-777777777777';
284
+ const firstExecutionId = '88888888-8888-4888-8888-888888888888';
285
+ const secondExecutionId = '99999999-9999-4999-8999-999999999999';
286
+ const selectedExecutionId = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa';
287
+ const run = storyRun({
288
+ status: 'succeeded',
289
+ jobs: [
290
+ makeJob({
291
+ id: jobId,
292
+ key: 'release',
293
+ name: 'release',
294
+ status: 'succeeded',
295
+ position: 0,
296
+ executions: [
297
+ makeExecution(firstExecutionId, jobId, 1, 'failed', 'failed'),
298
+ makeExecution(secondExecutionId, jobId, 2, 'failed', 'failed'),
299
+ makeExecution(selectedExecutionId, jobId, 3, 'succeeded'),
300
+ ],
301
+ }),
302
+ ],
303
+ });
304
+
305
+ return {run, jobId, selectedExecutionId};
306
+ }
307
+
308
+ function storyRun({
309
+ status,
310
+ jobs,
311
+ }: {
312
+ status: WorkflowRunDetail['status'];
313
+ jobs: Job[];
314
+ }): WorkflowRunDetail {
315
+ return workflowRunDetail({
316
+ id: RUN_ID,
317
+ status,
318
+ current_attempt: 1,
319
+ latest_attempt: 1,
320
+ run_attempt: workflowRunAttemptDto({
321
+ workflow_run_id: RUN_ID,
322
+ attempt: 1,
323
+ status,
324
+ }),
325
+ jobs: jobs.map((job) => ({
326
+ id: job.id,
327
+ key: job.key,
328
+ name: job.name,
329
+ mode: job.mode,
330
+ status: job.status,
331
+ status_reason: job.statusReason,
332
+ carried_over: job.carriedOver,
333
+ listening: job.listening,
334
+ listener_status: job.listenerStatus,
335
+ dependencies: job.dependencies,
336
+ position: job.position,
337
+ run_attempt_id: job.runAttemptId,
338
+ created_at: job.createdAt,
339
+ updated_at: job.updatedAt,
340
+ job_executions: job.jobExecutions.map((execution) => ({
341
+ id: execution.id,
342
+ job_id: execution.jobId,
343
+ sequence: execution.sequence,
344
+ name: execution.name,
345
+ status: execution.status,
346
+ status_reason: execution.statusReason,
347
+ trigger_events: [],
348
+ outputs: null,
349
+ queued_at: execution.queuedAt,
350
+ started_at: execution.startedAt,
351
+ finished_at: execution.finishedAt,
352
+ timed_out_at: execution.timedOutAt,
353
+ created_at: execution.createdAt,
354
+ updated_at: execution.updatedAt,
355
+ steps: execution.steps.map((step) => ({
356
+ id: step.id,
357
+ job_execution_id: step.jobExecutionId,
358
+ key: step.key,
359
+ name: step.name,
360
+ source_location: null,
361
+ status: step.status,
362
+ type: step.type,
363
+ config: step.config,
364
+ error: null,
365
+ position: step.position,
366
+ current_attempt: step.currentAttempt,
367
+ exit_code: null,
368
+ outputs: null,
369
+ response: null,
370
+ gate_result: null,
371
+ created_at: step.createdAt,
372
+ updated_at: step.updatedAt,
373
+ attempts: step.attempts.map((attempt) => ({
374
+ id: attempt.id,
375
+ step_id: attempt.stepId,
376
+ attempt: attempt.attempt,
377
+ execution_order: attempt.executionOrder,
378
+ status: attempt.status,
379
+ exit_code: attempt.exitCode,
380
+ output: attempt.output,
381
+ outputs: attempt.outputs,
382
+ response: attempt.response,
383
+ error: attempt.error,
384
+ gate_result: null,
385
+ restart_feedback: attempt.restartFeedback,
386
+ started_at: attempt.startedAt,
387
+ finished_at: attempt.finishedAt,
388
+ })),
389
+ })),
390
+ })),
391
+ outputs: job.jobExecutions[0]?.outputs ?? null,
392
+ resolution_reason: job.resolutionReason,
393
+ })),
394
+ });
395
+ }
396
+
397
+ function makeJob({
398
+ id,
399
+ key,
400
+ name,
401
+ status,
402
+ position,
403
+ dependencies = [],
404
+ executions,
405
+ }: {
406
+ id: string;
407
+ key: string;
408
+ name: string;
409
+ status: 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'skipped';
410
+ position: number;
411
+ dependencies?: string[];
412
+ executions: WorkflowRunDetail['jobs'][number]['jobExecutions'];
413
+ }): Job {
414
+ return workflowJob({
415
+ id,
416
+ key,
417
+ name,
418
+ status,
419
+ position,
420
+ dependencies,
421
+ job_executions: executions.map((execution) => executionDto(execution)),
422
+ });
423
+ }
424
+
425
+ function makeExecution(
426
+ id: string,
427
+ jobId: string,
428
+ sequence: number,
429
+ status: JobExecution['status'],
430
+ stepStatus?: 'running' | 'failed' | undefined,
431
+ ): JobExecution {
432
+ const step = stepStatus
433
+ ? workflowStepDto({
434
+ id: `${id.slice(0, 8)}-5555-4555-8555-555555555555`,
435
+ key: 'tests',
436
+ name: 'tests',
437
+ status: stepStatus,
438
+ attempts: [
439
+ workflowStepAttemptDto({
440
+ id: `${id.slice(0, 8)}-6666-4666-8666-666666666666`,
441
+ status: stepStatus,
442
+ }),
443
+ ],
444
+ })
445
+ : undefined;
446
+ return workflowExecution({
447
+ id,
448
+ jobId,
449
+ sequence,
450
+ status,
451
+ steps: step ? [step] : [],
452
+ });
453
+ }
454
+
455
+ function workflowExecution({
456
+ id,
457
+ jobId,
458
+ sequence,
459
+ status,
460
+ steps,
461
+ }: {
462
+ id: string;
463
+ jobId: string;
464
+ sequence: number;
465
+ status: JobExecution['status'];
466
+ steps: ReturnType<typeof workflowStepDto>[];
467
+ }) {
468
+ return workflowJobExecutionDto({
469
+ id,
470
+ job_id: jobId,
471
+ sequence,
472
+ status,
473
+ steps,
474
+ });
475
+ }
476
+
477
+ function executionDto(execution: JobExecution) {
478
+ return workflowJobExecutionDto({
479
+ id: execution.id,
480
+ job_id: execution.jobId,
481
+ sequence: execution.sequence,
482
+ name: execution.name,
483
+ status: execution.status,
484
+ status_reason: execution.statusReason,
485
+ queued_at: execution.queuedAt,
486
+ started_at: execution.startedAt,
487
+ finished_at: execution.finishedAt,
488
+ timed_out_at: execution.timedOutAt,
489
+ steps: execution.steps.map((step) =>
490
+ workflowStepDto({
491
+ id: step.id,
492
+ job_execution_id: execution.id,
493
+ key: step.key,
494
+ name: step.name,
495
+ status: step.status,
496
+ type: step.type,
497
+ config: step.config,
498
+ position: step.position,
499
+ current_attempt: step.currentAttempt,
500
+ attempts: step.attempts.map((attempt) =>
501
+ workflowStepAttemptDto({
502
+ id: attempt.id,
503
+ step_id: step.id,
504
+ attempt: attempt.attempt,
505
+ execution_order: attempt.executionOrder,
506
+ status: attempt.status,
507
+ exit_code: attempt.exitCode,
508
+ started_at: attempt.startedAt,
509
+ finished_at: attempt.finishedAt,
510
+ }),
511
+ ),
512
+ }),
513
+ ),
514
+ });
515
+ }
516
+
517
+ function StateExample({label, children}: {label: string; children: ReactNode}) {
518
+ return (
519
+ <section className="min-w-0 rounded-8 border border-border-neutral-base bg-background-neutral-base p-12">
520
+ <Text as="h3" size="xs" bold className="mb-8 text-foreground-neutral-muted">
521
+ {label}
522
+ </Text>
523
+ {children}
524
+ </section>
525
+ );
526
+ }