@shipfox/client-workflows 12.0.2 → 14.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 (325) hide show
  1. package/.storybook/main.ts +16 -1
  2. package/.storybook/preview.tsx +3 -3
  3. package/.storybook/workspace-source.ts +31 -0
  4. package/.turbo/turbo-build.log +1 -1
  5. package/CHANGELOG.md +79 -0
  6. package/dist/components/job-graph/job-duration-format.d.ts +2 -2
  7. package/dist/components/job-graph/job-duration-format.d.ts.map +1 -1
  8. package/dist/components/job-graph/job-duration-format.js +4 -2
  9. package/dist/components/job-graph/job-duration-format.js.map +1 -1
  10. package/dist/components/job-graph/job-duration-label.js +1 -1
  11. package/dist/components/job-graph/job-duration-label.js.map +1 -1
  12. package/dist/components/job-graph/job-graph-content.d.ts +2 -1
  13. package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
  14. package/dist/components/job-graph/job-graph-content.js +3 -3
  15. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  16. package/dist/components/job-graph/job-graph-view.d.ts +2 -1
  17. package/dist/components/job-graph/job-graph-view.d.ts.map +1 -1
  18. package/dist/components/job-graph/job-graph-view.js +2 -2
  19. package/dist/components/job-graph/job-graph-view.js.map +1 -1
  20. package/dist/components/job-graph/job-node.d.ts +1 -1
  21. package/dist/components/job-graph/job-node.d.ts.map +1 -1
  22. package/dist/components/job-graph/job-node.js +10 -9
  23. package/dist/components/job-graph/job-node.js.map +1 -1
  24. package/dist/components/job-graph/types.d.ts +2 -1
  25. package/dist/components/job-graph/types.d.ts.map +1 -1
  26. package/dist/components/job-graph/types.js.map +1 -1
  27. package/dist/components/step-list/step-list.d.ts +2 -2
  28. package/dist/components/step-list/step-list.d.ts.map +1 -1
  29. package/dist/components/step-list/step-list.js.map +1 -1
  30. package/dist/components/workflow-run-duration-label.js +1 -1
  31. package/dist/components/workflow-run-duration-label.js.map +1 -1
  32. package/dist/components/workflow-run-list/job-status-strip.d.ts +21 -0
  33. package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -0
  34. package/dist/components/workflow-run-list/job-status-strip.js +185 -0
  35. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -0
  36. package/dist/components/workflow-run-list/run-display.d.ts +27 -3
  37. package/dist/components/workflow-run-list/run-display.d.ts.map +1 -1
  38. package/dist/components/workflow-run-list/run-display.js +78 -7
  39. package/dist/components/workflow-run-list/run-display.js.map +1 -1
  40. package/dist/components/workflow-run-list/types.d.ts +23 -24
  41. package/dist/components/workflow-run-list/types.d.ts.map +1 -1
  42. package/dist/components/workflow-run-list/types.js.map +1 -1
  43. package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts +22 -0
  44. package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts.map +1 -0
  45. package/dist/components/workflow-run-list/workflow-run-filter-menu.js +68 -0
  46. package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -0
  47. package/dist/components/workflow-run-list/workflow-run-filters.d.ts +18 -0
  48. package/dist/components/workflow-run-list/workflow-run-filters.d.ts.map +1 -0
  49. package/dist/components/workflow-run-list/workflow-run-filters.js +236 -0
  50. package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -0
  51. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts +8 -4
  52. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
  53. package/dist/components/workflow-run-list/workflow-run-list-content.js +40 -12
  54. package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
  55. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts +29 -2
  56. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
  57. package/dist/components/workflow-run-list/workflow-run-list-states.js +103 -44
  58. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  59. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
  60. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
  61. package/dist/components/workflow-run-list/workflow-run-list-view.js +60 -43
  62. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
  63. package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
  64. package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
  65. package/dist/components/workflow-run-list/workflow-run-list.js +20 -8
  66. package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
  67. package/dist/components/workflow-run-list/workflow-run-row.d.ts +19 -9
  68. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  69. package/dist/components/workflow-run-list/workflow-run-row.js +179 -112
  70. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  71. package/dist/components/workflow-run-number-label.d.ts +15 -0
  72. package/dist/components/workflow-run-number-label.d.ts.map +1 -0
  73. package/dist/components/workflow-run-number-label.js +41 -0
  74. package/dist/components/workflow-run-number-label.js.map +1 -0
  75. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts +3 -3
  76. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts.map +1 -1
  77. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +10 -9
  78. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
  79. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +4 -9
  80. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  81. package/dist/components/workflow-run-summary/workflow-run-summary.js +107 -74
  82. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  83. package/dist/components/workflow-run-tabs/index.d.ts +6 -0
  84. package/dist/components/workflow-run-tabs/index.d.ts.map +1 -0
  85. package/dist/components/workflow-run-tabs/index.js +7 -0
  86. package/dist/components/workflow-run-tabs/index.js.map +1 -0
  87. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +11 -0
  88. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -0
  89. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +71 -0
  90. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -0
  91. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +2 -0
  92. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -0
  93. package/dist/components/workflow-run-tabs/run-annotations-empty.js +11 -0
  94. package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -0
  95. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +18 -0
  96. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +1 -0
  97. package/dist/components/workflow-run-tabs/run-jobs-list.js +107 -0
  98. package/dist/components/workflow-run-tabs/run-jobs-list.js.map +1 -0
  99. package/dist/components/workflow-run-tabs/run-tab-count.d.ts +6 -0
  100. package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +1 -0
  101. package/dist/components/workflow-run-tabs/run-tab-count.js +27 -0
  102. package/dist/components/workflow-run-tabs/run-tab-count.js.map +1 -0
  103. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +8 -0
  104. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +1 -0
  105. package/dist/components/workflow-run-tabs/run-tab-strip.js +82 -0
  106. package/dist/components/workflow-run-tabs/run-tab-strip.js.map +1 -0
  107. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts +2 -2
  108. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +1 -1
  109. package/dist/components/workflow-run-view/agent-config-failure-callout.js +4 -4
  110. package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +1 -1
  111. package/dist/components/workflow-run-view/job-card.d.ts +2 -2
  112. package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
  113. package/dist/components/workflow-run-view/job-card.js +51 -32
  114. package/dist/components/workflow-run-view/job-card.js.map +1 -1
  115. package/dist/components/workflow-run-view/job-execution-switcher.d.ts +1 -1
  116. package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +1 -1
  117. package/dist/components/workflow-run-view/job-execution-switcher.js +20 -8
  118. package/dist/components/workflow-run-view/job-execution-switcher.js.map +1 -1
  119. package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
  120. package/dist/components/workflow-run-view/workflow-run-selection.js +40 -1
  121. package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
  122. package/dist/components/workflow-run-view/workflow-run-states.d.ts +1 -0
  123. package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
  124. package/dist/components/workflow-run-view/workflow-run-states.js +10 -1
  125. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
  126. package/dist/components/workflow-run-view/workflow-run-view.d.ts +10 -5
  127. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  128. package/dist/components/workflow-run-view/workflow-run-view.js +136 -85
  129. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  130. package/dist/components/workflow-source-panel/index.d.ts +1 -0
  131. package/dist/components/workflow-source-panel/index.d.ts.map +1 -1
  132. package/dist/components/workflow-source-panel/index.js +1 -0
  133. package/dist/components/workflow-source-panel/index.js.map +1 -1
  134. package/dist/components/workflow-source-panel/workflow-source-content.d.ts +12 -0
  135. package/dist/components/workflow-source-panel/workflow-source-content.d.ts.map +1 -0
  136. package/dist/components/workflow-source-panel/workflow-source-content.js +53 -0
  137. package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -0
  138. package/dist/components/workflow-source-panel/workflow-source-panel.d.ts +1 -1
  139. package/dist/components/workflow-source-panel/workflow-source-panel.d.ts.map +1 -1
  140. package/dist/components/workflow-source-panel/workflow-source-panel.js +7 -54
  141. package/dist/components/workflow-source-panel/workflow-source-panel.js.map +1 -1
  142. package/dist/components/workflow-status/status-visuals.d.ts +4 -4
  143. package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
  144. package/dist/components/workflow-status/status-visuals.js +7 -0
  145. package/dist/components/workflow-status/status-visuals.js.map +1 -1
  146. package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -4
  147. package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
  148. package/dist/components/workflow-status/workflow-status-icon.js +6 -4
  149. package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
  150. package/dist/core/entities/job-execution.d.ts +10 -0
  151. package/dist/core/entities/job-execution.d.ts.map +1 -1
  152. package/dist/core/entities/job-execution.js +11 -0
  153. package/dist/core/entities/job-execution.js.map +1 -1
  154. package/dist/core/entities/job.d.ts +4 -2
  155. package/dist/core/entities/job.d.ts.map +1 -1
  156. package/dist/core/entities/job.js +8 -1
  157. package/dist/core/entities/job.js.map +1 -1
  158. package/dist/core/entities/step.d.ts +1 -1
  159. package/dist/core/entities/step.d.ts.map +1 -1
  160. package/dist/core/entities/step.js +2 -0
  161. package/dist/core/entities/step.js.map +1 -1
  162. package/dist/core/entities/workflow-run.d.ts +52 -4
  163. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  164. package/dist/core/entities/workflow-run.js +22 -3
  165. package/dist/core/entities/workflow-run.js.map +1 -1
  166. package/dist/core/workflow-run-tabs.d.ts +9 -0
  167. package/dist/core/workflow-run-tabs.d.ts.map +1 -0
  168. package/dist/core/workflow-run-tabs.js +3 -0
  169. package/dist/core/workflow-run-tabs.js.map +1 -0
  170. package/dist/core/workflow-run.d.ts +6 -6
  171. package/dist/core/workflow-run.d.ts.map +1 -1
  172. package/dist/core/workflow-run.js +3 -3
  173. package/dist/core/workflow-run.js.map +1 -1
  174. package/dist/feature.d.ts +7 -7
  175. package/dist/feature.js +5 -5
  176. package/dist/feature.js.map +1 -1
  177. package/dist/hooks/api/workflow-run-mapper.d.ts +4 -3
  178. package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
  179. package/dist/hooks/api/workflow-run-mapper.js +31 -3
  180. package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
  181. package/dist/hooks/api/workflow-runs.d.ts +4 -4
  182. package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
  183. package/dist/hooks/api/workflow-runs.js +12 -4
  184. package/dist/hooks/api/workflow-runs.js.map +1 -1
  185. package/dist/index.d.ts +4 -3
  186. package/dist/index.d.ts.map +1 -1
  187. package/dist/index.js +3 -2
  188. package/dist/index.js.map +1 -1
  189. package/dist/pages/project-workflows-page.js +46 -1
  190. package/dist/pages/project-workflows-page.js.map +1 -1
  191. package/dist/pages/workflow-run-detail-page.d.ts +11 -0
  192. package/dist/pages/workflow-run-detail-page.d.ts.map +1 -0
  193. package/dist/pages/workflow-run-detail-page.js +52 -0
  194. package/dist/pages/workflow-run-detail-page.js.map +1 -0
  195. package/dist/pages/workflow-run-list-page.d.ts +10 -0
  196. package/dist/pages/workflow-run-list-page.d.ts.map +1 -0
  197. package/dist/pages/workflow-run-list-page.js +43 -0
  198. package/dist/pages/workflow-run-list-page.js.map +1 -0
  199. package/dist/routes/inputs.d.ts +94 -5
  200. package/dist/routes/inputs.d.ts.map +1 -1
  201. package/dist/routes/inputs.js +199 -13
  202. package/dist/routes/inputs.js.map +1 -1
  203. package/dist/routes/project-route.d.ts +6 -0
  204. package/dist/routes/project-route.d.ts.map +1 -0
  205. package/dist/routes/project-route.js +25 -0
  206. package/dist/routes/project-route.js.map +1 -0
  207. package/dist/routes/run-detail.d.ts.map +1 -1
  208. package/dist/routes/run-detail.js +12 -7
  209. package/dist/routes/run-detail.js.map +1 -1
  210. package/dist/routes/runs.d.ts.map +1 -1
  211. package/dist/routes/runs.js +11 -6
  212. package/dist/routes/runs.js.map +1 -1
  213. package/dist/routes/workflows.d.ts.map +1 -1
  214. package/dist/routes/workflows.js +6 -5
  215. package/dist/routes/workflows.js.map +1 -1
  216. package/dist/tsconfig.test.tsbuildinfo +1 -1
  217. package/package.json +9 -9
  218. package/src/components/job-graph/job-duration-format.ts +11 -2
  219. package/src/components/job-graph/job-duration-label.tsx +5 -1
  220. package/src/components/job-graph/job-graph-content.tsx +7 -4
  221. package/src/components/job-graph/job-graph-view.test.tsx +19 -8
  222. package/src/components/job-graph/job-graph-view.tsx +6 -3
  223. package/src/components/job-graph/job-graph.stories.tsx +24 -20
  224. package/src/components/job-graph/job-node.stories.tsx +11 -6
  225. package/src/components/job-graph/job-node.test.tsx +82 -12
  226. package/src/components/job-graph/job-node.tsx +22 -15
  227. package/src/components/job-graph/types.ts +5 -1
  228. package/src/components/step-list/step-list.stories.tsx +2 -3
  229. package/src/components/step-list/step-list.tsx +3 -2
  230. package/src/components/workflow-run-duration-label.tsx +1 -1
  231. package/src/components/workflow-run-list/job-status-strip.tsx +189 -0
  232. package/src/components/workflow-run-list/run-display.test.ts +169 -30
  233. package/src/components/workflow-run-list/run-display.ts +116 -9
  234. package/src/components/workflow-run-list/types.ts +21 -17
  235. package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +100 -0
  236. package/src/components/workflow-run-list/workflow-run-filters.tsx +243 -0
  237. package/src/components/workflow-run-list/workflow-run-list-content.tsx +44 -16
  238. package/src/components/workflow-run-list/workflow-run-list-states.tsx +128 -27
  239. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +441 -104
  240. package/src/components/workflow-run-list/workflow-run-list-view.tsx +73 -43
  241. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +140 -20
  242. package/src/components/workflow-run-list/workflow-run-list.tsx +20 -10
  243. package/src/components/workflow-run-list/workflow-run-row.tsx +173 -106
  244. package/src/components/workflow-run-number-label.test.tsx +45 -0
  245. package/src/components/workflow-run-number-label.tsx +47 -0
  246. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +7 -8
  247. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +15 -14
  248. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +29 -38
  249. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +30 -24
  250. package/src/components/workflow-run-summary/workflow-run-summary.tsx +102 -77
  251. package/src/components/workflow-run-tabs/index.ts +5 -0
  252. package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +69 -0
  253. package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +97 -0
  254. package/src/components/workflow-run-tabs/run-annotations-empty.tsx +11 -0
  255. package/src/components/workflow-run-tabs/run-jobs-list.tsx +151 -0
  256. package/src/components/workflow-run-tabs/run-tab-count.tsx +28 -0
  257. package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +52 -0
  258. package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +117 -0
  259. package/src/components/workflow-run-tabs/run-tab-strip.tsx +97 -0
  260. package/src/components/workflow-run-view/agent-config-failure-callout.stories.tsx +2 -3
  261. package/src/components/workflow-run-view/agent-config-failure-callout.tsx +4 -4
  262. package/src/components/workflow-run-view/job-card.stories.tsx +11 -10
  263. package/src/components/workflow-run-view/job-card.test.tsx +115 -0
  264. package/src/components/workflow-run-view/job-card.tsx +74 -35
  265. package/src/components/workflow-run-view/job-execution-switcher.tsx +31 -9
  266. package/src/components/workflow-run-view/workflow-run-selection.test.ts +48 -0
  267. package/src/components/workflow-run-view/workflow-run-selection.ts +48 -1
  268. package/src/components/workflow-run-view/workflow-run-states.tsx +12 -1
  269. package/src/components/workflow-run-view/workflow-run-view.test.tsx +152 -94
  270. package/src/components/workflow-run-view/workflow-run-view.tsx +219 -95
  271. package/src/components/workflow-source-panel/index.ts +1 -0
  272. package/src/components/workflow-source-panel/workflow-source-content.tsx +84 -0
  273. package/src/components/workflow-source-panel/workflow-source-panel.tsx +14 -81
  274. package/src/components/workflow-status/status-visuals.test.ts +9 -0
  275. package/src/components/workflow-status/status-visuals.ts +6 -4
  276. package/src/components/workflow-status/workflow-status-icon.stories.tsx +10 -2
  277. package/src/components/workflow-status/workflow-status-icon.test.tsx +18 -0
  278. package/src/components/workflow-status/workflow-status-icon.tsx +17 -24
  279. package/src/core/entities/job-execution.ts +22 -0
  280. package/src/core/entities/job-status.test.ts +104 -0
  281. package/src/core/entities/job.ts +18 -3
  282. package/src/core/entities/step.ts +4 -0
  283. package/src/core/entities/workflow-run.ts +76 -7
  284. package/src/core/workflow-run-tabs.ts +8 -0
  285. package/src/core/workflow-run.test.ts +24 -9
  286. package/src/core/workflow-run.ts +17 -2
  287. package/src/feature.ts +5 -5
  288. package/src/hooks/api/workflow-run-mapper.ts +30 -3
  289. package/src/hooks/api/workflow-runs.test.tsx +2 -0
  290. package/src/hooks/api/workflow-runs.ts +12 -7
  291. package/src/index.ts +7 -2
  292. package/src/page-harness-budget.test.ts +1 -1
  293. package/src/pages/project-workflows-page.test.tsx +59 -5
  294. package/src/pages/project-workflows-page.tsx +34 -1
  295. package/src/pages/workflow-run-detail-page.tsx +72 -0
  296. package/src/pages/workflow-run-list-page.tsx +65 -0
  297. package/src/pages/workflow-run-pages.test.tsx +823 -0
  298. package/src/routes/inputs.test.ts +240 -12
  299. package/src/routes/inputs.ts +215 -11
  300. package/src/routes/project-route.tsx +25 -0
  301. package/src/routes/run-detail.tsx +15 -8
  302. package/src/routes/runs.tsx +14 -7
  303. package/src/routes/workflows.tsx +5 -4
  304. package/src/storybook-config.test.ts +27 -0
  305. package/test/fixtures/workflow-run.ts +76 -7
  306. package/test/pages.tsx +18 -8
  307. package/test/render.tsx +8 -2
  308. package/tsconfig.build.tsbuildinfo +1 -1
  309. package/tsconfig.test.json +7 -1
  310. package/dist/components/workflow-run-list/workflow-run-list-header.d.ts +0 -10
  311. package/dist/components/workflow-run-list/workflow-run-list-header.d.ts.map +0 -1
  312. package/dist/components/workflow-run-list/workflow-run-list-header.js +0 -55
  313. package/dist/components/workflow-run-list/workflow-run-list-header.js.map +0 -1
  314. package/dist/pages/workflow-run-first-time-use.d.ts +0 -6
  315. package/dist/pages/workflow-run-first-time-use.d.ts.map +0 -1
  316. package/dist/pages/workflow-run-first-time-use.js +0 -17
  317. package/dist/pages/workflow-run-first-time-use.js.map +0 -1
  318. package/dist/pages/workflow-run-page.d.ts +0 -10
  319. package/dist/pages/workflow-run-page.d.ts.map +0 -1
  320. package/dist/pages/workflow-run-page.js +0 -90
  321. package/dist/pages/workflow-run-page.js.map +0 -1
  322. package/src/components/workflow-run-list/workflow-run-list-header.tsx +0 -53
  323. package/src/pages/workflow-run-first-time-use.tsx +0 -17
  324. package/src/pages/workflow-run-page.test.tsx +0 -519
  325. package/src/pages/workflow-run-page.tsx +0 -94
