@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
package/src/feature.ts CHANGED
@@ -5,14 +5,14 @@ export const workflowsNavigation = [
5
5
  id: 'nav.runs',
6
6
  scope: 'project',
7
7
  label: 'Runs',
8
- to: '/workspaces/$wid/projects/$pid/runs',
8
+ to: '/w/$workspaceSlug/p/$projectSlug/runs',
9
9
  order: 100,
10
10
  },
11
11
  {
12
12
  id: 'nav.workflows',
13
13
  scope: 'project',
14
14
  label: 'Workflows',
15
- to: '/workspaces/$wid/projects/$pid/workflows',
15
+ to: '/w/$workspaceSlug/p/$projectSlug/workflows',
16
16
  order: 200,
17
17
  },
18
18
  ] as const satisfies readonly NavTabEntry[];
@@ -21,17 +21,17 @@ export const workflowsFeature = defineClientFeature({
21
21
  id: 'shipfox.workflows',
22
22
  routes: [
23
23
  {
24
- path: '/workspaces/$wid/projects/$pid/workflows',
24
+ path: '/w/$workspaceSlug/p/$projectSlug/workflows',
25
25
  parent: 'projectLayout',
26
26
  impl: '@shipfox/client-workflows/routes/workflows',
27
27
  },
28
28
  {
29
- path: '/workspaces/$wid/projects/$pid/runs',
29
+ path: '/w/$workspaceSlug/p/$projectSlug/runs',
30
30
  parent: 'projectLayout',
31
31
  impl: '@shipfox/client-workflows/routes/runs',
32
32
  },
33
33
  {
34
- path: '/workspaces/$wid/projects/$pid/runs/$workflowRunId',
34
+ path: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
35
35
  parent: 'projectLayout',
36
36
  impl: '@shipfox/client-workflows/routes/run-detail',
37
37
  },
@@ -7,6 +7,7 @@ import type {
7
7
  WorkflowRunDetailResponseDto,
8
8
  WorkflowRunJobDetailDto,
9
9
  WorkflowRunJobExecutionDetailDto,
10
+ WorkflowRunListItemDto,
10
11
  WorkflowRunListResponseDto,
11
12
  WorkflowRunResponseDto,
12
13
  WorkflowRunStepDetailDto,
@@ -25,7 +26,7 @@ import {
25
26
  type WorkflowRunDetail,
26
27
  type WorkflowRunListItem,
27
28
  type WorkflowRunListPage,
28
- workflowRunShortId,
29
+ type WorkflowRunRecord,
29
30
  workflowRunTriggerDisplayLabel,
30
31
  workflowRunTriggerLabel,
31
32
  } from '#core/workflow-run.js';
@@ -35,7 +36,9 @@ export function toWorkflowRun(dto: WorkflowRunResponseDto): WorkflowRun {
35
36
  id: dto.id,
36
37
  projectId: dto.project_id,
37
38
  definitionId: dto.definition_id,
39
+ number: dto.number,
38
40
  name: dto.name,
41
+ workflowName: dto.workflow_name,
39
42
  currentAttempt: dto.current_attempt,
40
43
  triggerProvider: dto.trigger_provider,
41
44
  triggerSource: dto.trigger_source,
@@ -49,13 +52,13 @@ export function toWorkflowRun(dto: WorkflowRunResponseDto): WorkflowRun {
49
52
  triggerEvent: dto.trigger_event,
50
53
  }),
51
54
  triggerPayload: dto.trigger_payload,
55
+ triggerReference: dto.trigger_reference,
52
56
  inputs: dto.inputs ?? null,
53
57
  sourceSnapshot: dto.source_snapshot
54
58
  ? {content: dto.source_snapshot.content, format: dto.source_snapshot.format}
55
59
  : null,
56
60
  createdAt: dto.created_at,
57
61
  updatedAt: dto.updated_at,
58
- shortId: workflowRunShortId(dto.id),
59
62
  isTemporary: dto.id.startsWith('temp-'),
60
63
  };
61
64
  }
@@ -73,7 +76,7 @@ export function toWorkflowRunAttempt(dto: WorkflowRunAttemptDto): WorkflowRunAtt
73
76
  });
74
77
  }
75
78
 
