@shipfox/client-workflows 12.0.1 → 13.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 (232) hide show
  1. package/.storybook/preview.tsx +3 -3
  2. package/.turbo/turbo-build.log +1 -1
  3. package/CHANGELOG.md +65 -0
  4. package/dist/components/job-graph/job-duration-format.d.ts +2 -2
  5. package/dist/components/job-graph/job-duration-format.d.ts.map +1 -1
  6. package/dist/components/job-graph/job-duration-format.js +4 -2
  7. package/dist/components/job-graph/job-duration-format.js.map +1 -1
  8. package/dist/components/job-graph/job-node.d.ts +1 -1
  9. package/dist/components/job-graph/job-node.d.ts.map +1 -1
  10. package/dist/components/job-graph/job-node.js +5 -4
  11. package/dist/components/job-graph/job-node.js.map +1 -1
  12. package/dist/components/step-list/step-list.d.ts +2 -2
  13. package/dist/components/step-list/step-list.d.ts.map +1 -1
  14. package/dist/components/step-list/step-list.js.map +1 -1
  15. package/dist/components/workflow-run-list/job-status-strip.d.ts +21 -0
  16. package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -0
  17. package/dist/components/workflow-run-list/job-status-strip.js +185 -0
  18. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -0
  19. package/dist/components/workflow-run-list/run-display.d.ts +27 -3
  20. package/dist/components/workflow-run-list/run-display.d.ts.map +1 -1
  21. package/dist/components/workflow-run-list/run-display.js +78 -7
  22. package/dist/components/workflow-run-list/run-display.js.map +1 -1
  23. package/dist/components/workflow-run-list/types.d.ts +23 -24
  24. package/dist/components/workflow-run-list/types.d.ts.map +1 -1
  25. package/dist/components/workflow-run-list/types.js.map +1 -1
  26. package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts +22 -0
  27. package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts.map +1 -0
  28. package/dist/components/workflow-run-list/workflow-run-filter-menu.js +68 -0
  29. package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -0
  30. package/dist/components/workflow-run-list/workflow-run-filters.d.ts +18 -0
  31. package/dist/components/workflow-run-list/workflow-run-filters.d.ts.map +1 -0
  32. package/dist/components/workflow-run-list/workflow-run-filters.js +236 -0
  33. package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -0
  34. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts +8 -4
  35. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
  36. package/dist/components/workflow-run-list/workflow-run-list-content.js +40 -12
  37. package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
  38. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts +29 -2
  39. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
  40. package/dist/components/workflow-run-list/workflow-run-list-states.js +103 -44
  41. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  42. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
  43. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
  44. package/dist/components/workflow-run-list/workflow-run-list-view.js +60 -43
  45. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
  46. package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
  47. package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
  48. package/dist/components/workflow-run-list/workflow-run-list.js +20 -8
  49. package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
  50. package/dist/components/workflow-run-list/workflow-run-row.d.ts +19 -9
  51. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  52. package/dist/components/workflow-run-list/workflow-run-row.js +179 -112
  53. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  54. package/dist/components/workflow-run-number-label.d.ts +15 -0
  55. package/dist/components/workflow-run-number-label.d.ts.map +1 -0
  56. package/dist/components/workflow-run-number-label.js +41 -0
  57. package/dist/components/workflow-run-number-label.js.map +1 -0
  58. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts +3 -3
  59. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts.map +1 -1
  60. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +8 -7
  61. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
  62. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +3 -3
  63. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  64. package/dist/components/workflow-run-summary/workflow-run-summary.js +16 -7
  65. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  66. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts +2 -2
  67. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +1 -1
  68. package/dist/components/workflow-run-view/agent-config-failure-callout.js +4 -4
  69. package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +1 -1
  70. package/dist/components/workflow-run-view/job-card.d.ts +2 -2
  71. package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
  72. package/dist/components/workflow-run-view/job-card.js +51 -32
  73. package/dist/components/workflow-run-view/job-card.js.map +1 -1
  74. package/dist/components/workflow-run-view/job-execution-switcher.d.ts +1 -1
  75. package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +1 -1
  76. package/dist/components/workflow-run-view/job-execution-switcher.js +20 -8
  77. package/dist/components/workflow-run-view/job-execution-switcher.js.map +1 -1
  78. package/dist/components/workflow-run-view/workflow-run-view.d.ts +3 -2
  79. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  80. package/dist/components/workflow-run-view/workflow-run-view.js +15 -11
  81. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  82. package/dist/components/workflow-status/status-visuals.d.ts +4 -4
  83. package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
  84. package/dist/components/workflow-status/status-visuals.js +7 -0
  85. package/dist/components/workflow-status/status-visuals.js.map +1 -1
  86. package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -4
  87. package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
  88. package/dist/components/workflow-status/workflow-status-icon.js +6 -4
  89. package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
  90. package/dist/core/entities/job-execution.d.ts +10 -0
  91. package/dist/core/entities/job-execution.d.ts.map +1 -1
  92. package/dist/core/entities/job-execution.js +11 -0
  93. package/dist/core/entities/job-execution.js.map +1 -1
  94. package/dist/core/entities/job.d.ts +4 -2
  95. package/dist/core/entities/job.d.ts.map +1 -1
  96. package/dist/core/entities/job.js +8 -1
  97. package/dist/core/entities/job.js.map +1 -1
  98. package/dist/core/entities/workflow-run.d.ts +52 -4
  99. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  100. package/dist/core/entities/workflow-run.js +22 -3
  101. package/dist/core/entities/workflow-run.js.map +1 -1
  102. package/dist/core/workflow-run.d.ts +6 -6
  103. package/dist/core/workflow-run.d.ts.map +1 -1
  104. package/dist/core/workflow-run.js +3 -3
  105. package/dist/core/workflow-run.js.map +1 -1
  106. package/dist/feature.d.ts +7 -7
  107. package/dist/feature.js +5 -5
  108. package/dist/feature.js.map +1 -1
  109. package/dist/hooks/api/workflow-run-mapper.d.ts +4 -3
  110. package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
  111. package/dist/hooks/api/workflow-run-mapper.js +31 -3
  112. package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
  113. package/dist/hooks/api/workflow-runs.d.ts +4 -4
  114. package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
  115. package/dist/hooks/api/workflow-runs.js +12 -4
  116. package/dist/hooks/api/workflow-runs.js.map +1 -1
  117. package/dist/index.d.ts +4 -3
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +3 -2
  120. package/dist/index.js.map +1 -1
  121. package/dist/pages/project-workflows-page.js +46 -1
  122. package/dist/pages/project-workflows-page.js.map +1 -1
  123. package/dist/pages/workflow-run-detail-page.d.ts +11 -0
  124. package/dist/pages/workflow-run-detail-page.d.ts.map +1 -0
  125. package/dist/pages/workflow-run-detail-page.js +30 -0
  126. package/dist/pages/workflow-run-detail-page.js.map +1 -0
  127. package/dist/pages/workflow-run-list-page.d.ts +10 -0
  128. package/dist/pages/workflow-run-list-page.d.ts.map +1 -0
  129. package/dist/pages/workflow-run-list-page.js +43 -0
  130. package/dist/pages/workflow-run-list-page.js.map +1 -0
  131. package/dist/routes/inputs.d.ts +61 -5
  132. package/dist/routes/inputs.d.ts.map +1 -1
  133. package/dist/routes/inputs.js +150 -13
  134. package/dist/routes/inputs.js.map +1 -1
  135. package/dist/routes/project-route.d.ts +6 -0
  136. package/dist/routes/project-route.d.ts.map +1 -0
  137. package/dist/routes/project-route.js +25 -0
  138. package/dist/routes/project-route.js.map +1 -0
  139. package/dist/routes/run-detail.d.ts.map +1 -1
  140. package/dist/routes/run-detail.js +12 -7
  141. package/dist/routes/run-detail.js.map +1 -1
  142. package/dist/routes/runs.d.ts.map +1 -1
  143. package/dist/routes/runs.js +11 -6
  144. package/dist/routes/runs.js.map +1 -1
  145. package/dist/routes/workflows.d.ts.map +1 -1
  146. package/dist/routes/workflows.js +6 -5
  147. package/dist/routes/workflows.js.map +1 -1
  148. package/dist/tsconfig.test.tsbuildinfo +1 -1
  149. package/package.json +9 -9
  150. package/src/components/job-graph/job-duration-format.ts +11 -2
  151. package/src/components/job-graph/job-graph-view.test.tsx +19 -8
  152. package/src/components/job-graph/job-graph.stories.tsx +23 -19
  153. package/src/components/job-graph/job-node.stories.tsx +10 -5
  154. package/src/components/job-graph/job-node.test.tsx +55 -10
  155. package/src/components/job-graph/job-node.tsx +16 -9
  156. package/src/components/step-list/step-list.stories.tsx +2 -3
  157. package/src/components/step-list/step-list.tsx +3 -2
  158. package/src/components/workflow-run-list/job-status-strip.tsx +189 -0
  159. package/src/components/workflow-run-list/run-display.test.ts +169 -30
  160. package/src/components/workflow-run-list/run-display.ts +116 -9
  161. package/src/components/workflow-run-list/types.ts +21 -17
  162. package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +100 -0
  163. package/src/components/workflow-run-list/workflow-run-filters.tsx +243 -0
  164. package/src/components/workflow-run-list/workflow-run-list-content.tsx +44 -16
  165. package/src/components/workflow-run-list/workflow-run-list-states.tsx +128 -27
  166. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +441 -104
  167. package/src/components/workflow-run-list/workflow-run-list-view.tsx +73 -43
  168. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +140 -20
  169. package/src/components/workflow-run-list/workflow-run-list.tsx +20 -10
  170. package/src/components/workflow-run-list/workflow-run-row.tsx +173 -106
  171. package/src/components/workflow-run-number-label.test.tsx +45 -0
  172. package/src/components/workflow-run-number-label.tsx +47 -0
  173. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +7 -8
  174. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +13 -12
  175. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +6 -8
  176. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +27 -2
  177. package/src/components/workflow-run-summary/workflow-run-summary.tsx +19 -9
  178. package/src/components/workflow-run-view/agent-config-failure-callout.stories.tsx +2 -3
  179. package/src/components/workflow-run-view/agent-config-failure-callout.tsx +4 -4
  180. package/src/components/workflow-run-view/job-card.stories.tsx +11 -10
  181. package/src/components/workflow-run-view/job-card.test.tsx +115 -0
  182. package/src/components/workflow-run-view/job-card.tsx +74 -35
  183. package/src/components/workflow-run-view/job-execution-switcher.tsx +31 -9
  184. package/src/components/workflow-run-view/workflow-run-view.test.tsx +88 -30
  185. package/src/components/workflow-run-view/workflow-run-view.tsx +24 -14
  186. package/src/components/workflow-status/status-visuals.test.ts +9 -0
  187. package/src/components/workflow-status/status-visuals.ts +6 -4
  188. package/src/components/workflow-status/workflow-status-icon.stories.tsx +10 -2
  189. package/src/components/workflow-status/workflow-status-icon.test.tsx +18 -0
  190. package/src/components/workflow-status/workflow-status-icon.tsx +17 -24
  191. package/src/core/entities/job-execution.ts +22 -0
  192. package/src/core/entities/job-status.test.ts +104 -0
  193. package/src/core/entities/job.ts +18 -3
  194. package/src/core/entities/workflow-run.ts +76 -7
  195. package/src/core/workflow-run.test.ts +24 -9
  196. package/src/core/workflow-run.ts +17 -2
  197. package/src/feature.ts +5 -5
  198. package/src/hooks/api/workflow-run-mapper.ts +30 -3
  199. package/src/hooks/api/workflow-runs.test.tsx +2 -0
  200. package/src/hooks/api/workflow-runs.ts +12 -7
  201. package/src/index.ts +7 -2
  202. package/src/page-harness-budget.test.ts +1 -1
  203. package/src/pages/project-workflows-page.test.tsx +59 -5
  204. package/src/pages/project-workflows-page.tsx +34 -1
  205. package/src/pages/workflow-run-detail-page.tsx +45 -0
  206. package/src/pages/workflow-run-list-page.tsx +65 -0
  207. package/src/pages/{workflow-run-page.test.tsx → workflow-run-pages.test.tsx} +211 -47
  208. package/src/routes/inputs.test.ts +193 -12
  209. package/src/routes/inputs.ts +170 -11
  210. package/src/routes/project-route.tsx +25 -0
  211. package/src/routes/run-detail.tsx +15 -8
  212. package/src/routes/runs.tsx +14 -7
  213. package/src/routes/workflows.tsx +5 -4
  214. package/test/fixtures/workflow-run.ts +76 -7
  215. package/test/pages.tsx +18 -8
  216. package/test/render.tsx +8 -2
  217. package/tsconfig.build.tsbuildinfo +1 -1
  218. package/dist/components/workflow-run-list/workflow-run-list-header.d.ts +0 -10
  219. package/dist/components/workflow-run-list/workflow-run-list-header.d.ts.map +0 -1
  220. package/dist/components/workflow-run-list/workflow-run-list-header.js +0 -55
  221. package/dist/components/workflow-run-list/workflow-run-list-header.js.map +0 -1
  222. package/dist/pages/workflow-run-first-time-use.d.ts +0 -6
  223. package/dist/pages/workflow-run-first-time-use.d.ts.map +0 -1
  224. package/dist/pages/workflow-run-first-time-use.js +0 -17
  225. package/dist/pages/workflow-run-first-time-use.js.map +0 -1
  226. package/dist/pages/workflow-run-page.d.ts +0 -10
  227. package/dist/pages/workflow-run-page.d.ts.map +0 -1
  228. package/dist/pages/workflow-run-page.js +0 -90
  229. package/dist/pages/workflow-run-page.js.map +0 -1
  230. package/src/components/workflow-run-list/workflow-run-list-header.tsx +0 -53
  231. package/src/pages/workflow-run-first-time-use.tsx +0 -17
  232. package/src/pages/workflow-run-page.tsx +0 -94
