@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
@@ -31,6 +31,8 @@ export interface WorkflowRunsSearch extends WorkflowRunSelectionInput {
31
31
  severity?: WorkflowRunAnnotationSeverity;
32
32
  }
33
33
 
34
+ export type WorkflowJobSearch = Omit<WorkflowRunSelectionInput, 'jobId'>;
35
+
34
36
  const STATUS_VALUES = new Set<string>(WORKFLOW_RUN_LIST_STATUSES);
35
37
  const TAB_VALUES = new Set<string>(WORKFLOW_RUN_TABS);
36
38
  const ANNOTATION_SEVERITY_VALUES = new Set<string>(WORKFLOW_RUN_ANNOTATION_SEVERITIES);
@@ -51,7 +53,6 @@ export function validateWorkflowRunsSearch(input: Record<string, unknown>): Work
51
53
  const event = repeatable(input.event);
52
54
  const after = calendarDate(input.after);
53
55
  const before = calendarDate(input.before);
54
- const runAttempt = positiveInteger(input.runAttempt);
55
56
  const tabValue = string(input.tab);
56
57
  const tab = tabValue && TAB_VALUES.has(tabValue) ? (tabValue as WorkflowRunTab) : undefined;
57
58
  const annotation = string(input.annotation);
@@ -72,14 +73,16 @@ export function validateWorkflowRunsSearch(input: Record<string, unknown>): Work
72
73
  ...(tab ? {tab} : {}),
73
74
  ...(annotation ? {annotation} : {}),
74
75
  ...(severity ? {severity} : {}),
75
- ...(string(input.job) ? {jobId: string(input.job)} : {}),
76
- ...(string(input.jobExecution) ? {jobExecutionId: string(input.jobExecution)} : {}),
77
- ...(string(input.step) ? {stepId: string(input.step)} : {}),
78
- ...(string(input.stepAttempt) ? {stepAttemptId: string(input.stepAttempt)} : {}),
79
- ...(runAttempt ? {runAttempt} : {}),
76
+ ...workflowSelectionFromSearch(input, true),
80
77
  };
81
78
  }
82
79
 
80
+ /** Reads the job-detail query string without allowing a job id to leak back into URL state. */
81
+ export function validateWorkflowJobSearch(input: Record<string, unknown>): WorkflowJobSearch {
82
+ const {jobId: _jobId, ...selection} = workflowSelectionFromSearch(input, false);
83
+ return selection;
84
+ }
85
+
83
86
  /**
84
87
  * Writes the query string back.
85
88
  *
@@ -101,23 +104,19 @@ export function workflowRunSearchParams(
101
104
  ...(search.tab && search.tab !== 'summary' ? {tab: search.tab} : {}),
102
105
  ...(search.annotation ? {annotation: search.annotation} : {}),
103
106
  ...(search.severity ? {severity: search.severity} : {}),
104
- ...(selection.jobId ? {job: selection.jobId} : {}),
105
- ...(selection.jobExecutionId ? {jobExecution: selection.jobExecutionId} : {}),
106
- ...(selection.stepId ? {step: selection.stepId} : {}),
107
- ...(selection.stepAttemptId ? {stepAttempt: selection.stepAttemptId} : {}),
108
- ...(selection.runAttempt ? {runAttempt: String(selection.runAttempt)} : {}),
107
+ ...workflowSelectionSearchParams(selection, true),
109
108
  };
110
109
  }
111
110
 
112
- /**
113
- * Resolves the detail surface for a URL. Explicit tabs win; legacy selection-only URLs belong
114
- * to Jobs so their selected detail remains visible instead of silently landing on Summary.
115
- */
111
+ export function workflowJobSearchParams(selection: WorkflowJobSearch) {
112
+ return workflowSelectionSearchParams(selection, false);
113
+ }
114
+
115
+ /** Resolves the run-level surface. Removed Jobs-tab and selection-only URLs fall back to Summary. */
116
116
  export function workflowRunTab(
117
117
  search: Pick<WorkflowRunsSearch, 'tab' | 'jobId' | 'jobExecutionId' | 'stepId' | 'stepAttemptId'>,
118
118
  ): WorkflowRunTab {
119
- if (search.tab) return search.tab;
120
- if (search.jobId || search.jobExecutionId || search.stepId || search.stepAttemptId) return 'jobs';
119
+ if (search.tab === 'annotations' || search.tab === 'source') return search.tab;
121
120
  return 'summary';
122
121
  }