@@ -1,519 +0,0 @@
1
- import type {
2
- WorkflowRunDetailResponseDto,
3
- WorkflowRunResponseDto,
4
- } from '@shipfox/api-workflows-dto';
5
- import {configureApiClient} from '@shipfox/client-api';
6
- import {act, screen, waitFor} from '@testing-library/react';
7
- import userEvent from '@testing-library/user-event';
8
- import {inlineLogBody, outputLine} from '#test/fixtures/logs.js';
9
- import {
10
- workflowJobDto,
11
- workflowJobExecutionDto,
12
- workflowRunDetailDto,
13
- workflowRunDto,
14
- workflowStepAttemptDto,
15
- workflowStepDto,
16
- } from '#test/fixtures/workflow-run.js';
17
- import {jsonResponse, PROJECT_TEST_WID, renderProjectPage} from '#test/pages.js';
18
- import {WorkflowRunPage} from './workflow-run-page.js';
19
-
20
- const PROJECT_ID = '44444444-4444-4444-8444-444444444444';
21
- const DEFINITION_ID = '55555555-5555-4555-8555-555555555555';
22
- const RUN_ID = '66666666-6666-4666-8666-666666666666';
23
- const SECOND_RUN_ID = '66666666-6666-4666-8666-000000000002';
24
- const BUILD_JOB_ID = '77777777-7777-4777-8777-777777777777';
25
- const BUILD_STEP_ID = '99999999-9999-4999-8999-000000000000';
26
- const BUILD_ATTEMPT_ID = 'aaaaaaaa-aaaa-4aaa-8aaa-000000000000';
27
- const DEPLOY_JOB_ID = '88888888-8888-4888-8888-888888888888';
28
- const DEPLOY_STEP_ID = '99999999-9999-4999-8999-999999999999';
29
- const DEPLOY_RETRY_STEP_ID = '99999999-9999-4999-8999-000000000003';
30
- const DEPLOY_ATTEMPT_ONE_ID = 'aaaaaaaa-aaaa-4aaa-8aaa-000000000001';
31
- const DEPLOY_ATTEMPT_TWO_ID = 'aaaaaaaa-aaaa-4aaa-8aaa-000000000002';
32
- const DEPLOY_EXECUTION_ONE_ID = 'bbbbbbbb-bbbb-4bbb-8bbb-000000000001';
33
- const DEPLOY_EXECUTION_TWO_ID = 'bbbbbbbb-bbbb-4bbb-8bbb-000000000002';
34
- const SMOKE_WEB_RE = /smoke-web/u;
35
- const EXECUTION_ONE_MENU_ITEM_PATTERN = /#1/;
36
- const RUN_DETAIL_PATH_RE = /^\/workflows\/runs\/([^/]+)$/u;
37
- const RUN_OVERRIDES = {
38
- id: RUN_ID,
39
- project_id: PROJECT_ID,
40
- definition_id: DEFINITION_ID,
41
- trigger_payload: {source: 'manual', event: 'fire'},
42
- created_at: '2026-05-07T01:01:00.000Z',
43
- updated_at: '2026-05-07T01:02:00.000Z',
44
- } satisfies Partial<WorkflowRunResponseDto>;
45
- const SECOND_RUN_OVERRIDES = {
46
- ...RUN_OVERRIDES,
47
- id: SECOND_RUN_ID,
48
- name: 'smoke-web',
49
- } satisfies Partial<WorkflowRunResponseDto>;
50
-
51
- describe('WorkflowRunPage', () => {
52
- test('keeps the runs list mounted and only skeletons the run view until a run is selected', async () => {
53
- configureApiClient({fetchImpl: vi.fn(() => new Promise<Response>(() => undefined))});
54
-
55
- renderRunsPath();
56
-
57
- // The run view has nothing to show until a run is selected, so it skeletons...
58
- expect(await screen.findByLabelText('Loading workflow run')).toBeInTheDocument();
59
- // ...but the runs list itself stays mounted; it is never replaced by a page skeleton.
60
- expect(screen.getByLabelText('Workflow runs')).toBeInTheDocument();
61
- });
62
-
63
- test('redirects to the most recent run when opened without a workflow run id', async () => {
64
- configureApiClient({fetchImpl: createRunsListFetch()});
65
-
66
- const {router} = renderRunsPath(
67
- `?job=${DEPLOY_JOB_ID}&step=${DEPLOY_STEP_ID}&stepAttempt=${DEPLOY_ATTEMPT_TWO_ID}&runAttempt=1`,
68
- );
69
-
70
- // Landing on /runs with runs present redirects to the newest run, so its row becomes the
71
- // selected (current) row in the rail even though the opened URL carried no workflow run id.
72
- const selectedRow = await screen.findByRole('link', {current: 'page'});
73
- expect(selectedRow).toHaveTextContent('deploy-web');
74
- expect(currentSearch(router).job).toBeUndefined();
75
- expect(currentSearch(router).step).toBeUndefined();
76
- expect(currentSearch(router).stepAttempt).toBeUndefined();
77
- expect(currentSearch(router).runAttempt).toBeUndefined();
78
- });
79
-
80
- test('shows the first-time-use surface when the project has no runs', async () => {
81
- configureApiClient({fetchImpl: createEmptyRunsFetch()});
82
-
83
- renderRunsPath();
84
-
85
- expect(await screen.findByText('No workflow runs yet')).toBeInTheDocument();
86
- // The rail and the perpetual detail skeleton give way to the onboarding surface entirely.
87
- expect(screen.queryByLabelText('Workflow runs')).not.toBeInTheDocument();
88
- expect(screen.queryByLabelText('Loading workflow run')).not.toBeInTheDocument();
89
- });
90
-
91
- test('restores a deep-linked job and exact attempt after data loads', async () => {
92
- configureApiClient({fetchImpl: createRunDetailFetch()});
93
-
94
- renderRunPath(
95
- `?job=${BUILD_JOB_ID}&step=${DEPLOY_STEP_ID}&stepAttempt=${DEPLOY_ATTEMPT_TWO_ID}`,
96
- );
97
-
98
- const deployJob = await screen.findByRole('button', {name: 'deploy, Running'});
99
- const deployAttempt = await screen.findByRole('button', {
100
- name: 'deploy, Running, attempt 2',
101
- });
102
-
103
- expect(deployJob).toHaveAttribute('aria-pressed', 'true');
104
- expect(deployAttempt).toHaveAttribute('aria-expanded', 'true');
105
- expect(await screen.findByText('attempt two log')).toBeInTheDocument();
106
- });
107
-
108
- test('selecting a job writes job search state and clears stale step state', async () => {
109
- const user = userEvent.setup();
110
- configureApiClient({fetchImpl: createRunDetailFetch()});
111
- const {router} = renderRunPath(
112
- `?step=${DEPLOY_STEP_ID}&stepAttempt=${DEPLOY_ATTEMPT_TWO_ID}&runAttempt=1`,
113
- );
114
-
115
- await user.click(await screen.findByRole('button', {name: 'build, Succeeded'}));
116
-
117
- await waitFor(() => {
118
- expect(currentSearch(router)).toMatchObject({job: BUILD_JOB_ID});
119
- });
120
- expect(currentSearch(router).step).toBeUndefined();
121
- expect(currentSearch(router).stepAttempt).toBeUndefined();
122
- expect(screen.getByRole('button', {name: 'checkout, Succeeded, attempt 1'})).toHaveAttribute(
123
- 'aria-expanded',
124
- 'false',
125
- );
126
- });
127
-
128
- test('selecting an attempt writes job, step, and attempt search state', async () => {
129
- const user = userEvent.setup();
130
- configureApiClient({fetchImpl: createRunDetailFetch()});
131
- const {router} = renderRunPath(`?job=${DEPLOY_JOB_ID}`);
132
-
133
- await user.click(await screen.findByRole('button', {name: 'deploy, Running, attempt 2'}));
134
-
135
- await waitFor(() => {
136
- expect(currentSearch(router)).toMatchObject({
137
- job: DEPLOY_JOB_ID,
138
- step: DEPLOY_STEP_ID,
139
- stepAttempt: DEPLOY_ATTEMPT_TWO_ID,
140
- });
141
- });
142
- });
143
-
144
- test('selecting a listening execution writes job execution search state and scopes step selection', async () => {
145
- const user = userEvent.setup();
146
- configureApiClient({
147
- fetchImpl: createRunDetailFetch({details: {[RUN_ID]: retryRunDetailDto()}}),
148
- });
149
- const {router} = renderRunPath(`?job=${DEPLOY_JOB_ID}`);
150
-
151
- await user.click(
152
- await screen.findByRole('button', {
153
- name: 'Switch job execution, currently execution 2',
154
- }),
155
- );
156
- await user.click(screen.getByRole('menuitem', {name: EXECUTION_ONE_MENU_ITEM_PATTERN}));
157
-
158
- await waitFor(() => {
159
- expect(currentSearch(router)).toMatchObject({
160
- job: DEPLOY_JOB_ID,
161
- jobExecution: DEPLOY_EXECUTION_ONE_ID,
162
- });
163
- });
164
- expect(screen.getByRole('button', {name: 'deploy, Failed, attempt 1'})).toBeInTheDocument();
165
-
166
- await user.click(screen.getByRole('button', {name: 'deploy, Failed, attempt 1'}));
167
-
168
- await waitFor(() => {
169
- expect(currentSearch(router)).toMatchObject({
170
- job: DEPLOY_JOB_ID,
171
- jobExecution: DEPLOY_EXECUTION_ONE_ID,
172
- step: DEPLOY_STEP_ID,
173
- stepAttempt: DEPLOY_ATTEMPT_ONE_ID,
174
- });
175
- });
176
- expect(await screen.findByText('attempt one log')).toBeInTheDocument();
177
- });
178
-
179
- test('collapsing an attempt removes step and attempt while preserving job', async () => {
180
- const user = userEvent.setup();
181
- configureApiClient({fetchImpl: createRunDetailFetch()});
182
- const {router} = renderRunPath(`?step=${DEPLOY_STEP_ID}&stepAttempt=${DEPLOY_ATTEMPT_TWO_ID}`);
183
-
184
- const deployAttempt = await screen.findByRole('button', {
185
- name: 'deploy, Running, attempt 2',
186
- });
187
- await user.click(deployAttempt);
188
-
189
- await waitFor(() => {
190
- expect(currentSearch(router)).toMatchObject({job: DEPLOY_JOB_ID});
191
- });
192
- expect(currentSearch(router).step).toBeUndefined();
193
- expect(currentSearch(router).stepAttempt).toBeUndefined();
194
- expect(deployAttempt).toHaveAttribute('aria-expanded', 'false');
195
- });
196
-
197
- test('back and forward navigation restores prior selections', async () => {
198
- const user = userEvent.setup();
199
- configureApiClient({fetchImpl: createRunDetailFetch()});
200
- const {router} = renderRunPath(`?job=${DEPLOY_JOB_ID}`);
201
-
202
- await user.click(await screen.findByRole('button', {name: 'deploy, Running, attempt 2'}));
203
- await waitFor(() => {
204
- expect(currentSearch(router).stepAttempt).toBe(DEPLOY_ATTEMPT_TWO_ID);
205
- });
206
-
207
- await act(() => {
208
- router.history.back();
209
- });
210
- await waitFor(() => {
211
- expect(currentSearch(router)).toMatchObject({job: DEPLOY_JOB_ID});
212
- });
213
- expect(currentSearch(router).step).toBeUndefined();
214
- expect(currentSearch(router).stepAttempt).toBeUndefined();
215
- expect(screen.getByRole('button', {name: 'deploy, Running, attempt 2'})).toHaveAttribute(
216
- 'aria-expanded',
217
- 'false',
218
- );
219
-
220
- await act(() => {
221
- router.history.forward();
222
- });
223
- await waitFor(() => {
224
- expect(currentSearch(router).stepAttempt).toBe(DEPLOY_ATTEMPT_TWO_ID);
225
- });
226
- expect(screen.getByRole('button', {name: 'deploy, Running, attempt 2'})).toHaveAttribute(
227
- 'aria-expanded',
228
- 'true',
229
- );
230
- });
231
-
232
- test('run rail links clear job, step, and attempt when switching runs', async () => {
233
- const user = userEvent.setup();
234
- configureApiClient({
235
- fetchImpl: createRunDetailFetch({
236
- runs: [workflowRunDto(RUN_OVERRIDES), workflowRunDto(SECOND_RUN_OVERRIDES)],
237
- details: {
238
- [RUN_ID]: defaultRunDetailDto(),
239
- [SECOND_RUN_ID]: workflowRunDetailDto({...SECOND_RUN_OVERRIDES, jobs: []}),
240
- },
241
- }),
242
- });
243
- const {router} = renderRunPath(`?step=${DEPLOY_STEP_ID}&stepAttempt=${DEPLOY_ATTEMPT_TWO_ID}`);
244
-
245
- await user.click(await screen.findByRole('link', {name: SMOKE_WEB_RE}));
246
-
247
- await waitFor(() => {
248
- expect(router.state.location.pathname).toContain(SECOND_RUN_ID);
249
- });
250
- expect(currentSearch(router).job).toBeUndefined();
251
- expect(currentSearch(router).step).toBeUndefined();
252
- expect(currentSearch(router).stepAttempt).toBeUndefined();
253
- expect(currentSearch(router).runAttempt).toBeUndefined();
254
- });
255
- });
256
-
257
- function renderRunsPath(search = '') {
258
- return renderProjectPage(
259
- `/workspaces/${PROJECT_TEST_WID}/projects/${PROJECT_ID}/runs${search}`,
260
- ({workflowRunId, search}) => (
261
- <WorkflowRunPage
262
- workspaceId={PROJECT_TEST_WID}
263
- projectId={PROJECT_ID}
264
- workflowRunId={workflowRunId}
265
- search={search}
266
- />
267
- ),
268
- );
269
- }
270
-
271
- function renderRunPath(search = '') {
272
- return renderProjectPage(
273
- `/workspaces/${PROJECT_TEST_WID}/projects/${PROJECT_ID}/runs/${RUN_ID}${search}`,
274
- ({workflowRunId, search}) => (
275
- <WorkflowRunPage
276
- workspaceId={PROJECT_TEST_WID}
277
- projectId={PROJECT_ID}
278
- workflowRunId={workflowRunId}
279
- search={search}
280
- />
281
- ),
282
- );
283
- }
284
-
285
- function createRunsListFetch() {
286
- return vi.fn((input: RequestInfo | URL) => {
287
- const url = new URL(requestInputUrl(input));
288
-
289
- if (url.pathname === '/workflows/runs') {
290
- return Promise.resolve(
291
- jsonResponse({
292
- runs: [workflowRunDto(RUN_OVERRIDES)],
293
- next_cursor: null,
294
- filtered_total_count: 1,
295
- }),
296
- );
297
- }
298
- if (url.pathname === `/workflows/runs/${RUN_ID}`) {
299
- return Promise.resolve(jsonResponse(workflowRunDetailDto({...RUN_OVERRIDES, jobs: []})));
300
- }
301
-
302
- return Promise.resolve(jsonResponse({code: 'not-found'}, {status: 404}));
303
- });
304
- }
305
-
306
- function createRunDetailFetch({
307
- runs = [workflowRunDto(RUN_OVERRIDES)],
308
- details = {[RUN_ID]: defaultRunDetailDto()},
309
- }: {
310
- runs?: WorkflowRunResponseDto[];
311
- details?: Record<string, WorkflowRunDetailResponseDto>;
312
- } = {}) {
313
- return vi.fn((input: RequestInfo | URL) => {
314
- const url = new URL(requestInputUrl(input));
315
-
316
- if (url.pathname === '/workflows/runs') {
317
- return Promise.resolve(
318
- jsonResponse({runs, next_cursor: null, filtered_total_count: runs.length}),
319
- );
320
- }
321
-
322
- const runMatch = url.pathname.match(RUN_DETAIL_PATH_RE);
323
- if (runMatch?.[1] && details[runMatch[1]]) {
324
- return Promise.resolve(jsonResponse(details[runMatch[1]]));
325
- }
326
-
327
- if (url.pathname === `/steps/${DEPLOY_STEP_ID}/attempts/1/logs`) {
328
- return Promise.resolve(jsonResponse(inlineLogBody(outputLine('attempt one log\n'), 1)));
329
- }
330
- if (url.pathname === `/steps/${DEPLOY_STEP_ID}/attempts/2/logs`) {
331
- return Promise.resolve(jsonResponse(inlineLogBody(outputLine('attempt two log\n'), 1)));
332
- }
333
- if (url.pathname === `/steps/${DEPLOY_RETRY_STEP_ID}/attempts/1/logs`) {
334
- return Promise.resolve(jsonResponse(inlineLogBody(outputLine('retry attempt log\n'), 1)));
335
- }
336
- if (url.pathname === `/steps/${BUILD_STEP_ID}/attempts/1/logs`) {
337
- return Promise.resolve(jsonResponse(inlineLogBody(outputLine('build log\n'), 1)));
338
- }
339
-
340
- return Promise.resolve(jsonResponse({code: 'not-found'}, {status: 404}));
341
- });
342
- }
343
-
344
- function createEmptyRunsFetch() {
345
- return vi.fn((input: RequestInfo | URL) => {
346
- const url = new URL(requestInputUrl(input));
347
-
348
- if (url.pathname === '/workflows/runs') {
349
- return Promise.resolve(jsonResponse({runs: [], next_cursor: null, filtered_total_count: 0}));
350
- }
351
-
352
- return Promise.resolve(jsonResponse({code: 'not-found'}, {status: 404}));
353
- });
354
- }
355
-
356
- function requestInputUrl(input: RequestInfo | URL) {
357
- if (input instanceof Request) return input.url;
358
- return String(input);
359
- }
360
-
361
- function defaultRunDetailDto(
362
- overrides: Partial<WorkflowRunDetailResponseDto> = {},
363
- ): WorkflowRunDetailResponseDto {
364
- return workflowRunDetailDto({
365
- ...RUN_OVERRIDES,
366
- jobs: [
367
- workflowJobDto({
368
- id: BUILD_JOB_ID,
369
- run_attempt_id: RUN_ID,
370
- name: 'build',
371
- status: 'succeeded',
372
- steps: [
373
- workflowStepDto({
374
- id: BUILD_STEP_ID,
375
- key: 'checkout',
376
- name: 'checkout',
377
- status: 'succeeded',
378
- current_attempt: 1,
379
- attempts: [
380
- workflowStepAttemptDto({
381
- id: BUILD_ATTEMPT_ID,
382
- step_id: BUILD_STEP_ID,
383
- status: 'succeeded',
384
- }),
385
- ],
386
- }),
387
- ],
388
- }),
389
- workflowJobDto({
390
- id: DEPLOY_JOB_ID,
391
- run_attempt_id: RUN_ID,
392
- name: 'deploy',
393
- status: 'running',
394
- position: 1,
395
- dependencies: ['build'],
396
- steps: [
397
- workflowStepDto({
398
- id: DEPLOY_STEP_ID,
399
- key: 'deploy',
400
- name: 'deploy',
401
- status: 'running',
402
- current_attempt: 2,
403
- attempts: [
404
- workflowStepAttemptDto({
405
- id: DEPLOY_ATTEMPT_ONE_ID,
406
- step_id: DEPLOY_STEP_ID,
407
- attempt: 1,
408
- execution_order: 1,
409
- status: 'failed',
410
- exit_code: 1,
411
- }),
412
- workflowStepAttemptDto({
413
- id: DEPLOY_ATTEMPT_TWO_ID,
414
- step_id: DEPLOY_STEP_ID,
415
- attempt: 2,
416
- execution_order: 2,
417
- status: 'running',
418
- exit_code: null,
419
- finished_at: null,
420
- }),
421
- ],
422
- }),
423
- ],
424
- }),
425
- ],
426
- ...overrides,
427
- });
428
- }
429
-
430
- function retryRunDetailDto(): WorkflowRunDetailResponseDto {
431
- return defaultRunDetailDto({
432
- jobs: [
433
- workflowJobDto({
434
- id: BUILD_JOB_ID,
435
- run_attempt_id: RUN_ID,
436
- name: 'build',
437
- status: 'succeeded',
438
- steps: [
439
- workflowStepDto({
440
- id: BUILD_STEP_ID,
441
- name: 'checkout',
442
- status: 'succeeded',
443
- attempts: [
444
- workflowStepAttemptDto({
445
- id: BUILD_ATTEMPT_ID,
446
- step_id: BUILD_STEP_ID,
447
- status: 'succeeded',
448
- }),
449
- ],
450
- }),
451
- ],
452
- }),
453
- workflowJobDto({
454
- id: DEPLOY_JOB_ID,
455
- run_attempt_id: RUN_ID,
456
- name: 'deploy',
457
- mode: 'listening',
458
- status: 'running',
459
- listener_status: 'listening',
460
- position: 1,
461
- dependencies: ['build'],
462
- job_executions: [
463
- workflowJobExecutionDto({
464
- id: DEPLOY_EXECUTION_ONE_ID,
465
- job_id: DEPLOY_JOB_ID,
466
- sequence: 1,
467
- name: 'deploy',
468
- status: 'failed',
469
- started_at: '2026-05-07T01:01:00.000Z',
470
- finished_at: '2026-05-07T01:02:00.000Z',
471
- steps: [
472
- workflowStepDto({
473
- id: DEPLOY_STEP_ID,
474
- name: 'deploy',
475
- status: 'failed',
476
- attempts: [
477
- workflowStepAttemptDto({
478
- id: DEPLOY_ATTEMPT_ONE_ID,
479
- step_id: DEPLOY_STEP_ID,
480
- status: 'failed',
481
- exit_code: 1,
482
- }),
483
- ],
484
- }),
485
- ],
486
- }),
487
- workflowJobExecutionDto({
488
- id: DEPLOY_EXECUTION_TWO_ID,
489
- job_id: DEPLOY_JOB_ID,
490
- sequence: 2,
491
- name: 'deploy',
492
- status: 'running',
493
- started_at: '2026-05-07T01:03:00.000Z',
494
- steps: [
495
- workflowStepDto({
496
- id: DEPLOY_RETRY_STEP_ID,
497
- name: 'deploy retry',
498
- status: 'running',
499
- attempts: [
500
- workflowStepAttemptDto({
501
- id: DEPLOY_ATTEMPT_TWO_ID,
502
- step_id: DEPLOY_RETRY_STEP_ID,
503
- status: 'running',
504
- exit_code: null,
505
- finished_at: null,
506
- }),
507
- ],
508
- }),
509
- ],
510
- }),
511
- ],
512
- }),
513
- ],
514
- });
515
- }
516
-
517
- function currentSearch({state}: ReturnType<typeof renderRunPath>['router']) {
518
- return state.location.search as Record<string, unknown>;
519
- }
@@ -1,94 +0,0 @@
1
- import {useNavigate} from '@tanstack/react-router';
2
- import {useCallback, useEffect} from 'react';
3
- import {WorkflowRunList} from '#components/workflow-run-list/workflow-run-list.js';
4
- import {WorkflowRunView} from '#components/workflow-run-view/index.js';
5
- import type {WorkflowRunSelectionInput} from '#core/workflow-run-url-state.js';
6
- import {useWorkflowRunsInfiniteQuery} from '#hooks/api/workflow-runs.js';
7
- import {type WorkflowRunsSearch, workflowRunSearchParams} from '#routes/inputs.js';
8
- import {WorkflowRunFirstTimeUse} from './workflow-run-first-time-use.js';
9
-
10
- interface WorkflowRunPageProps {
11
- workspaceId: string;
12
- projectId: string;
13
- workflowRunId?: string | undefined;
14
- search?: WorkflowRunsSearch;
15
- }
16
-
17
- /**
18
- * Resolve which surface the runs path should show from a single read of the runs list:
19
- * - when opened without a workflow run id, point the URL at the most recent run so the detail pane is
20
- * never empty (navigation happens in an effect since it mutates history);
21
- * - report `hasNoRuns` once the list has loaded with zero runs, so a brand-new project lands
22
- * on the first-time-use surface instead of an empty rail and a perpetual detail skeleton.
23
- */
24
- function useWorkflowRunPageTarget(
25
- workspaceId: string,
26
- projectId: string,
27
- workflowRunId: string | undefined,
28
- search: WorkflowRunsSearch,
29
- ) {
30
- const navigate = useNavigate();
31
- const {data, isPending} = useWorkflowRunsInfiniteQuery(projectId, {});
32
- const firstWorkflowRunId = data?.pages[0]?.runs[0]?.id;
33
- // Gate on data presence, not `!isError`: a transient refetch error after a prior success
34
- // keeps `data`, so the redirect (and the no-runs surface) still resolve from it instead of
35
- // stalling on the rail while active-run polling hits a blip.
36
- const isLoaded = !isPending && data !== undefined;
37
-
38
- useEffect(() => {
39
- if (workflowRunId || !isLoaded || !firstWorkflowRunId) return;
40
- navigate({
41
- to: '/workspaces/$wid/projects/$pid/runs/$workflowRunId',
42
- params: {wid: workspaceId, pid: projectId, workflowRunId: firstWorkflowRunId},
43
- search: workflowRunSearchParams(search, {}),
44
- replace: true,
45
- });
46
- }, [navigate, workspaceId, projectId, workflowRunId, isLoaded, firstWorkflowRunId, search]);
47
-
48
- return {hasNoRuns: isLoaded && firstWorkflowRunId === undefined};
49
- }
50
-
51
- export function WorkflowRunPage({
52
- workspaceId,
53
- projectId,
54
- workflowRunId,
55
- search = {},
56
- }: WorkflowRunPageProps) {
57
- const {hasNoRuns} = useWorkflowRunPageTarget(workspaceId, projectId, workflowRunId, search);
58
- const navigate = useNavigate();
59
- const selection: WorkflowRunSelectionInput = search;
60
- const onSelectionChange = useCallback(
61
- (nextSelection: WorkflowRunSelectionInput) => {
62
- navigate({
63
- search: workflowRunSearchParams(search, nextSelection) as never,
64
- });
65
- },
66
- [navigate, search],
67
- );
68
-
69
- if (!workflowRunId && hasNoRuns) {
70
- return <WorkflowRunFirstTimeUse />;
71
- }
72
-
73
- return (
74
- <div className="flex min-h-0 flex-1 overflow-hidden">
75
- <WorkflowRunList
76
- workspaceId={workspaceId}
77
- projectId={projectId}
78
- selectedWorkflowRunId={workflowRunId}
79
- search={search.search ?? ''}
80
- statusFilter={search.status ?? 'all'}
81
- onFiltersChange={(filters) =>
82
- navigate({search: workflowRunSearchParams({...search, ...filters}) as never})
83
- }
84
- />
85
- <WorkflowRunView
86
- workspaceId={workspaceId}
87
- projectId={projectId}
88
- workflowRunId={workflowRunId}
89
- selection={selection}
90
- onSelectionChange={onSelectionChange}
91
- />
92
- </div>
93
- );
94
- }