@shipfox/client-workflows 13.0.0 → 14.0.1

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 (133) hide show
  1. package/.storybook/main.ts +16 -1
  2. package/.storybook/workspace-source.ts +31 -0
  3. package/.turbo/turbo-build.log +1 -1
  4. package/CHANGELOG.md +37 -0
  5. package/dist/components/job-graph/job-duration-label.js +1 -1
  6. package/dist/components/job-graph/job-duration-label.js.map +1 -1
  7. package/dist/components/job-graph/job-graph-content.d.ts +2 -1
  8. package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
  9. package/dist/components/job-graph/job-graph-content.js +3 -3
  10. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  11. package/dist/components/job-graph/job-graph-view.d.ts +2 -1
  12. package/dist/components/job-graph/job-graph-view.d.ts.map +1 -1
  13. package/dist/components/job-graph/job-graph-view.js +2 -2
  14. package/dist/components/job-graph/job-graph-view.js.map +1 -1
  15. package/dist/components/job-graph/job-node.js +5 -5
  16. package/dist/components/job-graph/job-node.js.map +1 -1
  17. package/dist/components/job-graph/types.d.ts +2 -1
  18. package/dist/components/job-graph/types.d.ts.map +1 -1
  19. package/dist/components/job-graph/types.js.map +1 -1
  20. package/dist/components/workflow-run-duration-label.js +1 -1
  21. package/dist/components/workflow-run-duration-label.js.map +1 -1
  22. package/dist/components/workflow-run-number-label.js +1 -1
  23. package/dist/components/workflow-run-number-label.js.map +1 -1
  24. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +2 -2
  25. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
  26. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +2 -7
  27. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  28. package/dist/components/workflow-run-summary/workflow-run-summary.js +92 -68
  29. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  30. package/dist/components/workflow-run-tabs/index.d.ts +6 -0
  31. package/dist/components/workflow-run-tabs/index.d.ts.map +1 -0
  32. package/dist/components/workflow-run-tabs/index.js +7 -0
  33. package/dist/components/workflow-run-tabs/index.js.map +1 -0
  34. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +11 -0
  35. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -0
  36. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +71 -0
  37. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -0
  38. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +2 -0
  39. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -0
  40. package/dist/components/workflow-run-tabs/run-annotations-empty.js +11 -0
  41. package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -0
  42. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +18 -0
  43. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +1 -0
  44. package/dist/components/workflow-run-tabs/run-jobs-list.js +107 -0
  45. package/dist/components/workflow-run-tabs/run-jobs-list.js.map +1 -0
  46. package/dist/components/workflow-run-tabs/run-tab-count.d.ts +6 -0
  47. package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +1 -0
  48. package/dist/components/workflow-run-tabs/run-tab-count.js +27 -0
  49. package/dist/components/workflow-run-tabs/run-tab-count.js.map +1 -0
  50. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +8 -0
  51. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +1 -0
  52. package/dist/components/workflow-run-tabs/run-tab-strip.js +82 -0
  53. package/dist/components/workflow-run-tabs/run-tab-strip.js.map +1 -0
  54. package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
  55. package/dist/components/workflow-run-view/workflow-run-selection.js +40 -1
  56. package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
  57. package/dist/components/workflow-run-view/workflow-run-states.d.ts +1 -0
  58. package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
  59. package/dist/components/workflow-run-view/workflow-run-states.js +10 -1
  60. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
  61. package/dist/components/workflow-run-view/workflow-run-view.d.ts +8 -4
  62. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  63. package/dist/components/workflow-run-view/workflow-run-view.js +125 -78
  64. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  65. package/dist/components/workflow-source-panel/index.d.ts +1 -0
  66. package/dist/components/workflow-source-panel/index.d.ts.map +1 -1
  67. package/dist/components/workflow-source-panel/index.js +1 -0
  68. package/dist/components/workflow-source-panel/index.js.map +1 -1
  69. package/dist/components/workflow-source-panel/workflow-source-content.d.ts +12 -0
  70. package/dist/components/workflow-source-panel/workflow-source-content.d.ts.map +1 -0
  71. package/dist/components/workflow-source-panel/workflow-source-content.js +53 -0
  72. package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -0
  73. package/dist/components/workflow-source-panel/workflow-source-panel.d.ts +1 -1
  74. package/dist/components/workflow-source-panel/workflow-source-panel.d.ts.map +1 -1
  75. package/dist/components/workflow-source-panel/workflow-source-panel.js +7 -54
  76. package/dist/components/workflow-source-panel/workflow-source-panel.js.map +1 -1
  77. package/dist/core/entities/step.d.ts +1 -1
  78. package/dist/core/entities/step.d.ts.map +1 -1
  79. package/dist/core/entities/step.js +2 -0
  80. package/dist/core/entities/step.js.map +1 -1
  81. package/dist/core/workflow-run-tabs.d.ts +9 -0
  82. package/dist/core/workflow-run-tabs.d.ts.map +1 -0
  83. package/dist/core/workflow-run-tabs.js +3 -0
  84. package/dist/core/workflow-run-tabs.js.map +1 -0
  85. package/dist/pages/workflow-run-detail-page.d.ts.map +1 -1
  86. package/dist/pages/workflow-run-detail-page.js +28 -6
  87. package/dist/pages/workflow-run-detail-page.js.map +1 -1
  88. package/dist/routes/inputs.d.ts +33 -0
  89. package/dist/routes/inputs.d.ts.map +1 -1
  90. package/dist/routes/inputs.js +49 -0
  91. package/dist/routes/inputs.js.map +1 -1
  92. package/dist/tsconfig.test.tsbuildinfo +1 -1
  93. package/package.json +9 -9
  94. package/src/components/job-graph/job-duration-label.tsx +5 -1
  95. package/src/components/job-graph/job-graph-content.tsx +7 -4
  96. package/src/components/job-graph/job-graph-view.tsx +6 -3
  97. package/src/components/job-graph/job-graph.stories.tsx +1 -1
  98. package/src/components/job-graph/job-node.stories.tsx +1 -1
  99. package/src/components/job-graph/job-node.test.tsx +27 -2
  100. package/src/components/job-graph/job-node.tsx +6 -6
  101. package/src/components/job-graph/types.ts +5 -1
  102. package/src/components/workflow-run-duration-label.tsx +1 -1
  103. package/src/components/workflow-run-number-label.tsx +1 -1
  104. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +2 -2
  105. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +23 -30
  106. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +3 -22
  107. package/src/components/workflow-run-summary/workflow-run-summary.tsx +84 -69
  108. package/src/components/workflow-run-tabs/index.ts +5 -0
  109. package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +69 -0
  110. package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +97 -0
  111. package/src/components/workflow-run-tabs/run-annotations-empty.tsx +11 -0
  112. package/src/components/workflow-run-tabs/run-jobs-list.tsx +151 -0
  113. package/src/components/workflow-run-tabs/run-tab-count.tsx +28 -0
  114. package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +52 -0
  115. package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +117 -0
  116. package/src/components/workflow-run-tabs/run-tab-strip.tsx +97 -0
  117. package/src/components/workflow-run-view/workflow-run-selection.test.ts +48 -0
  118. package/src/components/workflow-run-view/workflow-run-selection.ts +48 -1
  119. package/src/components/workflow-run-view/workflow-run-states.tsx +12 -1
  120. package/src/components/workflow-run-view/workflow-run-view.test.tsx +64 -64
  121. package/src/components/workflow-run-view/workflow-run-view.tsx +199 -85
  122. package/src/components/workflow-source-panel/index.ts +1 -0
  123. package/src/components/workflow-source-panel/workflow-source-content.tsx +84 -0
  124. package/src/components/workflow-source-panel/workflow-source-panel.tsx +14 -81
  125. package/src/core/entities/step.ts +4 -0
  126. package/src/core/workflow-run-tabs.ts +8 -0
  127. package/src/pages/workflow-run-detail-page.tsx +31 -4
  128. package/src/pages/workflow-run-pages.test.tsx +185 -45
  129. package/src/routes/inputs.test.ts +47 -0
  130. package/src/routes/inputs.ts +45 -0
  131. package/src/storybook-config.test.ts +27 -0
  132. package/tsconfig.build.tsbuildinfo +1 -1
  133. package/tsconfig.test.json +7 -1