@@ -95,6 +95,9 @@ function ProjectWorkflowsPageInner({ projectId }) {
95
95
  /*#__PURE__*/ _jsx(WorkflowSyncAlert, {
96
96
  sync: sync
97
97
  }),
98
+ /*#__PURE__*/ _jsx(WorkflowSyncWarnings, {
99
+ sync: sync
100
+ }),
98
101
  /*#__PURE__*/ _jsx(WorkflowDefinitionsList, {
99
102
  definitions: definitions,
100
103
  isPending: definitionsQuery.isPending,
@@ -185,7 +188,7 @@ function WorkflowDefinitionsList({ definitions, isPending, isError, sync, runErr
185
188
  return(// The workflow-name cell holds a `<button>` so the row is
186
189
  // keyboard-reachable (Tab focuses, Enter/Space activates
187
190
  // via native button semantics). The TableRow itself is no
188
- // longer clickable a row-level onClick would be invisible
191
+ // longer clickable: a row-level onClick would be invisible
189
192
  // to keyboard users and require custom keydown handling.
190
193
  // The `group` class on the row still drives the Run button
191
194
  // reveal on hover or focus-within.
@@ -382,6 +385,48 @@ function WorkflowSyncAlert({ sync }) {
382
385
  })
383
386
  });
384
387
  }
388
+ function WorkflowSyncWarnings({ sync }) {
389
+ if (sync?.status !== 'succeeded' || sync.warnings.length === 0) return null;
390
+ const seenKeys = new Map();
391
+ const warningItems = sync.warnings.map((warning)=>{
392
+ const baseKey = `${warning.code}-${warning.path ?? 'workflow'}-${warning.message}`;
393
+ const occurrence = seenKeys.get(baseKey) ?? 0;
394
+ seenKeys.set(baseKey, occurrence + 1);
395
+ return {
396
+ key: `${baseKey}-${occurrence}`,
397
+ warning
398
+ };
399
+ });
400
+ return /*#__PURE__*/ _jsx(Callout, {
401
+ role: "status",
402
+ type: "warning",
403
+ children: /*#__PURE__*/ _jsxs("div", {
404
+ className: "flex flex-col gap-6",
405
+ children: [
406
+ /*#__PURE__*/ _jsx(Text, {
407
+ size: "sm",
408
+ bold: true,
409
+ children: "Workflow definition warnings"
410
+ }),
411
+ /*#__PURE__*/ _jsx("ul", {
412
+ className: "flex flex-col gap-4",
413
+ children: warningItems.map(({ key, warning })=>/*#__PURE__*/ _jsxs("li", {
414
+ children: [
415
+ /*#__PURE__*/ _jsx(Text, {
416
+ size: "sm",
417
+ children: warning.message
418
+ }),
419
+ warning.path ? /*#__PURE__*/ _jsx(Code, {
420
+ className: "text-foreground-neutral-muted",
421
+ children: warning.path
422
+ }) : null
423
+ ]
424
+ }, key))
425
+ })
426
+ ]
427
+ })
428
+ });
429
+ }
385
430
  function DefinitionSheet({ definition, onOpenChange }) {
386
431
  const normalizedJson = definition ? JSON.stringify({
387
432
  workflow_document: definition.workflowDocument,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/project-workflows-page.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {\n type Definition,\n type DefinitionSyncSummary,\n SourceStrip,\n useDefinitionsInfiniteQuery,\n useProjectQuery,\n} from '@shipfox/client-projects';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {LoadErrorState} from '@shipfox/react-ui/load-error-state';\nimport {RelativeTime, RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Sheet,\n SheetBody,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from '@shipfox/react-ui/sheet';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@shipfox/react-ui/table';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {useState} from 'react';\nimport {useFireManualWorkflowMutation} from '#hooks/api/workflow-runs.js';\n\nexport function ProjectWorkflowsPage({projectId}: {projectId: string}) {\n return (\n <RelativeTimeProvider>\n <ProjectWorkflowsPageInner projectId={projectId} />\n </RelativeTimeProvider>\n );\n}\n\nfunction ProjectWorkflowsPageInner({projectId}: {projectId: string}) {\n const projectQuery = useProjectQuery(projectId);\n const definitionsQuery = useDefinitionsInfiniteQuery(projectId);\n const fireManual = useFireManualWorkflowMutation();\n const [selectedDefinition, setSelectedDefinition] = useState<Definition | null>(null);\n const [runError, setRunError] = useState<{definitionId: string; message: string} | null>(null);\n const definitions = definitionsQuery.data?.pages.flatMap((page) => page.definitions) ?? [];\n const sync = definitionsQuery.data?.pages[0]?.sync;\n\n async function handleRun(definition: Definition) {\n setRunError(null);\n if (!definition.manualTrigger) return;\n try {\n await fireManual.mutateAsync({projectId, definitionId: definition.id});\n toast.success('Run queued');\n } catch (error) {\n const message = errorMessage(error, 'Could not queue run.');\n setRunError({definitionId: definition.id, message});\n toast.error(message);\n }\n }\n\n return (\n <div className=\"flex w-full flex-col gap-24\">\n {projectQuery.isPending ? (\n <div className=\"flex flex-col gap-12\">\n <Skeleton className=\"h-28 w-1/3\" />\n <Skeleton className=\"h-18 w-1/2\" />\n </div>\n ) : null}\n\n {projectQuery.isError && projectQuery.data === undefined ? (\n projectQuery.error instanceof ApiError && projectQuery.error.status === 404 ? (\n <EmptyState\n icon=\"errorWarningLine\"\n title=\"Project not found\"\n description=\"This project doesn't exist, or you don't have access to it.\"\n />\n ) : (\n <QueryLoadError query={projectQuery} subject=\"project\" />\n )\n ) : null}\n\n {projectQuery.data ? (\n <>\n <header className=\"flex flex-col gap-4\">\n <Header variant=\"h2\">Workflows</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Synced workflow definitions for this project source.\n </Text>\n </header>\n\n <SourceStrip\n connectionId={projectQuery.data.source.connectionId}\n externalRepositoryId={projectQuery.data.source.externalRepositoryId}\n sync={sync}\n isPending={definitionsQuery.isPending}\n />\n\n <WorkflowSyncAlert sync={sync} />\n\n <WorkflowDefinitionsList\n definitions={definitions}\n isPending={definitionsQuery.isPending}\n isError={definitionsQuery.isError}\n sync={sync ?? null}\n runError={runError}\n runningDefinitionId={\n fireManual.isPending && fireManual.variables\n ? fireManual.variables.definitionId\n : null\n }\n hasNextPage={definitionsQuery.hasNextPage}\n isFetchingNextPage={definitionsQuery.isFetchingNextPage}\n isFetchNextPageError={definitionsQuery.isFetchNextPageError}\n onRetry={() => definitionsQuery.refetch()}\n onLoadMore={() => definitionsQuery.fetchNextPage()}\n onOpenDefinition={setSelectedDefinition}\n onRun={(definition) => {\n void handleRun(definition);\n }}\n />\n </>\n ) : null}\n\n <DefinitionSheet\n definition={selectedDefinition}\n onOpenChange={(open) => {\n if (!open) setSelectedDefinition(null);\n }}\n />\n </div>\n );\n}\n\nfunction WorkflowDefinitionsList({\n definitions,\n isPending,\n isError,\n sync,\n runError,\n runningDefinitionId,\n hasNextPage,\n isFetchingNextPage,\n isFetchNextPageError,\n onRetry,\n onLoadMore,\n onOpenDefinition,\n onRun,\n}: {\n definitions: Definition[];\n isPending: boolean;\n isError: boolean;\n sync: DefinitionSyncSummary | null;\n runError: {definitionId: string; message: string} | null;\n runningDefinitionId: string | null;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n isFetchNextPageError: boolean;\n onRetry: () => void;\n onLoadMore: () => void;\n onOpenDefinition: (definition: Definition) => void;\n onRun: (definition: Definition) => void;\n}) {\n if (isPending) {\n return (\n <div className=\"flex flex-col gap-8\">\n <Skeleton className=\"h-40 w-full\" />\n <Skeleton className=\"h-40 w-full\" />\n <Skeleton className=\"h-40 w-full\" />\n </div>\n );\n }\n\n if (isError && definitions.length === 0) {\n return (\n <LoadErrorState\n title=\"Couldn't load workflows\"\n description=\"Definitions could not be loaded. Source metadata remains visible.\"\n onRetry={onRetry}\n retryLabel=\"Retry loading workflows\"\n />\n );\n }\n\n if (definitions.length === 0) {\n return <WorkflowEmptyState sync={sync} />;\n }\n\n return (\n <>\n <div className=\"hidden rounded-8 border border-border-neutral-base md:block\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-40\"></TableHead>\n <TableHead>Workflow</TableHead>\n <TableHead className=\"w-180\">Updated</TableHead>\n <TableHead className=\"w-80 text-right\"></TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {definitions.map((definition) => {\n const runErrorMessage =\n runError?.definitionId === definition.id ? runError.message : null;\n const isRunning = runningDefinitionId === definition.id;\n\n return (\n // The workflow-name cell holds a `<button>` so the row is\n // keyboard-reachable (Tab focuses, Enter/Space activates\n // via native button semantics). The TableRow itself is no\n // longer clickable — a row-level onClick would be invisible\n // to keyboard users and require custom keydown handling.\n // The `group` class on the row still drives the Run button\n // reveal on hover or focus-within.\n <TableRow key={definition.id} className=\"group\">\n <TableCell>\n <Icon\n name={sourceIcon(definition.source)}\n className=\"size-16 text-foreground-neutral-muted\"\n aria-hidden=\"true\"\n />\n </TableCell>\n <TableCell className=\"max-w-260\">\n <div className=\"flex min-w-0 flex-col gap-2\">\n <button\n type=\"button\"\n onClick={() => onOpenDefinition(definition)}\n className=\"flex min-w-0 flex-col gap-2 text-left outline-none focus-visible:shadow-border-interactive-with-active rounded-4\"\n >\n <Text size=\"sm\" bold className=\"truncate\">\n {definition.name}\n </Text>\n <Code className=\"truncate text-foreground-neutral-muted\">\n {definition.configPath ?? 'Manual definition'}\n </Code>\n </button>\n {runErrorMessage ? (\n <Text size=\"xs\" className=\"text-tag-error-text\">\n {runErrorMessage}\n </Text>\n ) : null}\n </div>\n </TableCell>\n <TableCell className=\"text-foreground-neutral-muted\">\n <RelativeTime value={definition.updatedAt} />\n </TableCell>\n <TableCell>\n {definition.manualTrigger ? (\n <div className=\"flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100\">\n <Button size=\"xs\" isLoading={isRunning} onClick={() => onRun(definition)}>\n Run\n </Button>\n </div>\n ) : null}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n\n <div className=\"flex flex-col rounded-8 border border-border-neutral-base md:hidden\">\n {definitions.map((definition) => {\n const runErrorMessage =\n runError?.definitionId === definition.id ? runError.message : null;\n const isRunning = runningDefinitionId === definition.id;\n\n return (\n <div\n key={definition.id}\n className=\"flex flex-col gap-10 border-b border-border-neutral-base p-12 last:border-b-0\"\n >\n <button\n type=\"button\"\n className=\"flex min-w-0 items-start gap-10 text-left\"\n onClick={() => onOpenDefinition(definition)}\n >\n <Icon\n name={sourceIcon(definition.source)}\n className=\"size-16 shrink-0 text-foreground-neutral-muted\"\n aria-hidden=\"true\"\n />\n <div className=\"flex min-w-0 flex-col gap-4\">\n <Text size=\"sm\" bold className=\"break-words\">\n {definition.name}\n </Text>\n <Code className=\"break-words text-foreground-neutral-muted\">\n {definition.configPath ?? 'Manual definition'}\n </Code>\n </div>\n </button>\n <div className=\"flex items-center justify-between gap-8\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n Updated <RelativeTime value={definition.updatedAt} />\n </Text>\n {definition.manualTrigger ? (\n <Button size=\"sm\" isLoading={isRunning} onClick={() => onRun(definition)}>\n Run\n </Button>\n ) : null}\n </div>\n {runErrorMessage ? (\n <Text size=\"xs\" className=\"text-tag-error-text\">\n {runErrorMessage}\n </Text>\n ) : null}\n </div>\n );\n })}\n </div>\n\n {isFetchNextPageError ? (\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex items-center justify-between gap-12\">\n <Text size=\"sm\">Could not load more workflows.</Text>\n <Button size=\"sm\" variant=\"secondary\" onClick={onLoadMore}>\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n\n {hasNextPage ? (\n <div className=\"flex justify-center\">\n <Button size=\"sm\" variant=\"secondary\" isLoading={isFetchingNextPage} onClick={onLoadMore}>\n Load more\n </Button>\n </div>\n ) : null}\n </>\n );\n}\n\nfunction sourceIcon(source: 'manual' | 'vcs'): IconName {\n return source === 'vcs' ? ('gitBranchLine' as IconName) : ('terminalLine' as IconName);\n}\n\nfunction WorkflowEmptyState({sync}: {sync: DefinitionSyncSummary | null}) {\n const message =\n sync?.status === 'failed' && sync.lastErrorCode === 'no-workflow-files'\n ? 'No workflow files found under .shipfox/workflows/.'\n : sync?.status === 'failed'\n ? (sync.lastErrorMessage ?? 'Workflow definitions could not be synced.')\n : sync?.status === 'syncing'\n ? 'Workflow definitions are being discovered.'\n : sync?.status === 'succeeded'\n ? 'No workflow definitions found.'\n : 'Workflow sync has not reported yet.';\n\n return <EmptyState icon=\"flowChart\" title=\"No workflows\" description={message} />;\n}\n\nfunction WorkflowSyncAlert({sync}: {sync: DefinitionSyncSummary | null | undefined}) {\n if (sync?.status !== 'failed') return null;\n\n return (\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex flex-col gap-4\">\n <Text size=\"sm\" bold>\n Workflow sync failed\n </Text>\n <Text size=\"sm\">\n {sync.lastErrorMessage ?? 'The latest workflow sync failed before definitions updated.'}\n </Text>\n </div>\n </Callout>\n );\n}\n\nfunction DefinitionSheet({\n definition,\n onOpenChange,\n}: {\n definition: Definition | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const normalizedJson = definition\n ? JSON.stringify(\n {\n workflow_document: definition.workflowDocument,\n workflow_model: definition.workflowModel,\n },\n null,\n 2,\n )\n : '';\n\n return (\n <Sheet open={Boolean(definition)} onOpenChange={onOpenChange}>\n <SheetContent className=\"w-full sm:max-w-[560px]\">\n {definition ? (\n <>\n <SheetHeader>\n <SheetTitle>{definition.name}</SheetTitle>\n <SheetDescription>\n {definition.configPath ?? 'Manual workflow definition'}\n </SheetDescription>\n </SheetHeader>\n <SheetBody className=\"gap-18\">\n <div className=\"grid w-full gap-10\">\n <Metadata label=\"Definition id\" value={definition.id} />\n <Metadata label=\"Source\" value={definition.source} />\n <Metadata label=\"Ref\" value={definition.ref ?? 'Not set'} />\n <Metadata label=\"SHA\" value={definition.sha ?? 'Not set'} />\n </div>\n <div className=\"flex w-full flex-col gap-8\">\n <Text size=\"sm\" bold>\n Normalized definition\n </Text>\n <pre className=\"max-h-[52vh] w-full overflow-auto rounded-8 border border-border-neutral-base bg-background-neutral-subtle p-12 scrollbar\">\n <Code as=\"code\" className=\"whitespace-pre text-foreground-neutral-base\">\n {normalizedJson}\n </Code>\n </pre>\n </div>\n </SheetBody>\n </>\n ) : null}\n </SheetContent>\n </Sheet>\n );\n}\n\nfunction Metadata({label, value}: {label: string; value: string}) {\n return (\n <div className=\"min-w-0 py-12 first:pt-0 last:pb-0\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {label}\n </Text>\n <Text size=\"sm\" className=\"break-words\">\n {value}\n </Text>\n </div>\n );\n}\n\nfunction errorMessage(error: unknown, fallback: string) {\n if (error instanceof ApiError && error.message) return error.message;\n if (error instanceof Error && error.message) return error.message;\n return fallback;\n}\n"],"names":["ApiError","SourceStrip","useDefinitionsInfiniteQuery","useProjectQuery","QueryLoadError","Button","Callout","EmptyState","Icon","LoadErrorState","RelativeTime","RelativeTimeProvider","Sheet","SheetBody","SheetContent","SheetDescription","SheetHeader","SheetTitle","Skeleton","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useState","useFireManualWorkflowMutation","ProjectWorkflowsPage","projectId","ProjectWorkflowsPageInner","projectQuery","definitionsQuery","fireManual","selectedDefinition","setSelectedDefinition","runError","setRunError","definitions","data","pages","flatMap","page","sync","handleRun","definition","manualTrigger","mutateAsync","definitionId","id","success","error","message","errorMessage","div","className","isPending","isError","undefined","status","icon","title","description","query","subject","header","variant","size","connectionId","source","externalRepositoryId","WorkflowSyncAlert","WorkflowDefinitionsList","runningDefinitionId","variables","hasNextPage","isFetchingNextPage","isFetchNextPageError","onRetry","refetch","onLoadMore","fetchNextPage","onOpenDefinition","onRun","DefinitionSheet","onOpenChange","open","length","retryLabel","WorkflowEmptyState","map","runErrorMessage","isRunning","name","sourceIcon","aria-hidden","button","type","onClick","bold","configPath","value","updatedAt","isLoading","role","lastErrorCode","lastErrorMessage","normalizedJson","JSON","stringify","workflow_document","workflowDocument","workflow_model","workflowModel","Boolean","Metadata","label","ref","sha","pre","as","fallback","Error"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/pages/project-workflows-page.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {\n type Definition,\n type DefinitionSyncSummary,\n SourceStrip,\n useDefinitionsInfiniteQuery,\n useProjectQuery,\n} from '@shipfox/client-projects';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {LoadErrorState} from '@shipfox/react-ui/load-error-state';\nimport {RelativeTime, RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Sheet,\n SheetBody,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from '@shipfox/react-ui/sheet';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@shipfox/react-ui/table';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {useState} from 'react';\nimport {useFireManualWorkflowMutation} from '#hooks/api/workflow-runs.js';\n\nexport function ProjectWorkflowsPage({projectId}: {projectId: string}) {\n return (\n <RelativeTimeProvider>\n <ProjectWorkflowsPageInner projectId={projectId} />\n </RelativeTimeProvider>\n );\n}\n\nfunction ProjectWorkflowsPageInner({projectId}: {projectId: string}) {\n const projectQuery = useProjectQuery(projectId);\n const definitionsQuery = useDefinitionsInfiniteQuery(projectId);\n const fireManual = useFireManualWorkflowMutation();\n const [selectedDefinition, setSelectedDefinition] = useState<Definition | null>(null);\n const [runError, setRunError] = useState<{definitionId: string; message: string} | null>(null);\n const definitions = definitionsQuery.data?.pages.flatMap((page) => page.definitions) ?? [];\n const sync = definitionsQuery.data?.pages[0]?.sync;\n\n async function handleRun(definition: Definition) {\n setRunError(null);\n if (!definition.manualTrigger) return;\n try {\n await fireManual.mutateAsync({projectId, definitionId: definition.id});\n toast.success('Run queued');\n } catch (error) {\n const message = errorMessage(error, 'Could not queue run.');\n setRunError({definitionId: definition.id, message});\n toast.error(message);\n }\n }\n\n return (\n <div className=\"flex w-full flex-col gap-24\">\n {projectQuery.isPending ? (\n <div className=\"flex flex-col gap-12\">\n <Skeleton className=\"h-28 w-1/3\" />\n <Skeleton className=\"h-18 w-1/2\" />\n </div>\n ) : null}\n\n {projectQuery.isError && projectQuery.data === undefined ? (\n projectQuery.error instanceof ApiError && projectQuery.error.status === 404 ? (\n <EmptyState\n icon=\"errorWarningLine\"\n title=\"Project not found\"\n description=\"This project doesn't exist, or you don't have access to it.\"\n />\n ) : (\n <QueryLoadError query={projectQuery} subject=\"project\" />\n )\n ) : null}\n\n {projectQuery.data ? (\n <>\n <header className=\"flex flex-col gap-4\">\n <Header variant=\"h2\">Workflows</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Synced workflow definitions for this project source.\n </Text>\n </header>\n\n <SourceStrip\n connectionId={projectQuery.data.source.connectionId}\n externalRepositoryId={projectQuery.data.source.externalRepositoryId}\n sync={sync}\n isPending={definitionsQuery.isPending}\n />\n\n <WorkflowSyncAlert sync={sync} />\n <WorkflowSyncWarnings sync={sync} />\n\n <WorkflowDefinitionsList\n definitions={definitions}\n isPending={definitionsQuery.isPending}\n isError={definitionsQuery.isError}\n sync={sync ?? null}\n runError={runError}\n runningDefinitionId={\n fireManual.isPending && fireManual.variables\n ? fireManual.variables.definitionId\n : null\n }\n hasNextPage={definitionsQuery.hasNextPage}\n isFetchingNextPage={definitionsQuery.isFetchingNextPage}\n isFetchNextPageError={definitionsQuery.isFetchNextPageError}\n onRetry={() => definitionsQuery.refetch()}\n onLoadMore={() => definitionsQuery.fetchNextPage()}\n onOpenDefinition={setSelectedDefinition}\n onRun={(definition) => {\n void handleRun(definition);\n }}\n />\n </>\n ) : null}\n\n <DefinitionSheet\n definition={selectedDefinition}\n onOpenChange={(open) => {\n if (!open) setSelectedDefinition(null);\n }}\n />\n </div>\n );\n}\n\nfunction WorkflowDefinitionsList({\n definitions,\n isPending,\n isError,\n sync,\n runError,\n runningDefinitionId,\n hasNextPage,\n isFetchingNextPage,\n isFetchNextPageError,\n onRetry,\n onLoadMore,\n onOpenDefinition,\n onRun,\n}: {\n definitions: Definition[];\n isPending: boolean;\n isError: boolean;\n sync: DefinitionSyncSummary | null;\n runError: {definitionId: string; message: string} | null;\n runningDefinitionId: string | null;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n isFetchNextPageError: boolean;\n onRetry: () => void;\n onLoadMore: () => void;\n onOpenDefinition: (definition: Definition) => void;\n onRun: (definition: Definition) => void;\n}) {\n if (isPending) {\n return (\n <div className=\"flex flex-col gap-8\">\n <Skeleton className=\"h-40 w-full\" />\n <Skeleton className=\"h-40 w-full\" />\n <Skeleton className=\"h-40 w-full\" />\n </div>\n );\n }\n\n if (isError && definitions.length === 0) {\n return (\n <LoadErrorState\n title=\"Couldn't load workflows\"\n description=\"Definitions could not be loaded. Source metadata remains visible.\"\n onRetry={onRetry}\n retryLabel=\"Retry loading workflows\"\n />\n );\n }\n\n if (definitions.length === 0) {\n return <WorkflowEmptyState sync={sync} />;\n }\n\n return (\n <>\n <div className=\"hidden rounded-8 border border-border-neutral-base md:block\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-40\"></TableHead>\n <TableHead>Workflow</TableHead>\n <TableHead className=\"w-180\">Updated</TableHead>\n <TableHead className=\"w-80 text-right\"></TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {definitions.map((definition) => {\n const runErrorMessage =\n runError?.definitionId === definition.id ? runError.message : null;\n const isRunning = runningDefinitionId === definition.id;\n\n return (\n // The workflow-name cell holds a `<button>` so the row is\n // keyboard-reachable (Tab focuses, Enter/Space activates\n // via native button semantics). The TableRow itself is no\n // longer clickable: a row-level onClick would be invisible\n // to keyboard users and require custom keydown handling.\n // The `group` class on the row still drives the Run button\n // reveal on hover or focus-within.\n <TableRow key={definition.id} className=\"group\">\n <TableCell>\n <Icon\n name={sourceIcon(definition.source)}\n className=\"size-16 text-foreground-neutral-muted\"\n aria-hidden=\"true\"\n />\n </TableCell>\n <TableCell className=\"max-w-260\">\n <div className=\"flex min-w-0 flex-col gap-2\">\n <button\n type=\"button\"\n onClick={() => onOpenDefinition(definition)}\n className=\"flex min-w-0 flex-col gap-2 text-left outline-none focus-visible:shadow-border-interactive-with-active rounded-4\"\n >\n <Text size=\"sm\" bold className=\"truncate\">\n {definition.name}\n </Text>\n <Code className=\"truncate text-foreground-neutral-muted\">\n {definition.configPath ?? 'Manual definition'}\n </Code>\n </button>\n {runErrorMessage ? (\n <Text size=\"xs\" className=\"text-tag-error-text\">\n {runErrorMessage}\n </Text>\n ) : null}\n </div>\n </TableCell>\n <TableCell className=\"text-foreground-neutral-muted\">\n <RelativeTime value={definition.updatedAt} />\n </TableCell>\n <TableCell>\n {definition.manualTrigger ? (\n <div className=\"flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100\">\n <Button size=\"xs\" isLoading={isRunning} onClick={() => onRun(definition)}>\n Run\n </Button>\n </div>\n ) : null}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n\n <div className=\"flex flex-col rounded-8 border border-border-neutral-base md:hidden\">\n {definitions.map((definition) => {\n const runErrorMessage =\n runError?.definitionId === definition.id ? runError.message : null;\n const isRunning = runningDefinitionId === definition.id;\n\n return (\n <div\n key={definition.id}\n className=\"flex flex-col gap-10 border-b border-border-neutral-base p-12 last:border-b-0\"\n >\n <button\n type=\"button\"\n className=\"flex min-w-0 items-start gap-10 text-left\"\n onClick={() => onOpenDefinition(definition)}\n >\n <Icon\n name={sourceIcon(definition.source)}\n className=\"size-16 shrink-0 text-foreground-neutral-muted\"\n aria-hidden=\"true\"\n />\n <div className=\"flex min-w-0 flex-col gap-4\">\n <Text size=\"sm\" bold className=\"break-words\">\n {definition.name}\n </Text>\n <Code className=\"break-words text-foreground-neutral-muted\">\n {definition.configPath ?? 'Manual definition'}\n </Code>\n </div>\n </button>\n <div className=\"flex items-center justify-between gap-8\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n Updated <RelativeTime value={definition.updatedAt} />\n </Text>\n {definition.manualTrigger ? (\n <Button size=\"sm\" isLoading={isRunning} onClick={() => onRun(definition)}>\n Run\n </Button>\n ) : null}\n </div>\n {runErrorMessage ? (\n <Text size=\"xs\" className=\"text-tag-error-text\">\n {runErrorMessage}\n </Text>\n ) : null}\n </div>\n );\n })}\n </div>\n\n {isFetchNextPageError ? (\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex items-center justify-between gap-12\">\n <Text size=\"sm\">Could not load more workflows.</Text>\n <Button size=\"sm\" variant=\"secondary\" onClick={onLoadMore}>\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n\n {hasNextPage ? (\n <div className=\"flex justify-center\">\n <Button size=\"sm\" variant=\"secondary\" isLoading={isFetchingNextPage} onClick={onLoadMore}>\n Load more\n </Button>\n </div>\n ) : null}\n </>\n );\n}\n\nfunction sourceIcon(source: 'manual' | 'vcs'): IconName {\n return source === 'vcs' ? ('gitBranchLine' as IconName) : ('terminalLine' as IconName);\n}\n\nfunction WorkflowEmptyState({sync}: {sync: DefinitionSyncSummary | null}) {\n const message =\n sync?.status === 'failed' && sync.lastErrorCode === 'no-workflow-files'\n ? 'No workflow files found under .shipfox/workflows/.'\n : sync?.status === 'failed'\n ? (sync.lastErrorMessage ?? 'Workflow definitions could not be synced.')\n : sync?.status === 'syncing'\n ? 'Workflow definitions are being discovered.'\n : sync?.status === 'succeeded'\n ? 'No workflow definitions found.'\n : 'Workflow sync has not reported yet.';\n\n return <EmptyState icon=\"flowChart\" title=\"No workflows\" description={message} />;\n}\n\nfunction WorkflowSyncAlert({sync}: {sync: DefinitionSyncSummary | null | undefined}) {\n if (sync?.status !== 'failed') return null;\n\n return (\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex flex-col gap-4\">\n <Text size=\"sm\" bold>\n Workflow sync failed\n </Text>\n <Text size=\"sm\">\n {sync.lastErrorMessage ?? 'The latest workflow sync failed before definitions updated.'}\n </Text>\n </div>\n </Callout>\n );\n}\n\nfunction WorkflowSyncWarnings({sync}: {sync: DefinitionSyncSummary | null | undefined}) {\n if (sync?.status !== 'succeeded' || sync.warnings.length === 0) return null;\n\n const seenKeys = new Map<string, number>();\n const warningItems = sync.warnings.map((warning) => {\n const baseKey = `${warning.code}-${warning.path ?? 'workflow'}-${warning.message}`;\n const occurrence = seenKeys.get(baseKey) ?? 0;\n seenKeys.set(baseKey, occurrence + 1);\n return {key: `${baseKey}-${occurrence}`, warning};\n });\n\n return (\n <Callout role=\"status\" type=\"warning\">\n <div className=\"flex flex-col gap-6\">\n <Text size=\"sm\" bold>\n Workflow definition warnings\n </Text>\n <ul className=\"flex flex-col gap-4\">\n {warningItems.map(({key, warning}) => (\n <li key={key}>\n <Text size=\"sm\">{warning.message}</Text>\n {warning.path ? (\n <Code className=\"text-foreground-neutral-muted\">{warning.path}</Code>\n ) : null}\n </li>\n ))}\n </ul>\n </div>\n </Callout>\n );\n}\n\nfunction DefinitionSheet({\n definition,\n onOpenChange,\n}: {\n definition: Definition | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const normalizedJson = definition\n ? JSON.stringify(\n {\n workflow_document: definition.workflowDocument,\n workflow_model: definition.workflowModel,\n },\n null,\n 2,\n )\n : '';\n\n return (\n <Sheet open={Boolean(definition)} onOpenChange={onOpenChange}>\n <SheetContent className=\"w-full sm:max-w-[560px]\">\n {definition ? (\n <>\n <SheetHeader>\n <SheetTitle>{definition.name}</SheetTitle>\n <SheetDescription>\n {definition.configPath ?? 'Manual workflow definition'}\n </SheetDescription>\n </SheetHeader>\n <SheetBody className=\"gap-18\">\n <div className=\"grid w-full gap-10\">\n <Metadata label=\"Definition id\" value={definition.id} />\n <Metadata label=\"Source\" value={definition.source} />\n <Metadata label=\"Ref\" value={definition.ref ?? 'Not set'} />\n <Metadata label=\"SHA\" value={definition.sha ?? 'Not set'} />\n </div>\n <div className=\"flex w-full flex-col gap-8\">\n <Text size=\"sm\" bold>\n Normalized definition\n </Text>\n <pre className=\"max-h-[52vh] w-full overflow-auto rounded-8 border border-border-neutral-base bg-background-neutral-subtle p-12 scrollbar\">\n <Code as=\"code\" className=\"whitespace-pre text-foreground-neutral-base\">\n {normalizedJson}\n </Code>\n </pre>\n </div>\n </SheetBody>\n </>\n ) : null}\n </SheetContent>\n </Sheet>\n );\n}\n\nfunction Metadata({label, value}: {label: string; value: string}) {\n return (\n <div className=\"min-w-0 py-12 first:pt-0 last:pb-0\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {label}\n </Text>\n <Text size=\"sm\" className=\"break-words\">\n {value}\n </Text>\n </div>\n );\n}\n\nfunction errorMessage(error: unknown, fallback: string) {\n if (error instanceof ApiError && error.message) return error.message;\n if (error instanceof Error && error.message) return error.message;\n return fallback;\n}\n"],"names":["ApiError","SourceStrip","useDefinitionsInfiniteQuery","useProjectQuery","QueryLoadError","Button","Callout","EmptyState","Icon","LoadErrorState","RelativeTime","RelativeTimeProvider","Sheet","SheetBody","SheetContent","SheetDescription","SheetHeader","SheetTitle","Skeleton","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useState","useFireManualWorkflowMutation","ProjectWorkflowsPage","projectId","ProjectWorkflowsPageInner","projectQuery","definitionsQuery","fireManual","selectedDefinition","setSelectedDefinition","runError","setRunError","definitions","data","pages","flatMap","page","sync","handleRun","definition","manualTrigger","mutateAsync","definitionId","id","success","error","message","errorMessage","div","className","isPending","isError","undefined","status","icon","title","description","query","subject","header","variant","size","connectionId","source","externalRepositoryId","WorkflowSyncAlert","WorkflowSyncWarnings","WorkflowDefinitionsList","runningDefinitionId","variables","hasNextPage","isFetchingNextPage","isFetchNextPageError","onRetry","refetch","onLoadMore","fetchNextPage","onOpenDefinition","onRun","DefinitionSheet","onOpenChange","open","length","retryLabel","WorkflowEmptyState","map","runErrorMessage","isRunning","name","sourceIcon","aria-hidden","button","type","onClick","bold","configPath","value","updatedAt","isLoading","role","lastErrorCode","lastErrorMessage","warnings","seenKeys","Map","warningItems","warning","baseKey","code","path","occurrence","get","set","key","ul","li","normalizedJson","JSON","stringify","workflow_document","workflowDocument","workflow_model","workflowModel","Boolean","Metadata","label","ref","sha","pre","as","fallback","Error"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type WorkflowRunsSearch } from '#routes/inputs.js';
2
+ interface WorkflowRunDetailPageProps {
3
+ projectId: string;
4
+ workspaceSlug: string;
5
+ projectSlug: string;
6
+ workflowRunId?: string | undefined;
7
+ search?: WorkflowRunsSearch;
8
+ }
9
+ export declare function WorkflowRunDetailPage({ projectId, workspaceSlug, projectSlug, workflowRunId, search, }: WorkflowRunDetailPageProps): import("react").JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=workflow-run-detail-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-run-detail-page.d.ts","sourceRoot":"","sources":["../../src/pages/workflow-run-detail-page.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAC,KAAK,kBAAkB,EAA0B,MAAM,mBAAmB,CAAC;AAEnF,UAAU,0BAA0B;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,aAAa,EACb,WAAW,EACX,aAAa,EACb,MAAW,GACZ,EAAE,0BAA0B,+BAwB5B"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useNavigate } from '@tanstack/react-router';
3
+ import { useCallback } from 'react';
4
+ import { WorkflowRunView } from '#components/workflow-run-view/index.js';
5
+ import { workflowRunSearchParams } from '#routes/inputs.js';
6
+ export function WorkflowRunDetailPage({ projectId, workspaceSlug, projectSlug, workflowRunId, search = {} }) {
7
+ const navigate = useNavigate();
8
+ const selection = search;
9
+ const onSelectionChange = useCallback((nextSelection)=>{
10
+ navigate({
11
+ search: workflowRunSearchParams(search, nextSelection)
12
+ });
13
+ }, [
14
+ navigate,
15
+ search
16
+ ]);
17
+ return /*#__PURE__*/ _jsx("div", {
18
+ className: "flex min-h-0 flex-1 overflow-hidden bg-background-neutral-base",
19
+ children: /*#__PURE__*/ _jsx(WorkflowRunView, {
20
+ projectId: projectId,
21
+ workspaceSlug: workspaceSlug,
22
+ projectSlug: projectSlug,
23
+ workflowRunId: workflowRunId,
24
+ selection: selection,
25
+ onSelectionChange: onSelectionChange
26
+ })
27
+ });
28
+ }
29
+
30
+ //# sourceMappingURL=workflow-run-detail-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/pages/workflow-run-detail-page.tsx"],"sourcesContent":["import {useNavigate} from '@tanstack/react-router';\nimport {useCallback} from 'react';\nimport {WorkflowRunView} from '#components/workflow-run-view/index.js';\nimport type {WorkflowRunSelectionInput} from '#core/workflow-run-url-state.js';\nimport {type WorkflowRunsSearch, workflowRunSearchParams} from '#routes/inputs.js';\n\ninterface WorkflowRunDetailPageProps {\n projectId: string;\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId?: string | undefined;\n search?: WorkflowRunsSearch;\n}\n\nexport function WorkflowRunDetailPage({\n projectId,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search = {},\n}: WorkflowRunDetailPageProps) {\n const navigate = useNavigate();\n const selection: WorkflowRunSelectionInput = search;\n const onSelectionChange = useCallback(\n (nextSelection: WorkflowRunSelectionInput) => {\n navigate({\n search: workflowRunSearchParams(search, nextSelection) as never,\n });\n },\n [navigate, search],\n );\n\n return (\n <div className=\"flex min-h-0 flex-1 overflow-hidden bg-background-neutral-base\">\n <WorkflowRunView\n projectId={projectId}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n selection={selection}\n onSelectionChange={onSelectionChange}\n />\n </div>\n );\n}\n"],"names":["useNavigate","useCallback","WorkflowRunView","workflowRunSearchParams","WorkflowRunDetailPage","projectId","workspaceSlug","projectSlug","workflowRunId","search","navigate","selection","onSelectionChange","nextSelection","div","className"],"mappings":";AAAA,SAAQA,WAAW,QAAO,yBAAyB;AACnD,SAAQC,WAAW,QAAO,QAAQ;AAClC,SAAQC,eAAe,QAAO,yCAAyC;AAEvE,SAAiCC,uBAAuB,QAAO,oBAAoB;AAUnF,OAAO,SAASC,sBAAsB,EACpCC,SAAS,EACTC,aAAa,EACbC,WAAW,EACXC,aAAa,EACbC,SAAS,CAAC,CAAC,EACgB;IAC3B,MAAMC,WAAWV;IACjB,MAAMW,YAAuCF;IAC7C,MAAMG,oBAAoBX,YACxB,CAACY;QACCH,SAAS;YACPD,QAAQN,wBAAwBM,QAAQI;QAC1C;IACF,GACA;QAACH;QAAUD;KAAO;IAGpB,qBACE,KAACK;QAAIC,WAAU;kBACb,cAAA,KAACb;YACCG,WAAWA;YACXC,eAAeA;YACfC,aAAaA;YACbC,eAAeA;YACfG,WAAWA;YACXC,mBAAmBA;;;AAI3B"}
@@ -0,0 +1,10 @@
1
+ import { type WorkflowRunsSearch } from '#routes/inputs.js';
2
+ interface WorkflowRunsPageProps {
3
+ projectId: string;
4
+ workspaceSlug: string;
5
+ projectSlug: string;
6
+ search?: WorkflowRunsSearch;
7
+ }
8
+ export declare function WorkflowRunsPage({ projectId, workspaceSlug, projectSlug, search, }: WorkflowRunsPageProps): import("react").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=workflow-run-list-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-run-list-page.d.ts","sourceRoot":"","sources":["../../src/pages/workflow-run-list-page.tsx"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAE3B,UAAU,qBAAqB;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAID,wBAAgB,gBAAgB,CAAC,EAC/B,SAAS,EACT,aAAa,EACb,WAAW,EACX,MAAqB,GACtB,EAAE,qBAAqB,+BAuCvB"}
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useNavigate } from '@tanstack/react-router';
3
+ import { useCallback } from 'react';
4
+ import { WorkflowRunList } from '#components/workflow-run-list/workflow-run-list.js';
5
+ import { applyWorkflowRunFilterPatch, clearWorkflowRunFilters, workflowRunSearchParams } from '#routes/inputs.js';
6
+ const EMPTY_SEARCH = {};
7
+ export function WorkflowRunsPage({ projectId, workspaceSlug, projectSlug, search = EMPTY_SEARCH }) {
8
+ const navigate = useNavigate();
9
+ // Filter changes replace history instead of pushing it, so Back leaves the list rather than
10
+ // walking every keystroke of a search box.
11
+ const commitSearch = useCallback((next)=>{
12
+ navigate({
13
+ search: workflowRunSearchParams(next, {}),
14
+ replace: true
15
+ });
16
+ }, [
17
+ navigate
18
+ ]);
19
+ const onFiltersChange = useCallback((patch)=>commitSearch(applyWorkflowRunFilterPatch(search, patch)), [
20
+ commitSearch,
21
+ search
22
+ ]);
23
+ const onClearFilters = useCallback(()=>commitSearch(clearWorkflowRunFilters(search)), [
24
+ commitSearch,
25
+ search
26
+ ]);
27
+ return /*#__PURE__*/ _jsx("div", {
28
+ className: "flex min-h-0 flex-1 overflow-hidden bg-background-neutral-base",
29
+ children: /*#__PURE__*/ _jsx("div", {
30
+ className: "mx-auto flex min-h-0 w-full max-w-[1120px] flex-1 flex-col px-24 py-24",
31
+ children: /*#__PURE__*/ _jsx(WorkflowRunList, {
32
+ projectId: projectId,
33
+ workspaceSlug: workspaceSlug,
34
+ projectSlug: projectSlug,
35
+ search: search,
36
+ onFiltersChange: onFiltersChange,
37
+ onClearFilters: onClearFilters
38
+ })
39
+ })
40
+ });
41
+ }
42
+
43
+ //# sourceMappingURL=workflow-run-list-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/pages/workflow-run-list-page.tsx"],"sourcesContent":["import {useNavigate} from '@tanstack/react-router';\nimport {useCallback} from 'react';\nimport {WorkflowRunList} from '#components/workflow-run-list/workflow-run-list.js';\nimport {\n applyWorkflowRunFilterPatch,\n clearWorkflowRunFilters,\n type WorkflowRunFilterPatch,\n type WorkflowRunsSearch,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\n\ninterface WorkflowRunsPageProps {\n projectId: string;\n workspaceSlug: string;\n projectSlug: string;\n search?: WorkflowRunsSearch;\n}\n\nconst EMPTY_SEARCH: WorkflowRunsSearch = {};\n\nexport function WorkflowRunsPage({\n projectId,\n workspaceSlug,\n projectSlug,\n search = EMPTY_SEARCH,\n}: WorkflowRunsPageProps) {\n const navigate = useNavigate();\n\n // Filter changes replace history instead of pushing it, so Back leaves the list rather than\n // walking every keystroke of a search box.\n const commitSearch = useCallback(\n (next: WorkflowRunsSearch) => {\n navigate({\n search: workflowRunSearchParams(next, {}) as never,\n replace: true,\n });\n },\n [navigate],\n );\n\n const onFiltersChange = useCallback(\n (patch: WorkflowRunFilterPatch) => commitSearch(applyWorkflowRunFilterPatch(search, patch)),\n [commitSearch, search],\n );\n\n const onClearFilters = useCallback(\n () => commitSearch(clearWorkflowRunFilters(search)),\n [commitSearch, search],\n );\n\n return (\n <div className=\"flex min-h-0 flex-1 overflow-hidden bg-background-neutral-base\">\n <div className=\"mx-auto flex min-h-0 w-full max-w-[1120px] flex-1 flex-col px-24 py-24\">\n <WorkflowRunList\n projectId={projectId}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n search={search}\n onFiltersChange={onFiltersChange}\n onClearFilters={onClearFilters}\n />\n </div>\n </div>\n );\n}\n"],"names":["useNavigate","useCallback","WorkflowRunList","applyWorkflowRunFilterPatch","clearWorkflowRunFilters","workflowRunSearchParams","EMPTY_SEARCH","WorkflowRunsPage","projectId","workspaceSlug","projectSlug","search","navigate","commitSearch","next","replace","onFiltersChange","patch","onClearFilters","div","className"],"mappings":";AAAA,SAAQA,WAAW,QAAO,yBAAyB;AACnD,SAAQC,WAAW,QAAO,QAAQ;AAClC,SAAQC,eAAe,QAAO,qDAAqD;AACnF,SACEC,2BAA2B,EAC3BC,uBAAuB,EAGvBC,uBAAuB,QAClB,oBAAoB;AAS3B,MAAMC,eAAmC,CAAC;AAE1C,OAAO,SAASC,iBAAiB,EAC/BC,SAAS,EACTC,aAAa,EACbC,WAAW,EACXC,SAASL,YAAY,EACC;IACtB,MAAMM,WAAWZ;IAEjB,4FAA4F;IAC5F,2CAA2C;IAC3C,MAAMa,eAAeZ,YACnB,CAACa;QACCF,SAAS;YACPD,QAAQN,wBAAwBS,MAAM,CAAC;YACvCC,SAAS;QACX;IACF,GACA;QAACH;KAAS;IAGZ,MAAMI,kBAAkBf,YACtB,CAACgB,QAAkCJ,aAAaV,4BAA4BQ,QAAQM,SACpF;QAACJ;QAAcF;KAAO;IAGxB,MAAMO,iBAAiBjB,YACrB,IAAMY,aAAaT,wBAAwBO,UAC3C;QAACE;QAAcF;KAAO;IAGxB,qBACE,KAACQ;QAAIC,WAAU;kBACb,cAAA,KAACD;YAAIC,WAAU;sBACb,cAAA,KAAClB;gBACCM,WAAWA;gBACXC,eAAeA;gBACfC,aAAaA;gBACbC,QAAQA;gBACRK,iBAAiBA;gBACjBE,gBAAgBA;;;;AAK1B"}
@@ -1,22 +1,78 @@
1
- import type { WorkflowRunListStatusFilter } from '#components/workflow-run-list/types.js';
2
1
  import type { WorkflowRunSelectionInput } from '#core/workflow-run-url-state.js';
2
+ /**
3
+ * Statuses the run list can filter by. Absent means all; there is deliberately no `all`
4
+ * value, so "no filter" and "every filter" are the same URL rather than two.
5
+ */
6
+ export declare const WORKFLOW_RUN_LIST_STATUSES: readonly ["succeeded", "failed", "running", "cancelled"];
7
+ export type WorkflowRunListStatus = (typeof WORKFLOW_RUN_LIST_STATUSES)[number];
3
8
  export interface WorkflowRunsSearch extends WorkflowRunSelectionInput {
4
9
  search?: string;
5
- status?: WorkflowRunListStatusFilter;
10
+ status?: WorkflowRunListStatus[];
11
+ branch?: string[];
12
+ actor?: string[];
13
+ event?: string[];
14
+ /** Inclusive lower bound on the run's local calendar date, `YYYY-MM-DD`. */
15
+ after?: string;
16
+ /** Inclusive upper bound on the run's local calendar date, `YYYY-MM-DD`. */
17
+ before?: string;
6
18
  }
19
+ /**
20
+ * Reads the run list and run detail query string.
21
+ *
22
+ * Every value is dropped rather than rejected when it does not fit: a truncated or
23
+ * hand-edited URL should degrade to a sensible page, never to an error screen. Unknown
24
+ * parameters are ignored for the same reason.
25
+ */
7
26
  export declare function validateWorkflowRunsSearch(input: Record<string, unknown>): WorkflowRunsSearch;
27
+ /**
28
+ * Writes the query string back.
29
+ *
30
+ * Array values reach the router as arrays and the app's `stringifySearch` expands each into
31
+ * repeated keys; joining them here would defeat that and break on branch names with commas.
32
+ */
8
33
  export declare function workflowRunSearchParams(search: WorkflowRunsSearch, selection?: WorkflowRunSelectionInput): {
9
34
  runAttempt?: string;
10
35
  stepAttempt?: string;
11
36
  step?: string;
12
37
  jobExecution?: string;
13
38
  job?: string;
14
- status?: "running" | "failed";
39
+ before?: string;
40
+ after?: string;
41
+ event?: string[];
42
+ actor?: string[];
43
+ branch?: string[];
44
+ status?: ("running" | "succeeded" | "failed" | "cancelled")[];
15
45
  search?: string;
16
46
  };
47
+ /**
48
+ * A filter change. Each member is explicitly optional-or-undefined so that "present and
49
+ * undefined" (clear this filter) stays distinguishable from "absent" (leave it alone) under
50
+ * `exactOptionalPropertyTypes`.
51
+ */
52
+ export interface WorkflowRunFilterPatch {
53
+ search?: string | undefined;
54
+ status?: WorkflowRunListStatus[] | undefined;
55
+ branch?: string[] | undefined;
56
+ actor?: string[] | undefined;
57
+ event?: string[] | undefined;
58
+ after?: string | undefined;
59
+ before?: string | undefined;
60
+ }
61
+ /**
62
+ * Applies a filter change.
63
+ *
64
+ * A key present with `undefined`, an empty string, or an empty array clears that filter; a
65
+ * key left out is untouched. Cleared filters are deleted rather than stored empty, so "no
66
+ * filter" is one URL instead of several that render the same list.
67
+ */
68
+ export declare function applyWorkflowRunFilterPatch(search: WorkflowRunsSearch, patch: WorkflowRunFilterPatch): WorkflowRunsSearch;
69
+ /** Drops every filter while keeping the run-selection parameters the detail route owns. */
70
+ export declare function clearWorkflowRunFilters(search: WorkflowRunsSearch): WorkflowRunsSearch;
71
+ /** True when any list filter is active, which separates "no matches" from "no runs". */
72
+ export declare function hasWorkflowRunFilters(search: WorkflowRunsSearch): boolean;
17
73
  export declare function workflowRouteParams(input: Record<string, unknown>): {
18
- wid: string;
19
- pid: string;
74
+ workspaceSlug: string;
75
+ projectSlug: string;
20
76
  workflowRunId?: string;
21
77
  };
22
78
  //# sourceMappingURL=inputs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/routes/inputs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,wCAAwC,CAAC;AACxF,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,iCAAiC,CAAC;AAE/E,MAAM,WAAW,kBAAmB,SAAQ,yBAAyB;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,2BAA2B,CAAC;CACtC;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,CAa7F;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,SAAS,GAAE,yBAAkC;;;;;;;;EAW9C;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAMA"}
1
+ {"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/routes/inputs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,iCAAiC,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,0BAA0B,0DAA2D,CAAC;AAEnG,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhF,MAAM,WAAW,kBAAmB,SAAQ,yBAAyB;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,CAwB7F;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,SAAS,GAAE,yBAAkC;;;;;;;;;;;;;EAgB9C;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,qBAAqB,EAAE,GAAG,SAAS,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,sBAAsB,GAC5B,kBAAkB,CAUpB;AAED,2FAA2F;AAC3F,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,CAUtF;AAED,wFAAwF;AACxF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAUzE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACnE,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAOA"}
@@ -1,14 +1,51 @@
1
- export function validateWorkflowRunsSearch(input) {
1
+ /**
2
+ * Statuses the run list can filter by. Absent means all; there is deliberately no `all`
3
+ * value, so "no filter" and "every filter" are the same URL rather than two.
4
+ */ export const WORKFLOW_RUN_LIST_STATUSES = [
5
+ 'succeeded',
6
+ 'failed',
7
+ 'running',
8
+ 'cancelled'
9
+ ];
10
+ const STATUS_VALUES = new Set(WORKFLOW_RUN_LIST_STATUSES);
11
+ const CALENDAR_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
12
+ /**
13
+ * Reads the run list and run detail query string.
14
+ *
15
+ * Every value is dropped rather than rejected when it does not fit: a truncated or
16
+ * hand-edited URL should degrade to a sensible page, never to an error screen. Unknown
17
+ * parameters are ignored for the same reason.
18
+ */ export function validateWorkflowRunsSearch(input) {
2
19
  const search = string(input.search);
3
- const status = input.status === 'failed' || input.status === 'running' ? input.status : 'all';
20
+ const status = repeatable(input.status).filter(isWorkflowRunListStatus);
21
+ const branch = repeatable(input.branch);
22
+ const actor = repeatable(input.actor);
23
+ const event = repeatable(input.event);
24
+ const after = calendarDate(input.after);
25
+ const before = calendarDate(input.before);
4
26
  const runAttempt = positiveInteger(input.runAttempt);
5
27
  return {
6
28
  ...search ? {
7
29
  search
8
30
  } : {},
9
- ...status === 'all' ? {} : {
31
+ ...status.length > 0 ? {
10
32
  status
11
- },
33
+ } : {},
34
+ ...branch.length > 0 ? {
35
+ branch
36
+ } : {},
37
+ ...actor.length > 0 ? {
38
+ actor
39
+ } : {},
40
+ ...event.length > 0 ? {
41
+ event
42
+ } : {},
43
+ ...after ? {
44
+ after
45
+ } : {},
46
+ ...before ? {
47
+ before
48
+ } : {},
12
49
  ...string(input.job) ? {
13
50
  jobId: string(input.job)
14
51
  } : {},
@@ -26,14 +63,34 @@ export function validateWorkflowRunsSearch(input) {
26
63
  } : {}
27
64
  };
28
65
  }
29
- export function workflowRunSearchParams(search, selection = search) {
66
+ /**
67
+ * Writes the query string back.
68
+ *
69
+ * Array values reach the router as arrays and the app's `stringifySearch` expands each into
70
+ * repeated keys; joining them here would defeat that and break on branch names with commas.
71
+ */ export function workflowRunSearchParams(search, selection = search) {
30
72
  return {
31
73
  ...search.search ? {
32
74
  search: search.search
33
75
  } : {},
34
- ...search.status && search.status !== 'all' ? {
76
+ ...search.status?.length ? {
35
77
  status: search.status
36
78
  } : {},
79
+ ...search.branch?.length ? {
80
+ branch: search.branch
81
+ } : {},
82
+ ...search.actor?.length ? {
83
+ actor: search.actor
84
+ } : {},
85
+ ...search.event?.length ? {
86
+ event: search.event
87
+ } : {},
88
+ ...search.after ? {
89
+ after: search.after
90
+ } : {},
91
+ ...search.before ? {
92
+ before: search.before
93
+ } : {},
37
94
  ...selection.jobId ? {
38
95
  job: selection.jobId
39
96
  } : {},
@@ -51,20 +108,100 @@ export function workflowRunSearchParams(search, selection = search) {
51
108
  } : {}
52
109
  };
53
110
  }
111
+ /**
112
+ * Applies a filter change.
113
+ *
114
+ * A key present with `undefined`, an empty string, or an empty array clears that filter; a
115
+ * key left out is untouched. Cleared filters are deleted rather than stored empty, so "no
116
+ * filter" is one URL instead of several that render the same list.
117
+ */ export function applyWorkflowRunFilterPatch(search, patch) {
118
+ const next = {
119
+ ...search
120
+ };
121
+ if ('search' in patch) setOrDelete(next, 'search', patch.search || undefined);
122
+ if ('status' in patch) setOrDelete(next, 'status', nonEmptyList(patch.status));
123
+ if ('branch' in patch) setOrDelete(next, 'branch', nonEmptyList(patch.branch));
124
+ if ('actor' in patch) setOrDelete(next, 'actor', nonEmptyList(patch.actor));
125
+ if ('event' in patch) setOrDelete(next, 'event', nonEmptyList(patch.event));
126
+ if ('after' in patch) setOrDelete(next, 'after', patch.after || undefined);
127
+ if ('before' in patch) setOrDelete(next, 'before', patch.before || undefined);
128
+ return next;
129
+ }
130
+ /** Drops every filter while keeping the run-selection parameters the detail route owns. */ export function clearWorkflowRunFilters(search) {
131
+ return applyWorkflowRunFilterPatch(search, {
132
+ search: undefined,
133
+ status: undefined,
134
+ branch: undefined,
135
+ actor: undefined,
136
+ event: undefined,
137
+ after: undefined,
138
+ before: undefined
139
+ });
140
+ }
141
+ /** True when any list filter is active, which separates "no matches" from "no runs". */ export function hasWorkflowRunFilters(search) {
142
+ return Boolean(search.search || search.status?.length || search.branch?.length || search.actor?.length || search.event?.length || search.after || search.before);
143
+ }
54
144
  export function workflowRouteParams(input) {
55
- const wid = string(input.wid);
56
- const pid = string(input.pid);
57
- if (!wid || !pid) throw new Error('Workflow route is missing required path parameters.');
145
+ const workspaceSlug = string(input.workspaceSlug);
146
+ const projectSlug = string(input.projectSlug);
147
+ if (!workspaceSlug || !projectSlug) throw new Error('Workflow route is missing required path parameters.');
58
148
  const workflowRunId = string(input.workflowRunId);
59
149
  return workflowRunId ? {
60
- wid,
61
- pid,
150
+ workspaceSlug,
151
+ projectSlug,
62
152
  workflowRunId
63
153
  } : {
64
- wid,
65
- pid
154
+ workspaceSlug,
155
+ projectSlug
66
156
  };
67
157
  }
158
+ function setOrDelete(target, key, value) {
159
+ if (value === undefined) delete target[key];
160
+ else target[key] = value;
161
+ }
162
+ function nonEmptyList(value) {
163
+ return value && value.length > 0 ? [
164
+ ...value
165
+ ] : undefined;
166
+ }
167
+ function isWorkflowRunListStatus(value) {
168
+ return STATUS_VALUES.has(value);
169
+ }
170
+ /**
171
+ * Normalizes a repeatable parameter into a deduplicated list.
172
+ *
173
+ * A single occurrence parses as a scalar rather than a one-element array, and the parser
174
+ * coerces bare digits to numbers, so `?branch=2024` has to survive as the string `"2024"`.
175
+ */ function repeatable(value) {
176
+ const values = Array.isArray(value) ? value : [
177
+ value
178
+ ];
179
+ const normalized = values.flatMap((entry)=>{
180
+ if (typeof entry === 'string') return entry.length > 0 ? [
181
+ entry
182
+ ] : [];
183
+ if (typeof entry === 'number' && Number.isFinite(entry)) return [
184
+ String(entry)
185
+ ];
186
+ if (typeof entry === 'boolean') return [
187
+ String(entry)
188
+ ];
189
+ return [];
190
+ });
191
+ return [
192
+ ...new Set(normalized)
193
+ ];
194
+ }
195
+ function calendarDate(value) {
196
+ if (typeof value !== 'string' || !CALENDAR_DATE_PATTERN.test(value)) return undefined;
197
+ // Rejects a well-shaped but impossible date such as 2026-02-31, which Date rolls forward.
198
+ // Parsed as UTC: whether `2026-05-01` is a real date does not depend on the reader's zone,
199
+ // and a local-time parse would reject a day the reader's zone skipped outright.
200
+ const [year, month, day] = value.split('-').map(Number);
201
+ const parsed = new Date(`${value}T00:00:00.000Z`);
202
+ const isRealDate = !Number.isNaN(parsed.getTime()) && parsed.getUTCFullYear() === year && parsed.getUTCMonth() === month - 1 && parsed.getUTCDate() === day;
203
+ return isRealDate ? value : undefined;
204
+ }
68
205
  function string(value) {
69
206
  return typeof value === 'string' && value.length > 0 ? value : undefined;
70
207
  }