123
122
 
@@ -203,6 +202,54 @@ export function workflowRouteParams(input: Record<string, unknown>): {
203
202
  return workflowRunId ? {workspaceSlug, projectSlug, workflowRunId} : {workspaceSlug, projectSlug};
204
203
  }
205
204
 
205
+ export function workflowJobRouteParams(input: Record<string, unknown>): {
206
+ workspaceSlug: string;
207
+ projectSlug: string;
208
+ workflowRunId: string;
209
+ jobId: string;
210
+ } {
211
+ const workspaceSlug = string(input.workspaceSlug);
212
+ const projectSlug = string(input.projectSlug);
213
+ const workflowRunId = string(input.workflowRunId);
214
+ const jobId = string(input.jobId);
215
+ if (!workspaceSlug || !projectSlug || !workflowRunId || !jobId) {
216
+ throw new Error('Workflow job route is missing required path parameters.');
217
+ }
218
+ return {workspaceSlug, projectSlug, workflowRunId, jobId};
219
+ }
220
+
221
+ function workflowSelectionFromSearch(
222
+ input: Record<string, unknown>,
223
+ includeJobId: boolean,
224
+ ): WorkflowRunSelectionInput {
225
+ const jobId = includeJobId ? string(input.job) : undefined;
226
+ const jobExecutionId = string(input.jobExecution);
227
+ const stepId = string(input.step);
228
+ const stepAttemptId = string(input.stepAttempt);
229
+ const runAttempt = positiveInteger(input.runAttempt);
230
+
231
+ return {
232
+ ...(jobId ? {jobId} : {}),
233
+ ...(jobExecutionId ? {jobExecutionId} : {}),
234
+ ...(stepId ? {stepId} : {}),
235
+ ...(stepAttemptId ? {stepAttemptId} : {}),
236
+ ...(runAttempt ? {runAttempt} : {}),
237
+ };
238
+ }
239
+
240
+ function workflowSelectionSearchParams(
241
+ selection: WorkflowRunSelectionInput,
242
+ includeJobId: boolean,
243
+ ) {
244
+ return {
245
+ ...(includeJobId && selection.jobId ? {job: selection.jobId} : {}),
246
+ ...(selection.jobExecutionId ? {jobExecution: selection.jobExecutionId} : {}),
247
+ ...(selection.stepId ? {step: selection.stepId} : {}),
248
+ ...(selection.stepAttemptId ? {stepAttempt: selection.stepAttemptId} : {}),
249
+ ...(selection.runAttempt ? {runAttempt: String(selection.runAttempt)} : {}),
250
+ };
251
+ }
252
+
206
253
  function setOrDelete<TKey extends keyof WorkflowRunsSearch>(
207
254
  target: WorkflowRunsSearch,
208
255
  key: TKey,
@@ -0,0 +1,29 @@
1
+ import {defineRoute, useRouteParams, useRouteSearch} from '@shipfox/client-shell/runtime';
2
+ import {WorkflowJobDetailPage} from '#pages/workflow-job-detail-page.js';
3
+ import {validateWorkflowJobSearch, workflowJobRouteParams} from './inputs.js';
4
+ import {ProjectRoute} from './project-route.js';
5
+
6
+ export default defineRoute({
7
+ staticData: {layout: 'full-bleed'},
8
+ validateSearch: validateWorkflowJobSearch,
9
+ component: () => {
10
+ const {workspaceSlug, projectSlug, workflowRunId, jobId} =
11
+ useRouteParams(workflowJobRouteParams);
12
+ const search = useRouteSearch(validateWorkflowJobSearch);
13
+
14
+ return (
15
+ <ProjectRoute>
16
+ {(project) => (
17
+ <WorkflowJobDetailPage
18
+ projectId={project.id}
19
+ workspaceSlug={workspaceSlug}
20
+ projectSlug={projectSlug}
21
+ workflowRunId={workflowRunId}
22
+ jobId={jobId}
23
+ search={search}
24
+ />
25
+ )}
26
+ </ProjectRoute>
27
+ );
28
+ },
29
+ });
package/test/pages.tsx CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  import {type RenderResult, render} from '@testing-library/react';
21
21
  import {createStore, Provider as JotaiProvider} from 'jotai';
22
22
  import type {ReactElement} from 'react';
23
- import {validateWorkflowRunsSearch} from '#routes/inputs.js';
23
+ import {validateWorkflowJobSearch, validateWorkflowRunsSearch} from '#routes/inputs.js';
24
24
 
25
25
  // The workflow run page navigates with the router (run rows are links and the page redirects
26
26
  // to the first run), so the harness mounts the page under a memory router whose route tree
@@ -39,6 +39,10 @@ const authState: AuthState = {
39
39
  workspaces: [{id: PROJECT_TEST_WID, name: 'Acme', slug: PROJECT_TEST_WSLUG, membershipId: 'm-1'}],
40
40
  };
41
41
 
42
+ type TestPageSearch =
43
+ | ReturnType<typeof validateWorkflowRunsSearch>
44
+ | ReturnType<typeof validateWorkflowJobSearch>;
45
+
42
46
  export function jsonResponse(body: unknown, init: ResponseInit = {}) {
43
47
  return new Response(JSON.stringify(body), {
44
48
  status: 200,
@@ -51,7 +55,8 @@ function createTestRouter(
51
55
  path: string,
52
56
  renderPage: (params: {
53
57
  workflowRunId?: string | undefined;
54
- search: ReturnType<typeof validateWorkflowRunsSearch>;
58
+ jobId?: string | undefined;
59
+ search: TestPageSearch;
55
60
  }) => ReactElement,
56
61
  ) {
57
62
  const rootRoute = createRootRoute({component: Outlet});
@@ -75,6 +80,21 @@ function createTestRouter(
75
80
  });
76
81
  },
77
82
  });
83
+ const jobDetailRoute = createRoute({
84
+ getParentRoute: () => rootRoute,
85
+ path: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',
86
+ component: function JobDetailRoute() {
87
+ const {workflowRunId, jobId} = useParams({strict: false}) as {
88
+ workflowRunId?: string;
89
+ jobId?: string;
90
+ };
91
+ return renderPage({
92
+ workflowRunId,
93
+ jobId,
94
+ search: validateWorkflowJobSearch(useSearch({strict: false}) as Record<string, unknown>),
95
+ });
96
+ },
97
+ });
78
98
  const workflowsRoute = createRoute({
79
99
  getParentRoute: () => rootRoute,
80
100
  path: '/w/$workspaceSlug/p/$projectSlug/workflows',
@@ -95,6 +115,7 @@ function createTestRouter(
95
115
  routeTree: rootRoute.addChildren([
96
116
  runsRoute,
97
117
  runDetailRoute,
118
+ jobDetailRoute,
98
119
  workflowsRoute,
99
120
  modelProviderSettingsRoute,
100
121
  ]),
@@ -105,7 +126,8 @@ export function renderProjectPage(
105
126
  path: string,
106
127
  renderPage: (params: {
107
128
  workflowRunId?: string | undefined;
108
- search: ReturnType<typeof validateWorkflowRunsSearch>;
129
+ jobId?: string | undefined;
130
+ search: TestPageSearch;
109
131
  }) => ReactElement,
110
132
  ): RenderResult & {
111
133
  queryClient: QueryClient;
package/test/render.tsx CHANGED
@@ -22,6 +22,11 @@ function createComponentRouter(element: ReactElement) {
22
22
  path: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
23
23
  component: () => null,
24
24
  });
25
+ const jobDetailRoute = createRoute({
26
+ getParentRoute: () => rootRoute,
27
+ path: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',
28
+ component: () => null,
29
+ });
25
30
  // The unfiltered empty state links here.
26
31
  const workflowsRoute = createRoute({
27
32
  getParentRoute: () => rootRoute,
@@ -31,7 +36,7 @@ function createComponentRouter(element: ReactElement) {
31
36
 
32
37
  return createRouter({
33
38
  history: createMemoryHistory({initialEntries: ['/']}),
34
- routeTree: rootRoute.addChildren([indexRoute, runDetailRoute, workflowsRoute]),
39
+ routeTree: rootRoute.addChildren([indexRoute, runDetailRoute, jobDetailRoute, workflowsRoute]),
35
40
  });
36
41
  }
37
42