@@ -8,17 +8,20 @@ import { RelativeTime } from '@shipfox/react-ui/relative-time';
8
8
  import { TimeTickerProvider } from '@shipfox/react-ui/time-ticker';
9
9
  import { Tooltip, TooltipContent, TooltipTrigger } from '@shipfox/react-ui/tooltip';
10
10
  import { Header, Text } from '@shipfox/react-ui/typography';
11
+ import { Link } from '@tanstack/react-router';
11
12
  import { useId } from 'react';
12
13
  import { WorkflowRunNumberLabel } from '#components/workflow-run-number-label.js';
13
14
  import { isWorkflowRunTerminal, WORKFLOW_RUN_STATUSES } from '#core/workflow-run.js';
15
+ import { validateWorkflowRunsSearch, workflowRunListSearchParams } from '#routes/inputs.js';
14
16
  import { WorkflowRunDurationLabel } from '../workflow-run-duration-label.js';
15
17
  import { getWorkflowStatusVisual } from '../workflow-status/status-visuals.js';
16
18
  import { WorkflowRunAttemptSwitcher } from './workflow-run-attempt-switcher.js';
17
19
  const STATUS_BADGE_LABEL_WIDTH_CH = Math.max(...WORKFLOW_RUN_STATUSES.map((status)=>getWorkflowStatusVisual(status).label.length));
18
- export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, sourceAvailable = false, sourceOpen = false, sourcePanelId, sourceButtonRef, onSourceToggle, cancelling = false, onCancel, rerunPending = false, onRerun, latestAttempt }) {
20
+ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling = false, onCancel, rerunPending = false, onRerun, latestAttempt }) {
19
21
  const headingId = useId();
20
22
  const status = getWorkflowStatusVisual(run.runAttempt.status);
21
23
  const action = workflowRunActionForRun(run);
24
+ const hasAction = canRenderWorkflowRunAction(action, onCancel, onRerun);
22
25
  const attemptSwitcher = latestAttempt && latestAttempt > 1 && workspaceSlug && projectSlug ? {
23
26
  workspaceSlug,
24
27
  projectSlug,
@@ -31,76 +34,92 @@ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, sourceAvai
31
34
  reducedMotionIntervalMs: 10_000,
32
35
  children: /*#__PURE__*/ _jsx("section", {
33
36
  "aria-labelledby": headingId,
34
- className: "border-b border-border-neutral-base bg-background-subtle-base px-16 py-8",
37
+ className: "bg-background-neutral-background px-16 py-12",
35
38
  children: /*#__PURE__*/ _jsxs("div", {
36
- className: "grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-12 gap-y-4 overflow-hidden",
39
+ className: "grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-12 gap-y-8 overflow-hidden max-[480px]:grid-cols-1",
37
40
  children: [
38
- /*#__PURE__*/ _jsxs("div", {
39
- className: "col-start-1 row-start-1 flex min-w-0 items-center gap-8",
40
- children: [
41
- /*#__PURE__*/ _jsx(Badge, {
42
- variant: status.badge,
43
- size: "xs",
44
- children: /*#__PURE__*/ _jsx("span", {
45
- className: "text-center",
46
- style: {
47
- width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`
48
- },
49
- children: status.label
50
- })
51
- }),
52
- /*#__PURE__*/ _jsxs(Tooltip, {
53
- children: [
54
- /*#__PURE__*/ _jsx(TooltipTrigger, {
55
- asChild: true,
56
- children: /*#__PURE__*/ _jsx(Header, {
57
- id: headingId,
58
- variant: "h3",
59
- className: "min-w-0 truncate",
41
+ /*#__PURE__*/ _jsx("nav", {
42
+ "aria-label": "Breadcrumb",
43
+ className: "col-start-1 row-start-1 min-w-0 max-[480px]:col-start-auto max-[480px]:row-start-auto",
44
+ children: /*#__PURE__*/ _jsxs("ol", {
45
+ className: "flex min-w-0 items-center gap-8",
46
+ children: [
47
+ workspaceSlug && projectSlug ? /*#__PURE__*/ _jsx("li", {
48
+ className: "shrink-0",
49
+ children: /*#__PURE__*/ _jsx(Link, {
50
+ to: "/w/$workspaceSlug/p/$projectSlug/runs",
51
+ params: {
52
+ workspaceSlug,
53
+ projectSlug
54
+ },
55
+ search: (previous)=>workflowRunListSearchParams(validateWorkflowRunsSearch(previous)),
56
+ className: "rounded-4 text-xs font-medium text-foreground-neutral-subtle underline decoration-border-neutral-strong underline-offset-4 outline-none hover:text-foreground-neutral-base focus-visible:shadow-border-interactive-with-active",
57
+ children: "Runs"
58
+ })
59
+ }) : null,
60
+ workspaceSlug && projectSlug ? /*#__PURE__*/ _jsx("li", {
61
+ "aria-hidden": "true",
62
+ className: "shrink-0 text-xs text-foreground-neutral-subtle",
63
+ children: "/"
64
+ }) : null,
65
+ /*#__PURE__*/ _jsxs("li", {
66
+ "aria-current": "page",
67
+ className: "flex min-w-0 items-center gap-8",
68
+ children: [
69
+ /*#__PURE__*/ _jsx(Badge, {
70
+ variant: status.badge,
71
+ size: "xs",
60
72
  children: /*#__PURE__*/ _jsx("span", {
61
- ref: headingTextRef,
62
- title: isHeadingTruncated ? run.name : undefined,
63
- className: "block min-w-0 truncate",
64
- children: run.name
73
+ className: "text-center",
74
+ style: {
75
+ width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`
76
+ },
77
+ children: status.label
65
78
  })
79
+ }),
80
+ /*#__PURE__*/ _jsxs(Tooltip, {
81
+ children: [
82
+ /*#__PURE__*/ _jsx(TooltipTrigger, {
83
+ asChild: true,
84
+ children: /*#__PURE__*/ _jsx(Header, {
85
+ id: headingId,
86
+ variant: "h3",
87
+ className: "min-w-0 truncate",
88
+ children: /*#__PURE__*/ _jsx("span", {
89
+ ref: headingTextRef,
90
+ title: isHeadingTruncated ? run.name : undefined,
91
+ className: "block min-w-0 truncate",
92
+ children: run.name
93
+ })
94
+ })
95
+ }),
96
+ isHeadingTruncated ? /*#__PURE__*/ _jsx(TooltipContent, {
97
+ children: /*#__PURE__*/ _jsx(Text, {
98
+ as: "span",
99
+ size: "xs",
100
+ className: "max-w-[360px] break-words",
101
+ children: run.name
102
+ })
103
+ }) : null
104
+ ]
66
105
  })
67
- }),
68
- isHeadingTruncated ? /*#__PURE__*/ _jsx(TooltipContent, {
69
- children: /*#__PURE__*/ _jsx(Text, {
70
- as: "span",
71
- size: "xs",
72
- className: "max-w-[360px] break-words",
73
- children: run.name
74
- })
75
- }) : null
76
- ]
77
- })
78
- ]
79
- }),
80
- /*#__PURE__*/ _jsxs("div", {
81
- className: "col-start-2 row-start-1 flex min-w-max items-center gap-6 justify-self-end",
82
- children: [
83
- /*#__PURE__*/ _jsx(WorkflowRunActionSlot, {
84
- action: action,
85
- cancelling: cancelling,
86
- onCancel: onCancel,
87
- rerunPending: rerunPending,
88
- onRerun: onRerun
89
- }),
90
- sourceAvailable ? /*#__PURE__*/ _jsx(Button, {
91
- ref: sourceButtonRef,
92
- type: "button",
93
- variant: "secondary",
94
- size: "xs",
95
- "aria-controls": sourcePanelId,
96
- "aria-expanded": sourceOpen,
97
- onClick: onSourceToggle,
98
- children: "View source"
99
- }) : null
100
- ]
106
+ ]
107
+ })
108
+ ]
109
+ })
101
110
  }),