76
- export function toWorkflowRunListItem(dto: WorkflowRunResponseDto): WorkflowRunListItem {
79
+ export function toWorkflowRunRecord(dto: WorkflowRunResponseDto): WorkflowRunRecord {
77
80
  return {
78
81
  ...toWorkflowRun(dto),
79
82
  status: dto.status,
@@ -89,6 +92,26 @@ export function toWorkflowRunListItem(dto: WorkflowRunResponseDto): WorkflowRunL
89
92
  };
90
93
  }
91
94
 
95
+ export function toWorkflowRunListItem(dto: WorkflowRunListItemDto): WorkflowRunListItem {
96
+ const statusCounts = dto.job_status_counts.map(({status, count}) => ({status, count}));
97
+ return {
98
+ ...toWorkflowRunRecord(dto),
99
+ jobs: {
100
+ preview: dto.jobs.map((job) => ({
101
+ id: job.id,
102
+ key: job.key,
103
+ name: job.name,
104
+ status: job.status,
105
+ position: job.position,
106
+ })),
107
+ statusCounts,
108
+ // Derived rather than sent: the counts already cover every job, and a separate total
109
+ // would be a second source of truth that could disagree with them.
110
+ total: statusCounts.reduce((sum, entry) => sum + entry.count, 0),
111
+ },
112
+ };
113
+ }
114
+
92
115
  export function toWorkflowRunListPage(dto: WorkflowRunListResponseDto): WorkflowRunListPage {
93
116
  return {
94
117
  runs: dto.runs.map(toWorkflowRunListItem),
@@ -220,6 +243,10 @@ function toWorkflowExecutionEvent(dto: WorkflowExecutionEventDto): WorkflowExecu
220
243
  event: dto.event,
221
244
  deliveryId: dto.delivery_id,
222
245
  receivedAt: dto.received_at,
246
+ project: dto.project,
247
+ repository: dto.repository,
248
+ ref: dto.ref,
249
+ commit: dto.commit,
223
250
  data: dto.data,
224
251
  };
225
252
  }
@@ -240,6 +240,8 @@ describe('workflow run API hooks', () => {
240
240
  expect(cached?.pages[0]?.runs[0]).toMatchObject({
241
241
  projectId: PROJECT_ID,
242
242
  definitionId: DEFINITION_ID,
243
+ number: null,
244
+ workflowName: 'New run',
243
245
  status: 'pending',
244
246
  triggerSource: 'manual',
245
247
  triggerProvider: null,
@@ -29,13 +29,14 @@ import {
29
29
  type WorkflowRunDetail,
30
30
  type WorkflowRunListItem,
31
31
  type WorkflowRunListPage,
32
+ type WorkflowRunRecord,
32
33
  type WorkflowRunStatus,
33
34
  } from '#core/workflow-run.js';
34
35
  import {
35
36
  toWorkflowRunAttempt,
36
37
  toWorkflowRunDetail,
37
- toWorkflowRunListItem,
38
38
  toWorkflowRunListPage,
39
+ toWorkflowRunRecord,
39
40
  } from './workflow-run-mapper.js';
40
41
 
41
42
  export interface WorkflowRunFilters {
@@ -250,8 +251,8 @@ async function cancelWorkflowRun({
250
251
  workflowRunId,
251
252
  }: {
252
253
  workflowRunId: string;
253
- }): Promise<WorkflowRunListItem> {
254
- return toWorkflowRunListItem(
254
+ }): Promise<WorkflowRunRecord> {
255
+ return toWorkflowRunRecord(
255
256
  await checkedApiRequest(workflowRunDtoSchema, `/workflows/runs/${workflowRunId}/cancel`, {
256
257
  method: 'POST',
257
258
  }),
@@ -264,8 +265,8 @@ export async function rerunWorkflowRun({
264
265
  }: {
265
266
  workflowRunId: string;
266
267
  mode: WorkflowRunRerunModeDto;
267
- }): Promise<WorkflowRunListItem> {
268
- return toWorkflowRunListItem(
268
+ }): Promise<WorkflowRunRecord> {
269
+ return toWorkflowRunRecord(
269
270
  await checkedApiRequest(workflowRunResponseSchema, `/workflows/runs/${workflowRunId}/rerun`, {
270
271
  method: 'POST',
271
272
  body: {mode} satisfies RerunWorkflowRunBodyDto,
@@ -338,7 +339,9 @@ function buildTempRun({
338
339
  id,
339
340
  projectId,
340
341
  definitionId,
342
+ number: null,
341
343
  name,
344
+ workflowName: name,
342
345
  status: 'pending',
343
346
  currentAttempt: 1,
344
347
  latestAttempt: 1,
@@ -348,12 +351,14 @@ function buildTempRun({
348
351
  triggerPayload: {source: 'manual', event: 'fire'},
349
352
  triggerDisplayLabel: 'fire',
350
353
  triggerLabel: 'manual · fire',
354
+ triggerReference: null,
351
355
  inputs: null,
352
356
  sourceSnapshot: null,
353
357
  createdAt,
354
358
  updatedAt: createdAt,
355
- shortId: id.slice(0, 8),
356
359
  isTemporary: true,
360
+ // The optimistic row genuinely has no jobs yet: the server has not planned the graph.
361
+ jobs: {preview: [], statusCounts: [], total: 0},
357
362
  runAttempt: new WorkflowRunAttemptSummary({
358
363
  workflowRunId: id,
359
364
  attempt: 1,
@@ -568,7 +573,7 @@ export function workflowRunAttemptsQueryOptions({
568
573
  export function useCancelWorkflowRunMutation(run: WorkflowRun | undefined) {
569
574
  const queryClient = useQueryClient();
570
575
  return useMutation({
571
- mutationFn: async (): Promise<WorkflowRunListItem> => {
576
+ mutationFn: async (): Promise<WorkflowRunRecord> => {
572
577
  if (!run) throw new Error('Workflow run is not loaded');
573
578
  return await cancelWorkflowRun({workflowRunId: run.id});
574
579
  },
package/src/index.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export type {
2
2
  AgentConfigIssue,
3
3
  JobDisplayDuration,
4
+ JobDisplayStatus,
4
5
  JobExecutionDisplayDuration,
6
+ JobExecutionDisplayStatus,
5
7
  JobExecutionStatus,
6
8
  JobExecutionTime,
7
9
  JobStatus,
@@ -14,6 +16,7 @@ export type {
14
16
  StepErrorReason,
15
17
  StepGateResult,
16
18
  StepSourceLocation,
19
+ WorkflowDisplayStatus,
17
20
  WorkflowRun,
18
21
  WorkflowRunAttempt,
19
22
  WorkflowRunDetail,
@@ -23,13 +26,14 @@ export type {
23
26
  WorkflowStatus,
24
27
  } from '#core/workflow-run.js';
25
28
  export {
29
+ deriveJobDisplayStatus,
30
+ deriveJobExecutionDisplayStatus,
26
31
  isWorkflowRunTerminal,
27
32
  isWorkflowStatus,
28
33
  Job,
29
34
  JobExecution,
30
35
  TERMINAL_WORKFLOW_RUN_STATUSES,
31
36
  WORKFLOW_RUN_STATUSES,
32
- workflowRunShortId,
33
37
  workflowRunTriggerLabel,
34
38
  } from '#core/workflow-run.js';
35
39
  export {
@@ -47,4 +51,5 @@ export {
47
51
  workflowRunsQueryKeys,
48
52
  } from './hooks/api/workflow-runs.js';
49
53
  export {ProjectWorkflowsPage} from './pages/project-workflows-page.js';
50
- export {WorkflowRunPage} from './pages/workflow-run-page.js';
54
+ export {WorkflowRunDetailPage} from './pages/workflow-run-detail-page.js';
55
+ export {WorkflowRunsPage} from './pages/workflow-run-list-page.js';
@@ -8,7 +8,7 @@ const PAGE_HARNESS_ALLOWLIST = [
8
8
  'src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx',
9
9
  'src/components/workflow-run-view/workflow-run-view.test.tsx',
10
10
  'src/pages/project-workflows-page.test.tsx',
11
- 'src/pages/workflow-run-page.test.tsx',
11
+ 'src/pages/workflow-run-pages.test.tsx',
12
12
  ];
13
13
 
14
14
  const srcDir = dirname(fileURLToPath(import.meta.url));
@@ -1,6 +1,11 @@
1
1
  import {configureApiClient} from '@shipfox/client-api';
2
2
  import {fireEvent, screen, waitFor} from '@testing-library/react';
3
- import {jsonResponse, PROJECT_TEST_WID, renderProjectPage} from '#test/pages.js';
3
+ import {
4
+ jsonResponse,
5
+ PROJECT_TEST_WID,
6
+ PROJECT_TEST_WSLUG,
7
+ renderProjectPage,
8
+ } from '#test/pages.js';
4
9
  import {ProjectWorkflowsPage} from './project-workflows-page.js';
5
10
 
6
11
  const PROJECT_ID = '44444444-4444-4444-8444-444444444444';
@@ -54,6 +59,13 @@ describe('ProjectWorkflowsPage', () => {
54
59
  finished_at: null,
55
60
  last_error_code: 'no-workflow-files',
56
61
  last_error_message: 'No workflow files found',
62
+ warnings: [
63
+ {
64
+ code: 're-evaluating-command',
65
+ message: 'Workflow data is re-executed as shell code.',
66
+ path: 'jobs.build.steps.0.run',
67
+ },
68
+ ],
57
69
  },
58
70
  }),
59
71
  ),
@@ -66,6 +78,47 @@ describe('ProjectWorkflowsPage', () => {
66
78
  await screen.findByText('No workflow files found under .shipfox/workflows/.'),
67
79
  ).toBeInTheDocument();
68
80
  expect(screen.getByText('Workflow sync failed')).toBeInTheDocument();
81
+ expect(screen.queryByText('Workflow definition warnings')).not.toBeInTheDocument();
82
+ });
83
+
84
+ test('shows definition warnings without rendering a sync failure', async () => {
85
+ configureApiClient({
86
+ fetchImpl: createProjectDetailFetch({
87
+ definitions: jsonResponse(
88
+ definitionsDto({
89
+ sync: {
90
+ ref: 'main',
91
+ status: 'succeeded',
92
+ last_sync_at: '2026-05-07T01:00:00.000Z',
93
+ started_at: '2026-05-07T00:59:55.000Z',
94
+ finished_at: '2026-05-07T01:00:00.000Z',
95
+ last_error_code: null,
96
+ last_error_message: null,
97
+ warnings: [
98
+ {
99
+ code: 're-evaluating-command',
100
+ message: 'Workflow data is re-executed as shell code.',
101
+ path: 'jobs.build.steps.0.run',
102
+ },
103
+ {
104
+ code: 're-evaluating-command',
105
+ message: 'Workflow data is re-executed as shell code.',
106
+ path: 'jobs.build.steps.0.run',
107
+ },
108
+ ],
109
+ },
110
+ }),
111
+ ),
112
+ }),
113
+ });
114
+
115
+ renderWorkflowsPage();
116
+
117
+ expect(await screen.findByText('Workflow definition warnings')).toBeInTheDocument();
118
+ expect(screen.getAllByText('Workflow data is re-executed as shell code.')).toHaveLength(2);
119
+ expect(screen.getAllByRole('listitem')).toHaveLength(2);
120
+ expect(screen.getAllByText('jobs.build.steps.0.run')).toHaveLength(2);
121
+ expect(screen.queryByText('Workflow sync failed')).not.toBeInTheDocument();
69
122
  });
70
123
 
71
124
  test('opens and closes the definition drawer by clicking the row', async () => {
@@ -123,10 +176,9 @@ describe('ProjectWorkflowsPage', () => {
123
176
  });
124
177
 
125
178
  function renderWorkflowsPage() {
126
- return renderProjectPage(
127
- `/workspaces/${PROJECT_TEST_WID}/projects/${PROJECT_ID}/workflows`,
128
- () => <ProjectWorkflowsPage projectId={PROJECT_ID} />,
129
- );
179
+ return renderProjectPage(`/w/${PROJECT_TEST_WSLUG}/p/project/workflows`, () => (
180
+ <ProjectWorkflowsPage projectId={PROJECT_ID} />
181
+ ));
130
182
  }
131
183
 
132
184
  function createProjectDetailFetch({
@@ -174,6 +226,7 @@ function projectDto() {
174
226
  id: PROJECT_ID,
175
227
  workspace_id: PROJECT_TEST_WID,
176
228
  name: 'Platform',
229
+ slug: 'platform',
177
230
  source: {
178
231
  connection_id: CONNECTION_ID,
179
232
  external_repository_id: 'platform',
@@ -238,6 +291,7 @@ function baseDefinitionsDto() {
238
291
  finished_at: '2026-05-07T01:00:00.000Z',
239
292
  last_error_code: null,
240
293
  last_error_message: null,
294
+ warnings: [],
241
295
  },
242
296
  };
243
297
  }
@@ -103,6 +103,7 @@ function ProjectWorkflowsPageInner({projectId}: {projectId: string}) {
103
103
  />
104
104
 
105
105
  <WorkflowSyncAlert sync={sync} />
106
+ <WorkflowSyncWarnings sync={sync} />
106
107
 
107
108
  <WorkflowDefinitionsList
108
109
  definitions={definitions}
@@ -214,7 +215,7 @@ function WorkflowDefinitionsList({
214
215
  // The workflow-name cell holds a `<button>` so the row is
215
216
  // keyboard-reachable (Tab focuses, Enter/Space activates
216
217
  // via native button semantics). The TableRow itself is no
217
- // longer clickable a row-level onClick would be invisible
218
+ // longer clickable: a row-level onClick would be invisible
218
219
  // to keyboard users and require custom keydown handling.
219
220
  // The `group` class on the row still drives the Run button
220
221
  // reveal on hover or focus-within.
@@ -374,6 +375,38 @@ function WorkflowSyncAlert({sync}: {sync: DefinitionSyncSummary | null | undefin
374
375
  );
375
376
  }
376
377
 
378
+ function WorkflowSyncWarnings({sync}: {sync: DefinitionSyncSummary | null | undefined}) {
379
+ if (sync?.status !== 'succeeded' || sync.warnings.length === 0) return null;
380
+
381
+ const seenKeys = new Map<string, number>();
382
+ const warningItems = sync.warnings.map((warning) => {
383
+ const baseKey = `${warning.code}-${warning.path ?? 'workflow'}-${warning.message}`;
384
+ const occurrence = seenKeys.get(baseKey) ?? 0;
385
+ seenKeys.set(baseKey, occurrence + 1);
386
+ return {key: `${baseKey}-${occurrence}`, warning};
387
+ });
388
+
389
+ return (
390
+ <Callout role="status" type="warning">
391
+ <div className="flex flex-col gap-6">
392
+ <Text size="sm" bold>
393
+ Workflow definition warnings
394
+ </Text>
395
+ <ul className="flex flex-col gap-4">
396
+ {warningItems.map(({key, warning}) => (
397
+ <li key={key}>
398
+ <Text size="sm">{warning.message}</Text>
399
+ {warning.path ? (
400
+ <Code className="text-foreground-neutral-muted">{warning.path}</Code>
401
+ ) : null}
402
+ </li>
403
+ ))}
404
+ </ul>
405
+ </div>
406
+ </Callout>
407
+ );
408
+ }
409
+
377
410
  function DefinitionSheet({
378
411
  definition,
379
412
  onOpenChange,
@@ -0,0 +1,72 @@
1
+ import {useNavigate} from '@tanstack/react-router';
2
+ import {useCallback} from 'react';
3
+ import {WorkflowRunView} from '#components/workflow-run-view/index.js';
4
+ import type {WorkflowRunSelectionInput} from '#core/workflow-run-url-state.js';
5
+ import {
6
+ validateWorkflowRunsSearch,
7
+ type WorkflowRunsSearch,
8
+ type WorkflowRunTab,
9
+ workflowRunSearchParams,
10
+ workflowRunTab,
11
+ } from '#routes/inputs.js';
12
+
13
+ interface WorkflowRunDetailPageProps {
14
+ projectId: string;
15
+ workspaceSlug: string;
16
+ projectSlug: string;
17
+ workflowRunId?: string | undefined;
18
+ search?: WorkflowRunsSearch;
19
+ }
20
+
21
+ export function WorkflowRunDetailPage({
22
+ projectId,
23
+ workspaceSlug,
24
+ projectSlug,
25
+ workflowRunId,
26
+ search = {},
27
+ }: WorkflowRunDetailPageProps) {
28
+ const navigate = useNavigate();
29
+ const selection: WorkflowRunSelectionInput = search;
30
+ const onSelectionChange = useCallback(
31
+ (nextSelection: WorkflowRunSelectionInput) => {
32
+ navigate({
33
+ search: ((previous: Record<string, unknown>) => {
34
+ const current = validateWorkflowRunsSearch(previous);
35
+ return workflowRunSearchParams(current, nextSelection);
36
+ }) as never,
37
+ replace: true,
38
+ });
39
+ },
40
+ [navigate],
41
+ );
42
+ const onTabChange = useCallback(
43
+ (nextTab: WorkflowRunTab) => {
44
+ navigate({
45
+ search: ((previous: Record<string, unknown>) => {
46
+ const current = validateWorkflowRunsSearch(previous);
47
+ const nextSearch = workflowRunSearchParams({...current, tab: nextTab}, current);
48
+ return nextTab === 'summary' &&
49
+ (current.jobId || current.jobExecutionId || current.stepId || current.stepAttemptId)
50
+ ? {...nextSearch, tab: nextTab}
51
+ : nextSearch;
52
+ }) as never,
53
+ });
54
+ },
55
+ [navigate],
56
+ );
57
+
58
+ return (
59
+ <div className="flex min-h-0 flex-1 overflow-hidden bg-background-neutral-background">
60
+ <WorkflowRunView
61
+ projectId={projectId}
62
+ workspaceSlug={workspaceSlug}
63
+ projectSlug={projectSlug}
64
+ workflowRunId={workflowRunId}
65
+ selection={selection}
66
+ onSelectionChange={onSelectionChange}
67
+ tab={workflowRunTab(search)}
68
+ onTabChange={onTabChange}
69
+ />
70
+ </div>
71
+ );
72
+ }
@@ -0,0 +1,65 @@
1
+ import {useNavigate} from '@tanstack/react-router';
2
+ import {useCallback} from 'react';
3
+ import {WorkflowRunList} from '#components/workflow-run-list/workflow-run-list.js';
4
+ import {
5
+ applyWorkflowRunFilterPatch,
6
+ clearWorkflowRunFilters,
7
+ type WorkflowRunFilterPatch,
8
+ type WorkflowRunsSearch,
9
+ workflowRunSearchParams,
10
+ } from '#routes/inputs.js';
11
+
12
+ interface WorkflowRunsPageProps {
13
+ projectId: string;
14
+ workspaceSlug: string;
15
+ projectSlug: string;
16
+ search?: WorkflowRunsSearch;
17
+ }
18
+
19
+ const EMPTY_SEARCH: WorkflowRunsSearch = {};
20
+
21
+ export function WorkflowRunsPage({
22
+ projectId,
23
+ workspaceSlug,
24
+ projectSlug,
25
+ search = EMPTY_SEARCH,
26
+ }: WorkflowRunsPageProps) {
27
+ const navigate = useNavigate();
28
+
29
+ // Filter changes replace history instead of pushing it, so Back leaves the list rather than
30
+ // walking every keystroke of a search box.
31
+ const commitSearch = useCallback(
32
+ (next: WorkflowRunsSearch) => {
33
+ navigate({
34
+ search: workflowRunSearchParams(next, {}) as never,
35
+ replace: true,
36
+ });
37
+ },
38
+ [navigate],
39
+ );
40
+
41
+ const onFiltersChange = useCallback(
42
+ (patch: WorkflowRunFilterPatch) => commitSearch(applyWorkflowRunFilterPatch(search, patch)),
43
+ [commitSearch, search],
44
+ );
45
+
46
+ const onClearFilters = useCallback(
47
+ () => commitSearch(clearWorkflowRunFilters(search)),
48
+ [commitSearch, search],
49
+ );
50
+
51
+ return (
52
+ <div className="flex min-h-0 flex-1 overflow-hidden bg-background-neutral-base">
53
+ <div className="mx-auto flex min-h-0 w-full max-w-[1120px] flex-1 flex-col px-24 py-24">
54
+ <WorkflowRunList
55
+ projectId={projectId}
56
+ workspaceSlug={workspaceSlug}
57
+ projectSlug={projectSlug}
58
+ search={search}
59
+ onFiltersChange={onFiltersChange}
60
+ onClearFilters={onClearFilters}
61
+ />
62
+ </div>
63
+ </div>
64
+ );
65
+ }