@shipfox/client-workflows 14.0.0 → 15.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/preview.tsx +7 -2
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +35 -0
- package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -0
- package/dist/components/{workflow-run-view → job-detail}/agent-config-failure-callout.js +2 -2
- package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -0
- package/dist/components/job-detail/job-detail-header.d.ts +8 -0
- package/dist/components/job-detail/job-detail-header.d.ts.map +1 -0
- package/dist/components/job-detail/job-detail-header.js +111 -0
- package/dist/components/job-detail/job-detail-header.js.map +1 -0
- package/dist/components/job-detail/job-detail-view.d.ts +16 -0
- package/dist/components/job-detail/job-detail-view.d.ts.map +1 -0
- package/dist/components/job-detail/job-detail-view.js +405 -0
- package/dist/components/job-detail/job-detail-view.js.map +1 -0
- package/dist/components/job-detail/job-empty-states.d.ts +10 -0
- package/dist/components/job-detail/job-empty-states.d.ts.map +1 -0
- package/dist/components/job-detail/job-empty-states.js +195 -0
- package/dist/components/job-detail/job-empty-states.js.map +1 -0
- package/dist/components/job-detail/job-execution-switcher.d.ts.map +1 -0
- package/dist/components/job-detail/job-execution-switcher.js.map +1 -0
- package/dist/components/job-detail/job-execution-time-text.d.ts.map +1 -0
- package/dist/components/job-detail/job-execution-time-text.js.map +1 -0
- package/dist/components/job-detail/job-selection.d.ts +24 -0
- package/dist/components/job-detail/job-selection.d.ts.map +1 -0
- package/dist/components/job-detail/job-selection.js +66 -0
- package/dist/components/job-detail/job-selection.js.map +1 -0
- package/dist/components/job-detail/step-attempt-log-panel.d.ts +15 -0
- package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -0
- package/dist/components/{workflow-run-view → job-detail}/step-attempt-log-panel.js +61 -16
- package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -0
- package/dist/components/job-graph/job-graph-content.js +1 -1
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/step-list/step-list-model.js +2 -2
- package/dist/components/step-list/step-list-model.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +1 -0
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js +29 -4
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-duration-label.d.ts +9 -4
- package/dist/components/workflow-run-duration-label.d.ts.map +1 -1
- package/dist/components/workflow-run-duration-label.js +27 -7
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +6 -5
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +68 -64
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts +0 -3
- package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/index.js +0 -3
- package/dist/components/workflow-run-tabs/index.js.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +3 -1
- package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotations-empty.js +2 -2
- package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -1
- package/dist/components/workflow-run-view/index.d.ts +0 -1
- package/dist/components/workflow-run-view/index.d.ts.map +1 -1
- package/dist/components/workflow-run-view/index.js +0 -1
- package/dist/components/workflow-run-view/index.js.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts +20 -0
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -0
- package/dist/components/workflow-run-view/run-workspace-nav.js +271 -0
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -0
- package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-selection.js +1 -14
- package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts +10 -8
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +250 -236
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
- package/dist/components/workflow-status/status-visuals.js +10 -2
- package/dist/components/workflow-status/status-visuals.js.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.js +13 -2
- package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +9 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js +13 -3
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/step-attempt.d.ts.map +1 -1
- package/dist/core/entities/step.d.ts +2 -0
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js +13 -0
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +56 -1
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +98 -0
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/workflow-run.d.ts +4 -4
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js +3 -3
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/feature.d.ts +20 -16
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +5 -0
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/workflow-runs.d.ts +5 -3
- package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
- package/dist/hooks/api/workflow-runs.js +5 -4
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/pages/workflow-job-detail-page.d.ts +11 -0
- package/dist/pages/workflow-job-detail-page.d.ts.map +1 -0
- package/dist/pages/workflow-job-detail-page.js +66 -0
- package/dist/pages/workflow-job-detail-page.js.map +1 -0
- package/dist/pages/workflow-run-detail-page.d.ts.map +1 -1
- package/dist/pages/workflow-run-detail-page.js +4 -37
- package/dist/pages/workflow-run-detail-page.js.map +1 -1
- package/dist/routes/inputs.d.ts +47 -34
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +69 -37
- package/dist/routes/inputs.js.map +1 -1
- package/dist/routes/job-detail.d.ts +10 -0
- package/dist/routes/job-detail.d.ts.map +1 -0
- package/dist/routes/job-detail.js +27 -0
- package/dist/routes/job-detail.js.map +1 -0
- package/dist/routes/project-route.d.ts +1 -1
- package/dist/routes/run-detail.d.ts.map +1 -1
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/components/{workflow-run-view → job-detail}/agent-config-failure-callout.stories.tsx +1 -1
- package/src/components/{workflow-run-view → job-detail}/agent-config-failure-callout.tsx +2 -2
- package/src/components/job-detail/job-detail-header.test.tsx +31 -0
- package/src/components/job-detail/job-detail-header.tsx +111 -0
- package/src/components/job-detail/job-detail-view.stories.tsx +526 -0
- package/src/components/job-detail/job-detail-view.tsx +450 -0
- package/src/components/job-detail/job-empty-states.test.ts +27 -0
- package/src/components/job-detail/job-empty-states.tsx +248 -0
- package/src/components/job-detail/job-selection.test.ts +188 -0
- package/src/components/job-detail/job-selection.ts +96 -0
- package/src/components/{workflow-run-view → job-detail}/step-attempt-log-panel.test.tsx +32 -9
- package/src/components/{workflow-run-view → job-detail}/step-attempt-log-panel.tsx +52 -18
- package/src/components/job-graph/job-graph-content.tsx +1 -1
- package/src/components/step-list/step-list-model.test.ts +1 -1
- package/src/components/step-list/step-list-model.ts +2 -2
- package/src/components/step-list/step-list.stories.tsx +1 -1
- package/src/components/step-list/step-list.test.tsx +33 -0
- package/src/components/step-list/step-list.tsx +40 -9
- package/src/components/workflow-run-duration-label.tsx +50 -12
- package/src/components/workflow-run-list/workflow-run-row.tsx +6 -4
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +38 -0
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +79 -5
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +56 -60
- package/src/components/workflow-run-tabs/index.ts +0 -3
- package/src/components/workflow-run-tabs/run-annotations-empty.tsx +6 -2
- package/src/components/workflow-run-view/index.ts +0 -1
- package/src/components/workflow-run-view/run-workspace-nav.test.tsx +242 -0
- package/src/components/workflow-run-view/run-workspace-nav.tsx +301 -0
- package/src/components/workflow-run-view/workflow-run-selection.ts +1 -23
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +62 -1136
- package/src/components/workflow-run-view/workflow-run-view.tsx +330 -321
- package/src/components/workflow-status/status-visuals.ts +5 -2
- package/src/components/workflow-status/workflow-status-icon.stories.tsx +1 -0
- package/src/components/workflow-status/workflow-status-icon.tsx +26 -1
- package/src/core/entities/job-execution.ts +17 -2
- package/src/core/entities/step.ts +26 -0
- package/src/core/entities/workflow-run-display.test.ts +159 -0
- package/src/core/entities/workflow-run.ts +139 -1
- package/src/core/workflow-run.ts +16 -1
- package/src/feature.ts +5 -0
- package/src/hooks/api/workflow-runs.ts +6 -2
- package/src/index.ts +1 -0
- package/src/page-harness-budget.test.ts +1 -0
- package/src/pages/workflow-job-detail-page.test.tsx +539 -0
- package/src/pages/workflow-job-detail-page.tsx +84 -0
- package/src/pages/workflow-run-detail-page.tsx +3 -43
- package/src/pages/workflow-run-pages.test.tsx +77 -332
- package/src/routes/inputs.test.ts +56 -6
- package/src/routes/inputs.ts +64 -17
- package/src/routes/job-detail.tsx +29 -0
- package/test/pages.tsx +25 -3
- package/test/render.tsx +6 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +0 -18
- package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +0 -1
- package/dist/components/workflow-run-tabs/run-jobs-list.js +0 -107
- package/dist/components/workflow-run-tabs/run-jobs-list.js.map +0 -1
- package/dist/components/workflow-run-tabs/run-tab-count.d.ts +0 -6
- package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +0 -1
- package/dist/components/workflow-run-tabs/run-tab-count.js +0 -27
- package/dist/components/workflow-run-tabs/run-tab-count.js.map +0 -1
- package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +0 -8
- package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +0 -1
- package/dist/components/workflow-run-tabs/run-tab-strip.js +0 -82
- package/dist/components/workflow-run-tabs/run-tab-strip.js.map +0 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +0 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +0 -1
- package/dist/components/workflow-run-view/job-card.d.ts +0 -17
- package/dist/components/workflow-run-view/job-card.d.ts.map +0 -1
- package/dist/components/workflow-run-view/job-card.js +0 -496
- package/dist/components/workflow-run-view/job-card.js.map +0 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +0 -1
- package/dist/components/workflow-run-view/job-execution-switcher.js.map +0 -1
- package/dist/components/workflow-run-view/job-execution-time-text.d.ts.map +0 -1
- package/dist/components/workflow-run-view/job-execution-time-text.js.map +0 -1
- package/dist/components/workflow-run-view/step-attempt-log-panel.d.ts +0 -9
- package/dist/components/workflow-run-view/step-attempt-log-panel.d.ts.map +0 -1
- package/dist/components/workflow-run-view/step-attempt-log-panel.js.map +0 -1
- package/src/components/workflow-run-tabs/run-jobs-list.tsx +0 -151
- package/src/components/workflow-run-tabs/run-tab-count.tsx +0 -28
- package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +0 -52
- package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +0 -117
- package/src/components/workflow-run-tabs/run-tab-strip.tsx +0 -97
- package/src/components/workflow-run-view/job-card.stories.tsx +0 -789
- package/src/components/workflow-run-view/job-card.test.tsx +0 -115
- package/src/components/workflow-run-view/job-card.tsx +0 -614
- /package/dist/components/{workflow-run-view → job-detail}/agent-config-failure-callout.d.ts +0 -0
- /package/dist/components/{workflow-run-view → job-detail}/job-execution-switcher.d.ts +0 -0
- /package/dist/components/{workflow-run-view → job-detail}/job-execution-switcher.js +0 -0
- /package/dist/components/{workflow-run-view → job-detail}/job-execution-time-text.d.ts +0 -0
- /package/dist/components/{workflow-run-view → job-detail}/job-execution-time-text.js +0 -0
- /package/src/components/{workflow-run-view → job-detail}/job-execution-switcher.tsx +0 -0
- /package/src/components/{workflow-run-view → job-detail}/job-execution-time-text.tsx +0 -0
|
@@ -2,37 +2,37 @@ import {ApiError} from '@shipfox/client-api';
|
|
|
2
2
|
import {QueryLoadError} from '@shipfox/client-ui';
|
|
3
3
|
import {EmptyState} from '@shipfox/react-ui/empty-state';
|
|
4
4
|
import {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
Select,
|
|
7
|
+
SelectContent,
|
|
8
|
+
SelectItem,
|
|
9
|
+
SelectTrigger,
|
|
10
|
+
SelectValue,
|
|
11
|
+
} from '@shipfox/react-ui/select';
|
|
6
12
|
import {toast} from '@shipfox/react-ui/toast';
|
|
7
13
|
import {useNavigate} from '@tanstack/react-router';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
type JobExecution,
|
|
11
|
-
resolveJobExecution,
|
|
12
|
-
type StepSourceLocation,
|
|
13
|
-
type WorkflowRunRerunMode,
|
|
14
|
-
} from '#core/workflow-run.js';
|
|
14
|
+
import {type ReactNode, useEffect} from 'react';
|
|
15
|
+
import type {Job, WorkflowRunRerunMode} from '#core/workflow-run.js';
|
|
15
16
|
import type {RunAnnotationSummary} from '#core/workflow-run-tabs.js';
|
|
16
|
-
import {
|
|
17
|
-
type WorkflowRunSelectionInput,
|
|
18
|
-
withoutWorkflowRunSelectionSearch,
|
|
19
|
-
} from '#core/workflow-run-url-state.js';
|
|
17
|
+
import {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';
|
|
20
18
|
import {
|
|
21
19
|
useCancelWorkflowRunMutation,
|
|
22
20
|
useRerunWorkflowRunMutation,
|
|
23
21
|
useWorkflowRunAttemptQuery,
|
|
24
22
|
} from '#hooks/api/workflow-runs.js';
|
|
25
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
type WorkflowJobSearch,
|
|
25
|
+
type WorkflowRunsSearch,
|
|
26
|
+
type WorkflowRunTab,
|
|
27
|
+
workflowJobSearchParams,
|
|
28
|
+
workflowRunSearchParams,
|
|
29
|
+
} from '#routes/inputs.js';
|
|
26
30
|
import {JobGraph} from '../job-graph/index.js';
|
|
27
31
|
import type {JobGraphSelectionSource} from '../job-graph/types.js';
|
|
28
32
|
import {WorkflowRunSummary} from '../workflow-run-summary/index.js';
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
RunJobsList,
|
|
33
|
-
RunTabStrip,
|
|
34
|
-
} from '../workflow-run-tabs/index.js';
|
|
35
|
-
import {WorkflowSourceContent, WorkflowSourcePanel} from '../workflow-source-panel/index.js';
|
|
33
|
+
import {RunAnnotationSummaryLine, RunAnnotationsEmpty} from '../workflow-run-tabs/index.js';
|
|
34
|
+
import {WorkflowSourceContent} from '../workflow-source-panel/index.js';
|
|
35
|
+
import {RunWorkspaceNav} from './run-workspace-nav.js';
|
|
36
36
|
import {resolveWorkflowRunSelection} from './workflow-run-selection.js';
|
|
37
37
|
import {
|
|
38
38
|
WorkflowRunContentSkeleton,
|
|
@@ -41,39 +41,43 @@ import {
|
|
|
41
41
|
WorkflowRunStaleError,
|
|
42
42
|
} from './workflow-run-states.js';
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
stepId: string;
|
|
46
|
-
location: StepSourceLocation;
|
|
47
|
-
}
|
|
44
|
+
type RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;
|
|
48
45
|
|
|
49
46
|
export interface WorkflowRunViewProps {
|
|
50
47
|
projectId: string;
|
|
51
48
|
workspaceSlug?: string | undefined;
|
|
52
49
|
projectSlug?: string | undefined;
|
|
53
50
|
workflowRunId?: string | undefined;
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
runAttempt?: number | undefined;
|
|
52
|
+
selection?: WorkflowRunsSearch | undefined;
|
|
56
53
|
tab?: WorkflowRunTab | undefined;
|
|
57
|
-
onTabChange?: ((tab: WorkflowRunTab) => void) | undefined;
|
|
58
54
|
annotationSummary?: RunAnnotationSummary | undefined;
|
|
55
|
+
activeJobId?: string | undefined;
|
|
56
|
+
jobSearch?: WorkflowJobSearch | undefined;
|
|
57
|
+
jobContent?: ReactNode | undefined;
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
61
|
+
* The persistent run workspace. Run-level sections and dedicated job routes share the same
|
|
62
|
+
* header and job index, while the all-jobs Summary always opens on the dependency graph.
|
|
64
63
|
*/
|
|
65
64
|
export function WorkflowRunView({
|
|
66
65
|
projectId,
|
|
67
66
|
workspaceSlug,
|
|
68
67
|
projectSlug,
|
|
69
68
|
workflowRunId,
|
|
69
|
+
runAttempt,
|
|
70
70
|
selection,
|
|
71
|
-
onSelectionChange,
|
|
72
71
|
tab,
|
|
73
|
-
onTabChange,
|
|
74
72
|
annotationSummary,
|
|
73
|
+
activeJobId,
|
|
74
|
+
jobSearch,
|
|
75
|
+
jobContent,
|
|
75
76
|
}: WorkflowRunViewProps) {
|
|
76
|
-
const runQuery = useWorkflowRunAttemptQuery({
|
|
77
|
+
const runQuery = useWorkflowRunAttemptQuery({
|
|
78
|
+
workflowRunId,
|
|
79
|
+
runAttempt: selection?.runAttempt ?? runAttempt,
|
|
80
|
+
});
|
|
77
81
|
const rerunMutation = useRerunWorkflowRunMutation(projectId);
|
|
78
82
|
|
|
79
83
|
return (
|
|
@@ -84,11 +88,13 @@ export function WorkflowRunView({
|
|
|
84
88
|
projectSlug={projectSlug}
|
|
85
89
|
query={runQuery}
|
|
86
90
|
rerunMutation={rerunMutation}
|
|
91
|
+
runAttempt={runAttempt}
|
|
87
92
|
selection={selection}
|
|
88
|
-
onSelectionChange={onSelectionChange}
|
|
89
93
|
tab={tab}
|
|
90
|
-
onTabChange={onTabChange}
|
|
91
94
|
annotationSummary={annotationSummary}
|
|
95
|
+
activeJobId={activeJobId}
|
|
96
|
+
jobSearch={jobSearch}
|
|
97
|
+
jobContent={jobContent}
|
|
92
98
|
/>
|
|
93
99
|
</div>
|
|
94
100
|
</RelativeTimeProvider>
|
|
@@ -100,107 +106,80 @@ function RunViewContent({
|
|
|
100
106
|
projectSlug,
|
|
101
107
|
query,
|
|
102
108
|
rerunMutation,
|
|
109
|
+
runAttempt,
|
|
103
110
|
selection,
|
|
104
|
-
onSelectionChange,
|
|
105
111
|
tab,
|
|
106
|
-
onTabChange,
|
|
107
112
|
annotationSummary,
|
|
113
|
+
activeJobId,
|
|
114
|
+
jobSearch,
|
|
115
|
+
jobContent,
|
|
108
116
|
}: {
|
|
109
117
|
workspaceSlug: string | undefined;
|
|
110
118
|
projectSlug: string | undefined;
|
|
111
119
|
query: ReturnType<typeof useWorkflowRunAttemptQuery>;
|
|
112
120
|
rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
runAttempt: number | undefined;
|
|
122
|
+
selection: WorkflowRunsSearch | undefined;
|
|
115
123
|
tab: WorkflowRunTab | undefined;
|
|
116
|
-
onTabChange: ((tab: WorkflowRunTab) => void) | undefined;
|
|
117
124
|
annotationSummary: RunAnnotationSummary | undefined;
|
|
125
|
+
activeJobId: string | undefined;
|
|
126
|
+
jobSearch: WorkflowJobSearch | undefined;
|
|
127
|
+
jobContent: ReactNode | undefined;
|
|
118
128
|
}) {
|
|
119
129
|
const navigate = useNavigate();
|
|
120
|
-
const [selectedJobId, setSelectedJobId] = useState<string | undefined>();
|
|
121
|
-
const [selectedJobExecutionId, setSelectedJobExecutionId] = useState<string | undefined>();
|
|
122
|
-
const [localTab, setLocalTab] = useState<WorkflowRunTab>(tab ?? 'summary');
|
|
123
|
-
const [sourcePanelOpen, setSourcePanelOpen] = useState(false);
|
|
124
|
-
const [sourceFocus, setSourceFocus] = useState<WorkflowSourceFocus | null>(null);
|
|
125
|
-
const sourcePanelId = useId();
|
|
126
|
-
// The step Source button that last opened the panel, so Escape / Close returns focus to it.
|
|
127
|
-
const lastSourceTriggerRef = useRef<HTMLButtonElement | null>(null);
|
|
128
|
-
const selectionControlled = selection !== undefined;
|
|
129
|
-
const tabControlled = onTabChange !== undefined;
|
|
130
|
-
const activeTab = tabControlled ? (tab ?? 'summary') : localTab;
|
|
131
|
-
const sourceAvailable =
|
|
132
|
-
query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;
|
|
133
|
-
const cancelMutation = useCancelWorkflowRunMutation(query.data);
|
|
134
|
-
|
|
135
|
-
useEffect(() => {
|
|
136
|
-
if (!sourceAvailable) {
|
|
137
|
-
setSourcePanelOpen(false);
|
|
138
|
-
setSourceFocus(null);
|
|
139
|
-
}
|
|
140
|
-
}, [sourceAvailable]);
|
|
141
|
-
|
|
142
|
-
// If a refetch drops the focused step or its location, close the panel so it never points at
|
|
143
|
-
// an unmounted Source button.
|
|
144
|
-
useEffect(() => {
|
|
145
|
-
if (!sourceFocus) return;
|
|
146
|
-
const stillLocated = query.data?.jobs.some((job) =>
|
|
147
|
-
job.jobExecutions.some((execution) =>
|
|
148
|
-
execution.steps.some((step) => step.id === sourceFocus.stepId && step.sourceLocation),
|
|
149
|
-
),
|
|
150
|
-
);
|
|
151
|
-
if (!stillLocated) {
|
|
152
|
-
setSourceFocus(null);
|
|
153
|
-
setSourcePanelOpen(false);
|
|
154
|
-
}
|
|
155
|
-
}, [sourceFocus, query.data]);
|
|
156
|
-
|
|
157
130
|
const runData = query.data;
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
? resolveWorkflowRunSelection({
|
|
161
|
-
run: runData,
|
|
162
|
-
selection: selection as WorkflowRunSelectionInput,
|
|
163
|
-
})
|
|
164
|
-
: undefined;
|
|
165
|
-
const hasExplicitJobSelection = Boolean(
|
|
166
|
-
selection?.jobId || selection?.jobExecutionId || selection?.stepId || selection?.stepAttemptId,
|
|
167
|
-
);
|
|
168
|
-
const selectedJob = selectionControlled
|
|
169
|
-
? hasExplicitJobSelection
|
|
170
|
-
? resolvedSelection?.job
|
|
171
|
-
: undefined
|
|
172
|
-
: (runData?.jobs.find((job) => job.id === selectedJobId) ?? runData?.jobs.at(0));
|
|
173
|
-
const selectedJobExecution = selectionControlled
|
|
174
|
-
? resolvedSelection?.jobExecution
|
|
175
|
-
: selectedJob
|
|
176
|
-
? resolveJobExecution(selectedJob, selectedJobExecutionId)
|
|
177
|
-
: undefined;
|
|
178
|
-
const selectedAttemptId = selectionControlled
|
|
179
|
-
? (resolvedSelection?.selectedAttemptId ?? null)
|
|
180
|
-
: undefined;
|
|
181
|
-
// Explicit per-step focus wins; fall back to the URL-selected step so deep links still
|
|
182
|
-
// pre-highlight source in the Source tab.
|
|
183
|
-
const highlightedLineRange =
|
|
184
|
-
sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;
|
|
131
|
+
const activeSection = runWorkspaceSection(tab);
|
|
132
|
+
const cancelMutation = useCancelWorkflowRunMutation(runData);
|
|
185
133
|
const sourceSnapshot = runData?.sourceSnapshot ?? null;
|
|
134
|
+
const resolvedSelection =
|
|
135
|
+
runData && selection ? resolveWorkflowRunSelection({run: runData, selection}) : undefined;
|
|
136
|
+
const highlightedLineRange = resolvedSelection?.step?.sourceLocation ?? null;
|
|
186
137
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
const hasLegacyJobSelection = Boolean(
|
|
140
|
+
selection?.jobId ||
|
|
141
|
+
selection?.jobExecutionId ||
|
|
142
|
+
selection?.stepId ||
|
|
143
|
+
selection?.stepAttemptId,
|
|
144
|
+
);
|
|
145
|
+
if (
|
|
146
|
+
activeJobId ||
|
|
147
|
+
activeSection !== 'summary' ||
|
|
148
|
+
!hasLegacyJobSelection ||
|
|
149
|
+
!resolvedSelection?.job ||
|
|
150
|
+
!runData ||
|
|
151
|
+
!workspaceSlug ||
|
|
152
|
+
!projectSlug
|
|
153
|
+
) {
|
|
154
|
+
return;
|
|
197
155
|
}
|
|
198
|
-
}
|
|
199
156
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
157
|
+
void navigate({
|
|
158
|
+
to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',
|
|
159
|
+
params: {
|
|
160
|
+
workspaceSlug,
|
|
161
|
+
projectSlug,
|
|
162
|
+
workflowRunId: runData.id,
|
|
163
|
+
jobId: resolvedSelection.job.id,
|
|
164
|
+
},
|
|
165
|
+
search: workflowJobSearchParams({
|
|
166
|
+
jobExecutionId: selection?.jobExecutionId,
|
|
167
|
+
stepId: selection?.stepId,
|
|
168
|
+
stepAttemptId: selection?.stepAttemptId,
|
|
169
|
+
runAttempt: selection?.runAttempt,
|
|
170
|
+
}) as never,
|
|
171
|
+
replace: true,
|
|
172
|
+
});
|
|
173
|
+
}, [
|
|
174
|
+
activeJobId,
|
|
175
|
+
activeSection,
|
|
176
|
+
navigate,
|
|
177
|
+
projectSlug,
|
|
178
|
+
resolvedSelection?.job,
|
|
179
|
+
runData,
|
|
180
|
+
selection,
|
|
181
|
+
workspaceSlug,
|
|
182
|
+
]);
|
|
204
183
|
|
|
205
184
|
async function rerun(mode: WorkflowRunRerunMode) {
|
|
206
185
|
if (!runData || !workspaceSlug || !projectSlug) {
|
|
@@ -212,11 +191,7 @@ function RunViewContent({
|
|
|
212
191
|
toast.success('Re-run started');
|
|
213
192
|
await navigate({
|
|
214
193
|
to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
|
|
215
|
-
params: {
|
|
216
|
-
workspaceSlug,
|
|
217
|
-
projectSlug,
|
|
218
|
-
workflowRunId: run.id,
|
|
219
|
-
},
|
|
194
|
+
params: {workspaceSlug, projectSlug, workflowRunId: run.id},
|
|
220
195
|
search: ((previous: Record<string, unknown>) =>
|
|
221
196
|
withoutWorkflowRunSelectionSearch(previous)) as never,
|
|
222
197
|
});
|
|
@@ -225,91 +200,42 @@ function RunViewContent({
|
|
|
225
200
|
}
|
|
226
201
|
}
|
|
227
202
|
|
|
228
|
-
function
|
|
229
|
-
if (source
|
|
230
|
-
if (!selectionControlled) {
|
|
231
|
-
setSelectedJobId(jobId);
|
|
232
|
-
setSelectedJobExecutionId(undefined);
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
// Keyboard roving focus is an interaction within the Summary graph. Keep that panel mounted
|
|
236
|
-
// and let JobGraphView own the visual selection so focus is not moved to a different tab.
|
|
237
|
-
if (source === 'keyboard') return;
|
|
238
|
-
|
|
239
|
-
onSelectionChange?.(
|
|
240
|
-
jobId ? {jobId, runAttempt: selection?.runAttempt} : {runAttempt: selection?.runAttempt},
|
|
241
|
-
);
|
|
242
|
-
}
|
|
203
|
+
function selectGraphJob(jobId: string | undefined, source: JobGraphSelectionSource = 'pointer') {
|
|
204
|
+
if (source !== 'pointer' || !jobId || !runData || !workspaceSlug || !projectSlug) return;
|
|
243
205
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
onSelectionChange?.({
|
|
252
|
-
jobId: selectedJob.id,
|
|
253
|
-
jobExecutionId,
|
|
254
|
-
runAttempt: selection?.runAttempt,
|
|
206
|
+
void navigate({
|
|
207
|
+
to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',
|
|
208
|
+
params: {workspaceSlug, projectSlug, workflowRunId: runData.id, jobId},
|
|
209
|
+
search: workflowJobSearchParams({
|
|
210
|
+
runAttempt: selection?.runAttempt ?? runAttempt ?? runData.runAttempt.attempt,
|
|
211
|
+
}) as never,
|
|
255
212
|
});
|
|
256
213
|
}
|
|
257
214
|
|
|
258
|
-
function
|
|
259
|
-
if (!
|
|
215
|
+
function selectAnnotationJob(jobId: string | undefined) {
|
|
216
|
+
if (!runData || !workspaceSlug || !projectSlug) return;
|
|
217
|
+
const nextSearch: WorkflowRunsSearch = {...selection, tab: 'annotations'};
|
|
218
|
+
if (jobId) nextSearch.jobId = jobId;
|
|
219
|
+
else delete nextSearch.jobId;
|
|
220
|
+
delete nextSearch.jobExecutionId;
|
|
221
|
+
delete nextSearch.stepId;
|
|
222
|
+
delete nextSearch.stepAttemptId;
|
|
260
223
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
runAttempt: selection?.runAttempt,
|
|
266
|
-
});
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const match = findAttemptSelection(selectedJobExecution, attemptId);
|
|
271
|
-
if (!match) return;
|
|
272
|
-
|
|
273
|
-
onSelectionChange?.({
|
|
274
|
-
jobId: selectedJob.id,
|
|
275
|
-
jobExecutionId: selectedJobExecution.id,
|
|
276
|
-
stepId: match.stepId,
|
|
277
|
-
stepAttemptId: match.attemptId,
|
|
278
|
-
runAttempt: selection?.runAttempt,
|
|
224
|
+
void navigate({
|
|
225
|
+
to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
|
|
226
|
+
params: {workspaceSlug, projectSlug, workflowRunId: runData.id},
|
|
227
|
+
search: workflowRunSearchParams(nextSearch, nextSearch) as never,
|
|
279
228
|
});
|
|
280
229
|
}
|
|
281
230
|
|
|
282
|
-
function openStepSource(
|
|
283
|
-
stepId: string,
|
|
284
|
-
location: StepSourceLocation,
|
|
285
|
-
trigger: HTMLButtonElement | null,
|
|
286
|
-
) {
|
|
287
|
-
setSourceFocus({stepId, location});
|
|
288
|
-
lastSourceTriggerRef.current = trigger;
|
|
289
|
-
setSourcePanelOpen(true);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function closeSourcePanel() {
|
|
293
|
-
const trigger = lastSourceTriggerRef.current;
|
|
294
|
-
setSourcePanelOpen(false);
|
|
295
|
-
// Defer so focus lands after the panel unmounts; clear the focus only after focusing so the
|
|
296
|
-
// opener button is still expanded (force-visible) on return.
|
|
297
|
-
window.setTimeout(() => {
|
|
298
|
-
if (trigger?.isConnected) trigger.focus();
|
|
299
|
-
setSourceFocus(null);
|
|
300
|
-
}, 0);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
231
|
function cancelRun() {
|
|
304
232
|
cancelMutation.mutate(undefined, {
|
|
305
|
-
onError: (error) =>
|
|
306
|
-
toast.error(cancelErrorMessage(error));
|
|
307
|
-
},
|
|
233
|
+
onError: (error) => toast.error(cancelErrorMessage(error)),
|
|
308
234
|
});
|
|
309
235
|
}
|
|
310
236
|
|
|
311
237
|
const fatalError = query.isError && runData === undefined;
|
|
312
|
-
const
|
|
238
|
+
const loadingOrError = fatalError ? (
|
|
313
239
|
query.error instanceof ApiError && query.error.status === 404 ? (
|
|
314
240
|
<WorkflowRunNotFound />
|
|
315
241
|
) : (
|
|
@@ -320,146 +246,229 @@ function RunViewContent({
|
|
|
320
246
|
) : null;
|
|
321
247
|
|
|
322
248
|
return (
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
249
|
+
<div className="flex min-h-0 min-w-0 flex-1 flex-col">
|
|
250
|
+
{runData ? (
|
|
251
|
+
<WorkflowRunSummary
|
|
252
|
+
workspaceSlug={workspaceSlug}
|
|
253
|
+
projectSlug={projectSlug}
|
|
254
|
+
run={runData}
|
|
255
|
+
cancelling={cancelMutation.isPending}
|
|
256
|
+
onCancel={cancelRun}
|
|
257
|
+
rerunPending={rerunMutation.isPending}
|
|
258
|
+
onRerun={(mode) => void rerun(mode)}
|
|
259
|
+
latestAttempt={runData.latestAttempt}
|
|
260
|
+
/>
|
|
261
|
+
) : (
|
|
262
|
+
<WorkflowRunSkeleton />
|
|
263
|
+
)}
|
|
264
|
+
{runData && query.isError ? <WorkflowRunStaleError query={query} /> : null}
|
|
265
|
+
|
|
266
|
+
<div
|
|
267
|
+
data-run-workspace-layout
|
|
268
|
+
className="flex min-h-0 min-w-0 flex-1 flex-col border-t border-border-neutral-base min-[768px]:flex-row"
|
|
328
269
|
>
|
|
329
|
-
{runData ? (
|
|
330
|
-
<
|
|
270
|
+
{runData && workspaceSlug && projectSlug ? (
|
|
271
|
+
<RunWorkspaceNav
|
|
331
272
|
workspaceSlug={workspaceSlug}
|
|
332
273
|
projectSlug={projectSlug}
|
|
333
274
|
run={runData}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
latestAttempt={runData.latestAttempt}
|
|
275
|
+
activeSection={activeSection}
|
|
276
|
+
currentJobId={activeJobId}
|
|
277
|
+
jobSearch={jobSearch}
|
|
278
|
+
annotationSummary={annotationSummary}
|
|
339
279
|
/>
|
|
340
280
|
) : (
|
|
341
|
-
<
|
|
281
|
+
<RunWorkspaceNavSkeleton />
|
|
342
282
|
)}
|
|
343
|
-
|
|
344
|
-
<
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
{runData ? (
|
|
371
|
-
<RunJobsList
|
|
372
|
-
jobs={runData.jobs}
|
|
373
|
-
selectedJobId={selectedJob?.id}
|
|
374
|
-
onSelectedJobChange={selectJob}
|
|
375
|
-
workspaceSlug={workspaceSlug}
|
|
376
|
-
selectedJobExecution={selectedJobExecution}
|
|
377
|
-
selectedAttemptId={selectedAttemptId}
|
|
378
|
-
onSelectedJobExecutionChange={selectJobExecution}
|
|
379
|
-
onSelectedAttemptChange={selectionControlled ? selectAttempt : undefined}
|
|
380
|
-
sourcePanelId={sourcePanelId}
|
|
381
|
-
sourceAvailable={sourceAvailable}
|
|
382
|
-
focusedSourceStepId={sourceFocus?.stepId ?? null}
|
|
383
|
-
onOpenStepSource={openStepSource}
|
|
384
|
-
/>
|
|
385
|
-
) : (
|
|
386
|
-
tabState
|
|
387
|
-
)}
|
|
388
|
-
</TabsContent>
|
|
389
|
-
<TabsContent
|
|
390
|
-
value="annotations"
|
|
391
|
-
tabIndex={-1}
|
|
392
|
-
className="mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24 outline-none"
|
|
393
|
-
>
|
|
394
|
-
{runData ? (
|
|
395
|
-
<>
|
|
396
|
-
<RunAnnotationSummaryLine
|
|
397
|
-
summary={annotationSummary}
|
|
398
|
-
workspaceSlug={workspaceSlug}
|
|
399
|
-
projectSlug={projectSlug}
|
|
400
|
-
workflowRunId={runData.id}
|
|
401
|
-
search={selection}
|
|
402
|
-
/>
|
|
403
|
-
<RunAnnotationsEmpty />
|
|
404
|
-
</>
|
|
405
|
-
) : (
|
|
406
|
-
tabState
|
|
407
|
-
)}
|
|
408
|
-
</TabsContent>
|
|
409
|
-
<TabsContent
|
|
410
|
-
value="source"
|
|
411
|
-
keepMounted={sourceSnapshot !== null}
|
|
412
|
-
tabIndex={-1}
|
|
413
|
-
className="mx-auto flex min-h-full w-full max-w-[1120px] px-24 outline-none"
|
|
414
|
-
>
|
|
415
|
-
{runData ? (
|
|
416
|
-
sourceSnapshot ? (
|
|
417
|
-
<WorkflowSourceContent
|
|
418
|
-
source={sourceSnapshot}
|
|
419
|
-
highlightedLineRange={highlightedLineRange}
|
|
420
|
-
scrollHighlightedIntoView
|
|
421
|
-
/>
|
|
422
|
-
) : (
|
|
423
|
-
<EmptyState
|
|
424
|
-
className="min-h-160 w-full"
|
|
425
|
-
icon="fileDamageLine"
|
|
426
|
-
title="Source snapshot unavailable"
|
|
427
|
-
description={
|
|
428
|
-
runData.isTemporary
|
|
429
|
-
? 'Temporary runs do not capture workflow source.'
|
|
430
|
-
: 'This run was created before workflow source snapshots were captured.'
|
|
431
|
-
}
|
|
432
|
-
/>
|
|
433
|
-
)
|
|
434
|
-
) : (
|
|
435
|
-
tabState
|
|
436
|
-
)}
|
|
437
|
-
</TabsContent>
|
|
438
|
-
</TabsContents>
|
|
439
|
-
</Tabs>
|
|
440
|
-
<WorkflowSourcePanel
|
|
441
|
-
id={sourcePanelId}
|
|
442
|
-
source={sourceSnapshot}
|
|
443
|
-
open={sourcePanelOpen && sourceAvailable}
|
|
444
|
-
onClose={closeSourcePanel}
|
|
445
|
-
highlightedLineRange={highlightedLineRange}
|
|
446
|
-
scrollHighlightedIntoView
|
|
447
|
-
/>
|
|
448
|
-
</>
|
|
283
|
+
|
|
284
|
+
<div
|
|
285
|
+
data-run-workspace-content
|
|
286
|
+
className="flex min-h-0 min-w-0 flex-1 flex-col bg-background-neutral-base"
|
|
287
|
+
>
|
|
288
|
+
{runData && jobContent ? (
|
|
289
|
+
jobContent
|
|
290
|
+
) : runData ? (
|
|
291
|
+
<RunSectionContent
|
|
292
|
+
section={activeSection}
|
|
293
|
+
run={runData}
|
|
294
|
+
annotationSummary={annotationSummary}
|
|
295
|
+
workspaceSlug={workspaceSlug}
|
|
296
|
+
projectSlug={projectSlug}
|
|
297
|
+
selection={selection}
|
|
298
|
+
selectedJobId={selection?.jobId}
|
|
299
|
+
onSelectGraphJob={selectGraphJob}
|
|
300
|
+
onSelectAnnotationJob={selectAnnotationJob}
|
|
301
|
+
sourceSnapshot={sourceSnapshot}
|
|
302
|
+
highlightedLineRange={highlightedLineRange}
|
|
303
|
+
/>
|
|
304
|
+
) : (
|
|
305
|
+
<div className="min-h-0 flex-1 overflow-auto p-24">{loadingOrError}</div>
|
|
306
|
+
)}
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
449
310
|
);
|
|
450
311
|
}
|
|
451
312
|
|
|
313
|
+
function RunSectionContent({
|
|
314
|
+
section,
|
|
315
|
+
run,
|
|
316
|
+
annotationSummary,
|
|
317
|
+
workspaceSlug,
|
|
318
|
+
projectSlug,
|
|
319
|
+
selection,
|
|
320
|
+
selectedJobId,
|
|
321
|
+
onSelectGraphJob,
|
|
322
|
+
onSelectAnnotationJob,
|
|
323
|
+
sourceSnapshot,
|
|
324
|
+
highlightedLineRange,
|
|
325
|
+
}: {
|
|
326
|
+
section: RunWorkspaceSection;
|
|
327
|
+
run: NonNullable<ReturnType<typeof useWorkflowRunAttemptQuery>['data']>;
|
|
328
|
+
annotationSummary: RunAnnotationSummary | undefined;
|
|
329
|
+
workspaceSlug: string | undefined;
|
|
330
|
+
projectSlug: string | undefined;
|
|
331
|
+
selection: WorkflowRunsSearch | undefined;
|
|
332
|
+
selectedJobId: string | undefined;
|
|
333
|
+
onSelectGraphJob: (jobId: string | undefined, source?: JobGraphSelectionSource) => void;
|
|
334
|
+
onSelectAnnotationJob: (jobId: string | undefined) => void;
|
|
335
|
+
sourceSnapshot: NonNullable<
|
|
336
|
+
ReturnType<typeof useWorkflowRunAttemptQuery>['data']
|
|
337
|
+
>['sourceSnapshot'];
|
|
338
|
+
highlightedLineRange: NonNullable<
|
|
339
|
+
ReturnType<typeof useWorkflowRunAttemptQuery>['data']
|
|
340
|
+
>['jobs'][number]['jobExecutions'][number]['steps'][number]['sourceLocation'];
|
|
341
|
+
}) {
|
|
342
|
+
if (section === 'summary') {
|
|
343
|
+
return (
|
|
344
|
+
<section aria-label="All jobs summary" className="min-h-0 flex-1 overflow-auto pb-24 pt-16">
|
|
345
|
+
<div className="mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24">
|
|
346
|
+
<JobGraph
|
|
347
|
+
run={run}
|
|
348
|
+
selectedJobId={selectedJobId}
|
|
349
|
+
onSelectedJobChange={onSelectGraphJob}
|
|
350
|
+
className="min-h-160 overflow-hidden"
|
|
351
|
+
/>
|
|
352
|
+
</div>
|
|
353
|
+
</section>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (section === 'annotations') {
|
|
358
|
+
const selectedJob = run.jobs.find((job) => job.id === selectedJobId);
|
|
359
|
+
return (
|
|
360
|
+
<section aria-label="Run annotations" className="min-h-0 flex-1 overflow-auto pb-24 pt-16">
|
|
361
|
+
<div className="mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24">
|
|
362
|
+
<div className="flex flex-wrap items-center justify-between gap-8">
|
|
363
|
+
<RunAnnotationSummaryLine
|
|
364
|
+
summary={annotationSummary}
|
|
365
|
+
workspaceSlug={workspaceSlug}
|
|
366
|
+
projectSlug={projectSlug}
|
|
367
|
+
workflowRunId={run.id}
|
|
368
|
+
search={selection}
|
|
369
|
+
/>
|
|
370
|
+
<AnnotationJobFilter
|
|
371
|
+
jobs={run.jobs}
|
|
372
|
+
selectedJobId={selectedJob?.id}
|
|
373
|
+
onSelect={onSelectAnnotationJob}
|
|
374
|
+
/>
|
|
375
|
+
</div>
|
|
376
|
+
<RunAnnotationsEmpty jobName={selectedJob?.displayName} />
|
|
377
|
+
</div>
|
|
378
|
+
</section>
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return (
|
|
383
|
+
<section aria-label="Workflow source" className="min-h-0 flex-1 overflow-auto pb-24 pt-16">
|
|
384
|
+
<div className="mx-auto flex min-h-full w-full max-w-[1120px] px-24">
|
|
385
|
+
{sourceSnapshot ? (
|
|
386
|
+
<WorkflowSourceContent
|
|
387
|
+
source={sourceSnapshot}
|
|
388
|
+
highlightedLineRange={highlightedLineRange}
|
|
389
|
+
scrollHighlightedIntoView
|
|
390
|
+
/>
|
|
391
|
+
) : (
|
|
392
|
+
<EmptyState
|
|
393
|
+
className="min-h-160 w-full"
|
|
394
|
+
icon="fileDamageLine"
|
|
395
|
+
title="Source snapshot unavailable"
|
|
396
|
+
description={
|
|
397
|
+
run.isTemporary
|
|
398
|
+
? 'Temporary runs do not capture workflow source.'
|
|
399
|
+
: 'This run was created before workflow source snapshots were captured.'
|
|
400
|
+
}
|
|
401
|
+
/>
|
|
402
|
+
)}
|
|
403
|
+
</div>
|
|
404
|
+
</section>
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const ALL_ANNOTATION_JOBS = 'all-jobs';
|
|
409
|
+
|
|
410
|
+
function AnnotationJobFilter({
|
|
411
|
+
jobs,
|
|
412
|
+
selectedJobId,
|
|
413
|
+
onSelect,
|
|
414
|
+
}: {
|
|
415
|
+
jobs: Job[];
|
|
416
|
+
selectedJobId: string | undefined;
|
|
417
|
+
onSelect: (jobId: string | undefined) => void;
|
|
418
|
+
}) {
|
|
419
|
+
const sortedJobs = [...jobs].sort(
|
|
420
|
+
(left, right) => left.position - right.position || left.id.localeCompare(right.id),
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
return (
|
|
424
|
+
<Select
|
|
425
|
+
value={selectedJobId ?? ALL_ANNOTATION_JOBS}
|
|
426
|
+
onValueChange={(value) => onSelect(value === ALL_ANNOTATION_JOBS ? undefined : value)}
|
|
427
|
+
>
|
|
428
|
+
<SelectTrigger
|
|
429
|
+
size="small"
|
|
430
|
+
aria-label="Filter annotations by job"
|
|
431
|
+
className="w-full min-[480px]:w-240"
|
|
432
|
+
>
|
|
433
|
+
<SelectValue />
|
|
434
|
+
</SelectTrigger>
|
|
435
|
+
<SelectContent align="end">
|
|
436
|
+
<SelectItem value={ALL_ANNOTATION_JOBS}>All jobs</SelectItem>
|
|
437
|
+
{sortedJobs.map((job) => (
|
|
438
|
+
<SelectItem key={job.id} value={job.id}>
|
|
439
|
+
{job.displayName}
|
|
440
|
+
</SelectItem>
|
|
441
|
+
))}
|
|
442
|
+
</SelectContent>
|
|
443
|
+
</Select>
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function RunWorkspaceNavSkeleton() {
|
|
448
|
+
return (
|
|
449
|
+
<aside
|
|
450
|
+
aria-label="Loading run navigation"
|
|
451
|
+
className="hidden w-240 shrink-0 border-r border-border-neutral-base bg-background-neutral-background p-12 min-[768px]:block"
|
|
452
|
+
>
|
|
453
|
+
<div className="flex flex-col gap-16">
|
|
454
|
+
<div className="h-32 rounded-4 bg-background-components-subtle" />
|
|
455
|
+
<div className="flex flex-col gap-6">
|
|
456
|
+
<div className="h-16 w-64 rounded-4 bg-background-components-subtle" />
|
|
457
|
+
<div className="h-32 rounded-4 bg-background-components-subtle" />
|
|
458
|
+
<div className="h-32 rounded-4 bg-background-components-subtle" />
|
|
459
|
+
</div>
|
|
460
|
+
</div>
|
|
461
|
+
</aside>
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function runWorkspaceSection(tab: WorkflowRunTab | undefined): RunWorkspaceSection {
|
|
466
|
+
return tab === 'annotations' || tab === 'source' ? tab : 'summary';
|
|
467
|
+
}
|
|
468
|
+
|
|
452
469
|
function cancelErrorMessage(error: unknown): string {
|
|
453
470
|
if (error instanceof ApiError && error.code === 'run-already-finished') {
|
|
454
471
|
return 'This workflow run has already finished.';
|
|
455
472
|
}
|
|
456
473
|
return 'Could not cancel workflow run.';
|
|
457
474
|
}
|
|
458
|
-
|
|
459
|
-
function findAttemptSelection(jobExecution: JobExecution, attemptId: string) {
|
|
460
|
-
for (const step of jobExecution.steps) {
|
|
461
|
-
const attempt = step.attempts.find((candidate) => candidate.id === attemptId);
|
|
462
|
-
if (attempt) return {stepId: step.id, attemptId: attempt.id};
|
|
463
|
-
}
|
|
464
|
-
return undefined;
|
|
465
|
-
}
|