111
+ hasAction ? /*#__PURE__*/ _jsx("div", {
112
+ className: "col-start-2 row-start-1 flex min-w-max items-center gap-6 justify-self-end max-[480px]:col-start-auto max-[480px]:row-start-auto max-[480px]:justify-self-start",
113
+ children: /*#__PURE__*/ _jsx(WorkflowRunActionSlot, {
114
+ action: action,
115
+ cancelling: cancelling,
116
+ onCancel: onCancel,
117
+ rerunPending: rerunPending,
118
+ onRerun: onRerun
119
+ })
120
+ }) : null,
102
121
  /*#__PURE__*/ _jsxs("div", {
103
- className: "col-span-2 row-start-2 flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-muted",
122
+ className: "col-span-2 row-start-2 flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-subtle max-[480px]:col-span-1 max-[480px]:row-start-auto",
104
123
  children: [
105
124
  run.number !== null ? /*#__PURE__*/ _jsxs(_Fragment, {
106
125
  children: [
@@ -128,7 +147,7 @@ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, sourceAvai
128
147
  children: /*#__PURE__*/ _jsxs("button", {
129
148
  type: "button",
130
149
  "aria-label": run.triggerLabel,
131
- className: "inline-flex max-w-full min-w-0 items-center gap-4 rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-muted outline-none focus-visible:shadow-button-neutral-focus",
150
+ className: "inline-flex max-w-full min-w-0 cursor-help items-center gap-4 rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-subtle outline-none focus-visible:shadow-button-neutral-focus",
132
151
  children: [
133
152
  /*#__PURE__*/ _jsx(TriggerSourceIcon, {
134
153
  provider: run.triggerProvider,
@@ -161,14 +180,14 @@ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, sourceAvai
161
180
  run.number !== null || attemptSwitcher || run.triggerDisplayLabel ? /*#__PURE__*/ _jsx(MetadataSeparator, {}) : null,
162
181
  /*#__PURE__*/ _jsx(RelativeTime, {
163
182
  value: run.runAttempt.createdAt,
164
- className: "shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-muted"
183
+ className: "shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-subtle"
165
184
  }),
166
185
  displayDuration ? /*#__PURE__*/ _jsxs(_Fragment, {
167
186
  children: [
168
187
  /*#__PURE__*/ _jsx(MetadataSeparator, {}),
169
188
  /*#__PURE__*/ _jsx(WorkflowRunDurationLabel, {
170
189
  duration: displayDuration,
171
- className: "text-foreground-neutral-muted"
190
+ className: "text-foreground-neutral-subtle"
172
191
  })
173
192
  ]
174
193
  }) : null
@@ -244,6 +263,11 @@ function workflowRunActionForRun(run) {
244
263
  if (run.runAttempt.status === 'succeeded' || !hasFailedOrCancelledJobs(run)) return 'rerun-all';
245
264
  return 'rerun-menu';
246
265
  }
266
+ function canRenderWorkflowRunAction(action, onCancel, onRerun) {
267
+ if (action === 'cancel') return onCancel !== undefined;
268
+ if (action === 'rerun-all' || action === 'rerun-menu') return onRerun !== undefined;
269
+ return false;
270
+ }
247
271
  function hasFailedOrCancelledJobs(run) {
248
272
  if (!workflowRunHasJobs(run)) return false;
249
273
  return run.jobs.some((job)=>job.status === 'failed' || job.status === 'cancelled');
@@ -251,7 +275,7 @@ function hasFailedOrCancelledJobs(run) {
251
275
  function workflowRunHasJobs(run) {
252
276
  return 'jobs' in run && Array.isArray(run.jobs);
253
277
  }
254
- function MetadataSeparator() {
278
+ export function MetadataSeparator() {
255
279
  return /*#__PURE__*/ _jsx("span", {
256
280
  "aria-hidden": "true",
257
281
  className: "h-12 w-px shrink-0 bg-border-neutral-base"
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-summary/workflow-run-summary.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport type {Ref} from 'react';\nimport {useId} from 'react';\nimport {WorkflowRunNumberLabel} from '#components/workflow-run-number-label.js';\nimport {\n isWorkflowRunTerminal,\n type Job,\n WORKFLOW_RUN_STATUSES,\n type WorkflowRunDetail,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport {WorkflowRunDurationLabel} from '../workflow-run-duration-label.js';\nimport {getWorkflowStatusVisual} from '../workflow-status/status-visuals.js';\nimport {WorkflowRunAttemptSwitcher} from './workflow-run-attempt-switcher.js';\n\nconst STATUS_BADGE_LABEL_WIDTH_CH = Math.max(\n ...WORKFLOW_RUN_STATUSES.map((status) => getWorkflowStatusVisual(status).label.length),\n);\n\ntype WorkflowRunAction = 'cancel' | 'rerun-all' | 'rerun-menu' | 'none';\n\nexport interface WorkflowRunSummaryProps {\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n run: WorkflowRunDetail;\n sourceAvailable?: boolean | undefined;\n sourceOpen?: boolean | undefined;\n sourcePanelId?: string | undefined;\n sourceButtonRef?: Ref<HTMLButtonElement> | undefined;\n onSourceToggle?: (() => void) | undefined;\n cancelling?: boolean | undefined;\n onCancel?: (() => void) | undefined;\n rerunPending?: boolean | undefined;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n latestAttempt?: number | undefined;\n}\n\nexport function WorkflowRunSummary({\n workspaceSlug,\n projectSlug,\n run,\n sourceAvailable = false,\n sourceOpen = false,\n sourcePanelId,\n sourceButtonRef,\n onSourceToggle,\n cancelling = false,\n onCancel,\n rerunPending = false,\n onRerun,\n latestAttempt,\n}: WorkflowRunSummaryProps) {\n const headingId = useId();\n const status = getWorkflowStatusVisual(run.runAttempt.status);\n const action = workflowRunActionForRun(run);\n const attemptSwitcher =\n latestAttempt && latestAttempt > 1 && workspaceSlug && projectSlug\n ? {workspaceSlug, projectSlug, latestAttempt}\n : null;\n const displayDuration = run.runAttempt.displayDuration;\n const {ref: headingTextRef, isTruncated: isHeadingTruncated} =\n useIsTextTruncated<HTMLSpanElement>(run.name);\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={headingId}\n className=\"border-b border-border-neutral-base bg-background-subtle-base px-16 py-8\"\n >\n <div className=\"grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-12 gap-y-4 overflow-hidden\">\n <div className=\"col-start-1 row-start-1 flex min-w-0 items-center gap-8\">\n <Badge variant={status.badge} size=\"xs\">\n <span className=\"text-center\" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>\n {status.label}\n </span>\n </Badge>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Header id={headingId} variant=\"h3\" className=\"min-w-0 truncate\">\n <span\n ref={headingTextRef}\n title={isHeadingTruncated ? run.name : undefined}\n className=\"block min-w-0 truncate\"\n >\n {run.name}\n </span>\n </Header>\n </TooltipTrigger>\n {isHeadingTruncated ? (\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"max-w-[360px] break-words\">\n {run.name}\n </Text>\n </TooltipContent>\n ) : null}\n </Tooltip>\n </div>\n\n <div className=\"col-start-2 row-start-1 flex min-w-max items-center gap-6 justify-self-end\">\n <WorkflowRunActionSlot\n action={action}\n cancelling={cancelling}\n onCancel={onCancel}\n rerunPending={rerunPending}\n onRerun={onRerun}\n />\n {sourceAvailable ? (\n <Button\n ref={sourceButtonRef}\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n aria-controls={sourcePanelId}\n aria-expanded={sourceOpen}\n onClick={onSourceToggle}\n >\n View source\n </Button>\n ) : null}\n </div>\n\n <div className=\"col-span-2 row-start-2 flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-muted\">\n {run.number !== null ? (\n <>\n <WorkflowRunNumberLabel run={run} />\n {attemptSwitcher || run.triggerDisplayLabel ? <MetadataSeparator /> : null}\n </>\n ) : null}\n\n {attemptSwitcher ? (\n <WorkflowRunAttemptSwitcher\n workspaceSlug={attemptSwitcher.workspaceSlug}\n projectSlug={attemptSwitcher.projectSlug}\n run={run}\n latestAttempt={attemptSwitcher.latestAttempt}\n />\n ) : null}\n\n {run.triggerDisplayLabel ? (\n <>\n {attemptSwitcher ? <MetadataSeparator /> : null}\n <span className=\"min-w-0\">\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={run.triggerLabel}\n className=\"inline-flex max-w-full min-w-0 items-center gap-4 rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-muted outline-none focus-visible:shadow-button-neutral-focus\"\n >\n <TriggerSourceIcon\n provider={run.triggerProvider}\n source={run.triggerSource}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {run.triggerDisplayLabel}\n </Text>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {run.triggerLabel}\n </Text>\n </TooltipContent>\n </Tooltip>\n </span>\n </>\n ) : null}\n\n {run.number !== null || attemptSwitcher || run.triggerDisplayLabel ? (\n <MetadataSeparator />\n ) : null}\n <RelativeTime\n value={run.runAttempt.createdAt}\n className=\"shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-muted\"\n />\n\n {displayDuration ? (\n <>\n <MetadataSeparator />\n <WorkflowRunDurationLabel\n duration={displayDuration}\n className=\"text-foreground-neutral-muted\"\n />\n </>\n ) : null}\n </div>\n </div>\n </section>\n </TimeTickerProvider>\n );\n}\n\nfunction WorkflowRunActionSlot({\n action,\n cancelling,\n onCancel,\n rerunPending,\n onRerun,\n}: {\n action: WorkflowRunAction;\n cancelling: boolean;\n onCancel?: (() => void) | undefined;\n rerunPending: boolean;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n}) {\n if (action === 'none') return null;\n\n if (action === 'cancel') {\n if (!onCancel) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"danger\"\n size=\"xs\"\n isLoading={cancelling}\n disabled={cancelling}\n onClick={onCancel}\n >\n Cancel workflow\n </Button>\n );\n }\n\n if (action === 'rerun-all') {\n if (!onRerun) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n isLoading={rerunPending}\n disabled={rerunPending}\n onClick={() => onRerun('all')}\n >\n Re-run workflow\n </Button>\n );\n }\n\n if (!onRerun) return null;\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n iconRight=\"arrowDownSLine\"\n isLoading={rerunPending}\n disabled={rerunPending}\n >\n Re-run jobs\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('all')}>\n Re-run all jobs\n </DropdownMenuItem>\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('failed')}>\n Re-run failed jobs\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction workflowRunActionForRun(run: WorkflowRunDetail): WorkflowRunAction {\n if (run.runAttempt.attempt !== run.currentAttempt) return 'none';\n if (!isWorkflowRunTerminal(run.runAttempt.status)) return 'cancel';\n if (run.runAttempt.status === 'succeeded' || !hasFailedOrCancelledJobs(run)) return 'rerun-all';\n return 'rerun-menu';\n}\n\nfunction hasFailedOrCancelledJobs(run: WorkflowRunDetail): boolean {\n if (!workflowRunHasJobs(run)) return false;\n\n return run.jobs.some((job) => job.status === 'failed' || job.status === 'cancelled');\n}\n\nfunction workflowRunHasJobs(run: WorkflowRunDetail): run is WorkflowRunDetail & {jobs: Job[]} {\n return 'jobs' in run && Array.isArray(run.jobs);\n}\n\nfunction MetadataSeparator() {\n return <span aria-hidden=\"true\" className=\"h-12 w-px shrink-0 bg-border-neutral-base\" />;\n}\n"],"names":["TriggerSourceIcon","Badge","Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","useIsTextTruncated","RelativeTime","TimeTickerProvider","Tooltip","TooltipContent","TooltipTrigger","Header","Text","useId","WorkflowRunNumberLabel","isWorkflowRunTerminal","WORKFLOW_RUN_STATUSES","WorkflowRunDurationLabel","getWorkflowStatusVisual","WorkflowRunAttemptSwitcher","STATUS_BADGE_LABEL_WIDTH_CH","Math","max","map","status","label","length","WorkflowRunSummary","workspaceSlug","projectSlug","run","sourceAvailable","sourceOpen","sourcePanelId","sourceButtonRef","onSourceToggle","cancelling","onCancel","rerunPending","onRerun","latestAttempt","headingId","runAttempt","action","workflowRunActionForRun","attemptSwitcher","displayDuration","ref","headingTextRef","isTruncated","isHeadingTruncated","name","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","className","div","variant","badge","size","span","style","width","asChild","id","title","undefined","as","WorkflowRunActionSlot","type","aria-controls","aria-expanded","onClick","number","triggerDisplayLabel","MetadataSeparator","button","aria-label","triggerLabel","provider","triggerProvider","source","triggerSource","aria-hidden","value","createdAt","duration","isLoading","disabled","iconRight","align","onSelect","attempt","currentAttempt","hasFailedOrCancelledJobs","workflowRunHasJobs","jobs","some","job","Array","isArray"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-summary/workflow-run-summary.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {useId} from 'react';\nimport {WorkflowRunNumberLabel} from '#components/workflow-run-number-label.js';\nimport {\n isWorkflowRunTerminal,\n type Job,\n WORKFLOW_RUN_STATUSES,\n type WorkflowRunDetail,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport {validateWorkflowRunsSearch, workflowRunListSearchParams} from '#routes/inputs.js';\nimport {WorkflowRunDurationLabel} from '../workflow-run-duration-label.js';\nimport {getWorkflowStatusVisual} from '../workflow-status/status-visuals.js';\nimport {WorkflowRunAttemptSwitcher} from './workflow-run-attempt-switcher.js';\n\nconst STATUS_BADGE_LABEL_WIDTH_CH = Math.max(\n ...WORKFLOW_RUN_STATUSES.map((status) => getWorkflowStatusVisual(status).label.length),\n);\n\ntype WorkflowRunAction = 'cancel' | 'rerun-all' | 'rerun-menu' | 'none';\n\nexport interface WorkflowRunSummaryProps {\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n run: WorkflowRunDetail;\n cancelling?: boolean | undefined;\n onCancel?: (() => void) | undefined;\n rerunPending?: boolean | undefined;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n latestAttempt?: number | undefined;\n}\n\nexport function WorkflowRunSummary({\n workspaceSlug,\n projectSlug,\n run,\n cancelling = false,\n onCancel,\n rerunPending = false,\n onRerun,\n latestAttempt,\n}: WorkflowRunSummaryProps) {\n const headingId = useId();\n const status = getWorkflowStatusVisual(run.runAttempt.status);\n const action = workflowRunActionForRun(run);\n const hasAction = canRenderWorkflowRunAction(action, onCancel, onRerun);\n const attemptSwitcher =\n latestAttempt && latestAttempt > 1 && workspaceSlug && projectSlug\n ? {workspaceSlug, projectSlug, latestAttempt}\n : null;\n const displayDuration = run.runAttempt.displayDuration;\n const {ref: headingTextRef, isTruncated: isHeadingTruncated} =\n useIsTextTruncated<HTMLSpanElement>(run.name);\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section aria-labelledby={headingId} className=\"bg-background-neutral-background px-16 py-12\">\n <div className=\"grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-12 gap-y-8 overflow-hidden max-[480px]:grid-cols-1\">\n <nav\n aria-label=\"Breadcrumb\"\n className=\"col-start-1 row-start-1 min-w-0 max-[480px]:col-start-auto max-[480px]:row-start-auto\"\n >\n <ol className=\"flex min-w-0 items-center gap-8\">\n {workspaceSlug && projectSlug ? (\n <li className=\"shrink-0\">\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs\"\n params={{workspaceSlug, projectSlug}}\n search={\n ((previous: Record<string, unknown>) =>\n workflowRunListSearchParams(validateWorkflowRunsSearch(previous))) as never\n }\n className=\"rounded-4 text-xs font-medium text-foreground-neutral-subtle underline decoration-border-neutral-strong underline-offset-4 outline-none hover:text-foreground-neutral-base focus-visible:shadow-border-interactive-with-active\"\n >\n Runs\n </Link>\n </li>\n ) : null}\n {workspaceSlug && projectSlug ? (\n <li aria-hidden=\"true\" className=\"shrink-0 text-xs text-foreground-neutral-subtle\">\n /\n </li>\n ) : null}\n <li aria-current=\"page\" className=\"flex min-w-0 items-center gap-8\">\n <Badge variant={status.badge} size=\"xs\">\n <span className=\"text-center\" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>\n {status.label}\n </span>\n </Badge>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Header id={headingId} variant=\"h3\" className=\"min-w-0 truncate\">\n <span\n ref={headingTextRef}\n title={isHeadingTruncated ? run.name : undefined}\n className=\"block min-w-0 truncate\"\n >\n {run.name}\n </span>\n </Header>\n </TooltipTrigger>\n {isHeadingTruncated ? (\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"max-w-[360px] break-words\">\n {run.name}\n </Text>\n </TooltipContent>\n ) : null}\n </Tooltip>\n </li>\n </ol>\n </nav>\n\n {hasAction ? (\n <div className=\"col-start-2 row-start-1 flex min-w-max items-center gap-6 justify-self-end max-[480px]:col-start-auto max-[480px]:row-start-auto max-[480px]:justify-self-start\">\n <WorkflowRunActionSlot\n action={action}\n cancelling={cancelling}\n onCancel={onCancel}\n rerunPending={rerunPending}\n onRerun={onRerun}\n />\n </div>\n ) : null}\n\n <div className=\"col-span-2 row-start-2 flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-subtle max-[480px]:col-span-1 max-[480px]:row-start-auto\">\n {run.number !== null ? (\n <>\n <WorkflowRunNumberLabel run={run} />\n {attemptSwitcher || run.triggerDisplayLabel ? <MetadataSeparator /> : null}\n </>\n ) : null}\n\n {attemptSwitcher ? (\n <WorkflowRunAttemptSwitcher\n workspaceSlug={attemptSwitcher.workspaceSlug}\n projectSlug={attemptSwitcher.projectSlug}\n run={run}\n latestAttempt={attemptSwitcher.latestAttempt}\n />\n ) : null}\n\n {run.triggerDisplayLabel ? (\n <>\n {attemptSwitcher ? <MetadataSeparator /> : null}\n <span className=\"min-w-0\">\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={run.triggerLabel}\n className=\"inline-flex max-w-full min-w-0 cursor-help items-center gap-4 rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-subtle outline-none focus-visible:shadow-button-neutral-focus\"\n >\n <TriggerSourceIcon\n provider={run.triggerProvider}\n source={run.triggerSource}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {run.triggerDisplayLabel}\n </Text>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {run.triggerLabel}\n </Text>\n </TooltipContent>\n </Tooltip>\n </span>\n </>\n ) : null}\n\n {run.number !== null || attemptSwitcher || run.triggerDisplayLabel ? (\n <MetadataSeparator />\n ) : null}\n <RelativeTime\n value={run.runAttempt.createdAt}\n className=\"shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-subtle\"\n />\n\n {displayDuration ? (\n <>\n <MetadataSeparator />\n <WorkflowRunDurationLabel\n duration={displayDuration}\n className=\"text-foreground-neutral-subtle\"\n />\n </>\n ) : null}\n </div>\n </div>\n </section>\n </TimeTickerProvider>\n );\n}\n\nfunction WorkflowRunActionSlot({\n action,\n cancelling,\n onCancel,\n rerunPending,\n onRerun,\n}: {\n action: WorkflowRunAction;\n cancelling: boolean;\n onCancel?: (() => void) | undefined;\n rerunPending: boolean;\n onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;\n}) {\n if (action === 'none') return null;\n\n if (action === 'cancel') {\n if (!onCancel) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"danger\"\n size=\"xs\"\n isLoading={cancelling}\n disabled={cancelling}\n onClick={onCancel}\n >\n Cancel workflow\n </Button>\n );\n }\n\n if (action === 'rerun-all') {\n if (!onRerun) return null;\n\n return (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n isLoading={rerunPending}\n disabled={rerunPending}\n onClick={() => onRerun('all')}\n >\n Re-run workflow\n </Button>\n );\n }\n\n if (!onRerun) return null;\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n iconRight=\"arrowDownSLine\"\n isLoading={rerunPending}\n disabled={rerunPending}\n >\n Re-run jobs\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('all')}>\n Re-run all jobs\n </DropdownMenuItem>\n <DropdownMenuItem disabled={rerunPending} onSelect={() => onRerun('failed')}>\n Re-run failed jobs\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction workflowRunActionForRun(run: WorkflowRunDetail): WorkflowRunAction {\n if (run.runAttempt.attempt !== run.currentAttempt) return 'none';\n if (!isWorkflowRunTerminal(run.runAttempt.status)) return 'cancel';\n if (run.runAttempt.status === 'succeeded' || !hasFailedOrCancelledJobs(run)) return 'rerun-all';\n return 'rerun-menu';\n}\n\nfunction canRenderWorkflowRunAction(\n action: WorkflowRunAction,\n onCancel: (() => void) | undefined,\n onRerun: ((mode: WorkflowRunRerunMode) => void) | undefined,\n): boolean {\n if (action === 'cancel') return onCancel !== undefined;\n if (action === 'rerun-all' || action === 'rerun-menu') return onRerun !== undefined;\n return false;\n}\n\nfunction hasFailedOrCancelledJobs(run: WorkflowRunDetail): boolean {\n if (!workflowRunHasJobs(run)) return false;\n\n return run.jobs.some((job) => job.status === 'failed' || job.status === 'cancelled');\n}\n\nfunction workflowRunHasJobs(run: WorkflowRunDetail): run is WorkflowRunDetail & {jobs: Job[]} {\n return 'jobs' in run && Array.isArray(run.jobs);\n}\n\nexport function MetadataSeparator() {\n return <span aria-hidden=\"true\" className=\"h-12 w-px shrink-0 bg-border-neutral-base\" />;\n}\n"],"names":["TriggerSourceIcon","Badge","Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","useIsTextTruncated","RelativeTime","TimeTickerProvider","Tooltip","TooltipContent","TooltipTrigger","Header","Text","Link","useId","WorkflowRunNumberLabel","isWorkflowRunTerminal","WORKFLOW_RUN_STATUSES","validateWorkflowRunsSearch","workflowRunListSearchParams","WorkflowRunDurationLabel","getWorkflowStatusVisual","WorkflowRunAttemptSwitcher","STATUS_BADGE_LABEL_WIDTH_CH","Math","max","map","status","label","length","WorkflowRunSummary","workspaceSlug","projectSlug","run","cancelling","onCancel","rerunPending","onRerun","latestAttempt","headingId","runAttempt","action","workflowRunActionForRun","hasAction","canRenderWorkflowRunAction","attemptSwitcher","displayDuration","ref","headingTextRef","isTruncated","isHeadingTruncated","name","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","className","div","nav","aria-label","ol","li","to","params","search","previous","aria-hidden","aria-current","variant","badge","size","span","style","width","asChild","id","title","undefined","as","WorkflowRunActionSlot","number","triggerDisplayLabel","MetadataSeparator","button","type","triggerLabel","provider","triggerProvider","source","triggerSource","value","createdAt","duration","isLoading","disabled","onClick","iconRight","align","onSelect","attempt","currentAttempt","hasFailedOrCancelledJobs","workflowRunHasJobs","jobs","some","job","Array","isArray"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { RunAnnotationSummaryLine } from './run-annotation-summary-line.js';
2
+ export { RunAnnotationsEmpty } from './run-annotations-empty.js';
3
+ export { RunJobsList } from './run-jobs-list.js';
4
+ export { RunTabCount } from './run-tab-count.js';
5
+ export { RunTabStrip } from './run-tab-strip.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,wBAAwB,EAAC,MAAM,kCAAkC,CAAC;AAC1E,OAAO,EAAC,mBAAmB,EAAC,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { RunAnnotationSummaryLine } from './run-annotation-summary-line.js';
2
+ export { RunAnnotationsEmpty } from './run-annotations-empty.js';
3
+ export { RunJobsList } from './run-jobs-list.js';
4
+ export { RunTabCount } from './run-tab-count.js';
5
+ export { RunTabStrip } from './run-tab-strip.js';
6
+
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/workflow-run-tabs/index.ts"],"sourcesContent":["export {RunAnnotationSummaryLine} from './run-annotation-summary-line.js';\nexport {RunAnnotationsEmpty} from './run-annotations-empty.js';\nexport {RunJobsList} from './run-jobs-list.js';\nexport {RunTabCount} from './run-tab-count.js';\nexport {RunTabStrip} from './run-tab-strip.js';\n"],"names":["RunAnnotationSummaryLine","RunAnnotationsEmpty","RunJobsList","RunTabCount","RunTabStrip"],"mappings":"AAAA,SAAQA,wBAAwB,QAAO,mCAAmC;AAC1E,SAAQC,mBAAmB,QAAO,6BAA6B;AAC/D,SAAQC,WAAW,QAAO,qBAAqB;AAC/C,SAAQC,WAAW,QAAO,qBAAqB;AAC/C,SAAQC,WAAW,QAAO,qBAAqB"}
@@ -0,0 +1,11 @@
1
+ import type { RunAnnotationSummary } from '#core/workflow-run-tabs.js';
2
+ import { type WorkflowRunsSearch } from '#routes/inputs.js';
3
+ export interface RunAnnotationSummaryLineProps {
4
+ summary?: RunAnnotationSummary | undefined;
5
+ workspaceSlug?: string | undefined;
6
+ projectSlug?: string | undefined;
7
+ workflowRunId?: string | undefined;
8
+ search?: WorkflowRunsSearch | undefined;
9
+ }
10
+ export declare function RunAnnotationSummaryLine({ summary, workspaceSlug, projectSlug, workflowRunId, search, }: RunAnnotationSummaryLineProps): import("react").JSX.Element | null;
11
+ //# sourceMappingURL=run-annotation-summary-line.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-annotation-summary-line.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/run-annotation-summary-line.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAGL,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAG3B,MAAM,WAAW,6BAA6B;IAC5C,OAAO,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CACzC;AAED,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,aAAa,EACb,WAAW,EACX,aAAa,EACb,MAAW,GACZ,EAAE,6BAA6B,sCA2B/B"}
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Text } from '@shipfox/react-ui/typography';
3
+ import { Link } from '@tanstack/react-router';
4
+ import { WORKFLOW_RUN_ANNOTATION_SEVERITIES, workflowRunSearchParams } from '#routes/inputs.js';
5
+ import { MetadataSeparator } from '../workflow-run-summary/workflow-run-summary.js';
6
+ export function RunAnnotationSummaryLine({ summary, workspaceSlug, projectSlug, workflowRunId, search = {} }) {
7
+ if (!summary) return null;
8
+ const visibleSeverities = WORKFLOW_RUN_ANNOTATION_SEVERITIES.filter((severity)=>summary[severity] > 0);
9
+ return /*#__PURE__*/ _jsxs("div", {
10
+ className: "flex flex-wrap items-center gap-x-8 gap-y-4 text-foreground-neutral-subtle",
11
+ children: [
12
+ /*#__PURE__*/ _jsxs(Text, {
13
+ as: "span",
14
+ size: "xs",
15
+ className: "shrink-0 text-foreground-neutral-subtle",
16
+ children: [
17
+ summary.total,
18
+ " ",
19
+ pluralize('annotation', summary.total)
20
+ ]
21
+ }),
22
+ visibleSeverities.map((severity)=>/*#__PURE__*/ _jsxs("span", {
23
+ className: "inline-flex shrink-0 items-center gap-8",
24
+ children: [
25
+ /*#__PURE__*/ _jsx(MetadataSeparator, {}),
26
+ /*#__PURE__*/ _jsx(SeverityLink, {
27
+ count: summary[severity],
28
+ severity: severity,
29
+ workspaceSlug: workspaceSlug,
30
+ projectSlug: projectSlug,
31
+ workflowRunId: workflowRunId,
32
+ search: search
33
+ })
34
+ ]
35
+ }, severity))
36
+ ]
37
+ });
38
+ }
39
+ function SeverityLink({ count, severity, workspaceSlug, projectSlug, workflowRunId, search }) {
40
+ const label = `${count} ${pluralize(severity, count)}`;
41
+ if (!workspaceSlug || !projectSlug || !workflowRunId) {
42
+ return /*#__PURE__*/ _jsx("span", {
43
+ className: "text-foreground-highlight-interactive",
44
+ children: label
45
+ });
46
+ }
47
+ const searchWithoutAnnotation = {
48
+ ...search
49
+ };
50
+ delete searchWithoutAnnotation.annotation;
51
+ return /*#__PURE__*/ _jsx(Link, {
52
+ to: "/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId",
53
+ params: {
54
+ workspaceSlug,
55
+ projectSlug,
56
+ workflowRunId
57
+ },
58
+ search: workflowRunSearchParams({
59
+ ...searchWithoutAnnotation,
60
+ tab: 'annotations',
61
+ severity
62
+ }, search),
63
+ className: "text-foreground-highlight-interactive outline-none hover:underline focus-visible:shadow-border-interactive-with-active",
64
+ children: label
65
+ });
66
+ }
67
+ function pluralize(word, count) {
68
+ return count === 1 ? word : `${word}s`;
69
+ }
70
+
71
+ //# sourceMappingURL=run-annotation-summary-line.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotation-summary-line.tsx"],"sourcesContent":["import {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport type {RunAnnotationSummary} from '#core/workflow-run-tabs.js';\nimport {\n WORKFLOW_RUN_ANNOTATION_SEVERITIES,\n type WorkflowRunAnnotationSeverity,\n type WorkflowRunsSearch,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {MetadataSeparator} from '../workflow-run-summary/workflow-run-summary.js';\n\nexport interface RunAnnotationSummaryLineProps {\n summary?: RunAnnotationSummary | undefined;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n search?: WorkflowRunsSearch | undefined;\n}\n\nexport function RunAnnotationSummaryLine({\n summary,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search = {},\n}: RunAnnotationSummaryLineProps) {\n if (!summary) return null;\n\n const visibleSeverities = WORKFLOW_RUN_ANNOTATION_SEVERITIES.filter(\n (severity) => summary[severity] > 0,\n );\n\n return (\n <div className=\"flex flex-wrap items-center gap-x-8 gap-y-4 text-foreground-neutral-subtle\">\n <Text as=\"span\" size=\"xs\" className=\"shrink-0 text-foreground-neutral-subtle\">\n {summary.total} {pluralize('annotation', summary.total)}\n </Text>\n {visibleSeverities.map((severity) => (\n <span key={severity} className=\"inline-flex shrink-0 items-center gap-8\">\n <MetadataSeparator />\n <SeverityLink\n count={summary[severity]}\n severity={severity}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n </span>\n ))}\n </div>\n );\n}\n\nfunction SeverityLink({\n count,\n severity,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n count: number;\n severity: WorkflowRunAnnotationSeverity;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n search: WorkflowRunsSearch;\n}) {\n const label = `${count} ${pluralize(severity, count)}`;\n if (!workspaceSlug || !projectSlug || !workflowRunId) {\n return <span className=\"text-foreground-highlight-interactive\">{label}</span>;\n }\n\n const searchWithoutAnnotation = {...search};\n delete searchWithoutAnnotation.annotation;\n\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {...searchWithoutAnnotation, tab: 'annotations', severity},\n search,\n ) as never\n }\n className=\"text-foreground-highlight-interactive outline-none hover:underline focus-visible:shadow-border-interactive-with-active\"\n >\n {label}\n </Link>\n );\n}\n\nfunction pluralize(word: string, count: number): string {\n return count === 1 ? word : `${word}s`;\n}\n"],"names":["Text","Link","WORKFLOW_RUN_ANNOTATION_SEVERITIES","workflowRunSearchParams","MetadataSeparator","RunAnnotationSummaryLine","summary","workspaceSlug","projectSlug","workflowRunId","search","visibleSeverities","filter","severity","div","className","as","size","total","pluralize","map","span","SeverityLink","count","label","searchWithoutAnnotation","annotation","to","params","tab","word"],"mappings":";AAAA,SAAQA,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAE5C,SACEC,kCAAkC,EAGlCC,uBAAuB,QAClB,oBAAoB;AAC3B,SAAQC,iBAAiB,QAAO,kDAAkD;AAUlF,OAAO,SAASC,yBAAyB,EACvCC,OAAO,EACPC,aAAa,EACbC,WAAW,EACXC,aAAa,EACbC,SAAS,CAAC,CAAC,EACmB;IAC9B,IAAI,CAACJ,SAAS,OAAO;IAErB,MAAMK,oBAAoBT,mCAAmCU,MAAM,CACjE,CAACC,WAAaP,OAAO,CAACO,SAAS,GAAG;IAGpC,qBACE,MAACC;QAAIC,WAAU;;0BACb,MAACf;gBAAKgB,IAAG;gBAAOC,MAAK;gBAAKF,WAAU;;oBACjCT,QAAQY,KAAK;oBAAC;oBAAEC,UAAU,cAAcb,QAAQY,KAAK;;;YAEvDP,kBAAkBS,GAAG,CAAC,CAACP,yBACtB,MAACQ;oBAAoBN,WAAU;;sCAC7B,KAACX;sCACD,KAACkB;4BACCC,OAAOjB,OAAO,CAACO,SAAS;4BACxBA,UAAUA;4BACVN,eAAeA;4BACfC,aAAaA;4BACbC,eAAeA;4BACfC,QAAQA;;;mBARDG;;;AAcnB;AAEA,SAASS,aAAa,EACpBC,KAAK,EACLV,QAAQ,EACRN,aAAa,EACbC,WAAW,EACXC,aAAa,EACbC,MAAM,EAQP;IACC,MAAMc,QAAQ,GAAGD,MAAM,CAAC,EAAEJ,UAAUN,UAAUU,QAAQ;IACtD,IAAI,CAAChB,iBAAiB,CAACC,eAAe,CAACC,eAAe;QACpD,qBAAO,KAACY;YAAKN,WAAU;sBAAyCS;;IAClE;IAEA,MAAMC,0BAA0B;QAAC,GAAGf,MAAM;IAAA;IAC1C,OAAOe,wBAAwBC,UAAU;IAEzC,qBACE,KAACzB;QACC0B,IAAG;QACHC,QAAQ;YAACrB;YAAeC;YAAaC;QAAa;QAClDC,QACEP,wBACE;YAAC,GAAGsB,uBAAuB;YAAEI,KAAK;YAAehB;QAAQ,GACzDH;QAGJK,WAAU;kBAETS;;AAGP;AAEA,SAASL,UAAUW,IAAY,EAAEP,KAAa;IAC5C,OAAOA,UAAU,IAAIO,OAAO,GAAGA,KAAK,CAAC,CAAC;AACxC"}
@@ -0,0 +1,2 @@
1
+ export declare function RunAnnotationsEmpty(): import("react").JSX.Element;
2
+ //# sourceMappingURL=run-annotations-empty.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-annotations-empty.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/run-annotations-empty.tsx"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,gCAQlC"}
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { EmptyState } from '@shipfox/react-ui/empty-state';
3
+ export function RunAnnotationsEmpty() {
4
+ return /*#__PURE__*/ _jsx(EmptyState, {
5
+ icon: "fileDamageLine",
6
+ title: "No annotations",
7
+ description: "This run has no annotations to show."
8
+ });
9
+ }
10
+
11
+ //# sourceMappingURL=run-annotations-empty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotations-empty.tsx"],"sourcesContent":["import {EmptyState} from '@shipfox/react-ui/empty-state';\n\nexport function RunAnnotationsEmpty() {\n return (\n <EmptyState\n icon=\"fileDamageLine\"\n title=\"No annotations\"\n description=\"This run has no annotations to show.\"\n />\n );\n}\n"],"names":["EmptyState","RunAnnotationsEmpty","icon","title","description"],"mappings":";AAAA,SAAQA,UAAU,QAAO,gCAAgC;AAEzD,OAAO,SAASC;IACd,qBACE,KAACD;QACCE,MAAK;QACLC,OAAM;QACNC,aAAY;;AAGlB"}
@@ -0,0 +1,18 @@
1
+ import { defaultJobExecution, type Job } from '#core/workflow-run.js';
2
+ import { JobCard } from '../workflow-run-view/job-card.js';
3
+ export interface RunJobsListProps {
4
+ jobs: Job[];
5
+ selectedJobId?: string | undefined;
6
+ onSelectedJobChange: (jobId: string | undefined) => void;
7
+ workspaceSlug?: string | undefined;
8
+ selectedJobExecution: ReturnType<typeof defaultJobExecution>;
9
+ selectedAttemptId: string | null | undefined;
10
+ onSelectedJobExecutionChange: ((jobExecutionId: string | undefined) => void) | undefined;
11
+ onSelectedAttemptChange: ((attemptId: string | undefined) => void) | undefined;
12
+ sourcePanelId?: string | undefined;
13
+ sourceAvailable?: boolean | undefined;
14
+ focusedSourceStepId?: string | null | undefined;
15
+ onOpenStepSource?: Parameters<typeof JobCard>[0]['onOpenStepSource'];
16
+ }
17
+ export declare function RunJobsList({ jobs, selectedJobId, onSelectedJobChange, workspaceSlug, selectedJobExecution, selectedAttemptId, onSelectedJobExecutionChange, onSelectedAttemptChange, sourcePanelId, sourceAvailable, focusedSourceStepId, onOpenStepSource, }: RunJobsListProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=run-jobs-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-jobs-list.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/run-jobs-list.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,mBAAmB,EAGnB,KAAK,GAAG,EACT,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAC,OAAO,EAAC,MAAM,kCAAkC,CAAC;AAGzD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACzD,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oBAAoB,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;IAC7D,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC7C,4BAA4B,EAAE,CAAC,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACzF,uBAAuB,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC/E,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChD,gBAAgB,CAAC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;CACtE;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,4BAA4B,EAC5B,uBAAuB,EACvB,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GACjB,EAAE,gBAAgB,+BAsFlB"}
@@ -0,0 +1,107 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { EmptyState } from '@shipfox/react-ui/empty-state';
3
+ import { useTimeTick } from '@shipfox/react-ui/time-ticker';
4
+ import { cn } from '@shipfox/react-ui/utils';
5
+ import { defaultJobExecution, deriveJobDisplayStatus, deriveJobExecutionDisplayStatus } from '#core/workflow-run.js';
6
+ import { formatJobDurationAccessibleLabel } from '../job-graph/job-duration-format.js';
7
+ import { JobDurationLabel } from '../job-graph/job-duration-label.js';
8
+ import { JobCard } from '../workflow-run-view/job-card.js';
9
+ import { WorkflowStatusIcon } from '../workflow-status/workflow-status-icon.js';
10
+ export function RunJobsList({ jobs, selectedJobId, onSelectedJobChange, workspaceSlug, selectedJobExecution, selectedAttemptId, onSelectedJobExecutionChange, onSelectedAttemptChange, sourcePanelId, sourceAvailable, focusedSourceStepId, onOpenStepSource }) {
11
+ useTimeTick();
12
+ if (jobs.length === 0) {
13
+ return /*#__PURE__*/ _jsx("section", {
14
+ "aria-label": "Workflow jobs",
15
+ className: "rounded-8 border border-border-neutral-base bg-background-components-base",
16
+ children: /*#__PURE__*/ _jsx("div", {
17
+ className: "min-h-160",
18
+ children: /*#__PURE__*/ _jsx(RunJobsEmptyState, {})
19
+ })
20
+ });
21
+ }
22
+ const orderedJobs = [
23
+ ...jobs
24
+ ].sort(compareJobs);
25
+ return /*#__PURE__*/ _jsx("section", {
26
+ "aria-label": "Workflow jobs",
27
+ className: "flex min-w-0 flex-col gap-8",
28
+ children: /*#__PURE__*/ _jsx("ul", {
29
+ "aria-label": "Run jobs",
30
+ className: "m-0 flex min-w-0 list-none flex-col gap-8 p-0",
31
+ children: orderedJobs.map((job)=>{
32
+ const selected = job.id === selectedJobId;
33
+ const status = deriveJobDisplayStatus(job);
34
+ const execution = defaultJobExecution(job);
35
+ const durationLabel = formatJobDurationAccessibleLabel(job.displayDuration, execution ? deriveJobExecutionDisplayStatus(execution) : undefined);
36
+ const rowLabel = [
37
+ job.displayName,
38
+ statusLabel(status),
39
+ durationLabel,
40
+ job.carriedOver ? 'reused' : undefined
41
+ ].filter((part)=>Boolean(part)).join(', ');
42
+ return /*#__PURE__*/ _jsxs("li", {
43
+ className: "min-w-0",
44
+ children: [
45
+ /*#__PURE__*/ _jsxs("button", {
46
+ type: "button",
47
+ "aria-expanded": selected,
48
+ "aria-controls": selected ? `job-card-${job.id}` : undefined,
49
+ "aria-label": rowLabel,
50
+ onClick: ()=>onSelectedJobChange(selected ? undefined : job.id),
51
+ className: cn('relative flex min-h-44 w-full min-w-0 items-center gap-8 rounded-8 bg-background-neutral-base px-12 text-left shadow-button-neutral outline-none transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus', selected && 'bg-background-components-hover hover:bg-background-components-pressed'),
52
+ children: [
53
+ selected ? /*#__PURE__*/ _jsx("span", {
54
+ "aria-hidden": "true",
55
+ className: "absolute inset-y-6 left-0 w-px rounded-full bg-border-highlights-interactive"
56
+ }) : null,
57
+ /*#__PURE__*/ _jsx(WorkflowStatusIcon, {
58
+ status: status,
59
+ size: 14,
60
+ tooltip: false
61
+ }),
62
+ /*#__PURE__*/ _jsx("span", {
63
+ className: "min-w-0 flex-1 truncate text-sm font-medium text-foreground-neutral-base",
64
+ children: job.displayName
65
+ }),
66
+ /*#__PURE__*/ _jsx(JobDurationLabel, {
67
+ duration: job.displayDuration
68
+ })
69
+ ]
70
+ }),
71
+ selected ? /*#__PURE__*/ _jsx("div", {
72
+ id: `job-card-${job.id}`,
73
+ className: "mt-8",
74
+ children: /*#__PURE__*/ _jsx(JobCard, {
75
+ workspaceSlug: workspaceSlug,
76
+ job: job,
77
+ selectedJobExecution: selectedJobExecution,
78
+ selectedAttemptId: job.carriedOver ? undefined : selectedAttemptId,
79
+ onSelectedJobExecutionChange: onSelectedJobExecutionChange,
80
+ onSelectedAttemptChange: onSelectedAttemptChange,
81
+ sourcePanelId: sourcePanelId,
82
+ sourceAvailable: sourceAvailable,
83
+ focusedSourceStepId: focusedSourceStepId,
84
+ onOpenStepSource: onOpenStepSource
85
+ })
86
+ }) : null
87
+ ]
88
+ }, job.id);
89
+ })
90
+ })
91
+ });
92
+ }
93
+ function RunJobsEmptyState() {
94
+ return /*#__PURE__*/ _jsx(EmptyState, {
95
+ icon: "componentLine",
96
+ title: "No jobs yet",
97
+ description: "This run has not materialized jobs."
98
+ });
99
+ }
100
+ function compareJobs(left, right) {
101
+ return left.position - right.position || left.displayName.localeCompare(right.displayName) || left.id.localeCompare(right.id);
102
+ }
103
+ function statusLabel(status) {
104
+ return status === 'listening' ? 'Listening' : status.charAt(0).toUpperCase() + status.slice(1);
105
+ }
106
+
107
+ //# sourceMappingURL=run-jobs-list.js.map