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