@shipfox/client-workflows 12.0.2 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/preview.tsx +3 -3
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +56 -0
- package/dist/components/job-graph/job-duration-format.d.ts +2 -2
- package/dist/components/job-graph/job-duration-format.d.ts.map +1 -1
- package/dist/components/job-graph/job-duration-format.js +4 -2
- package/dist/components/job-graph/job-duration-format.js.map +1 -1
- package/dist/components/job-graph/job-node.d.ts +1 -1
- package/dist/components/job-graph/job-node.d.ts.map +1 -1
- package/dist/components/job-graph/job-node.js +5 -4
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +2 -2
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts +21 -0
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -0
- package/dist/components/workflow-run-list/job-status-strip.js +185 -0
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -0
- package/dist/components/workflow-run-list/run-display.d.ts +27 -3
- package/dist/components/workflow-run-list/run-display.d.ts.map +1 -1
- package/dist/components/workflow-run-list/run-display.js +78 -7
- package/dist/components/workflow-run-list/run-display.js.map +1 -1
- package/dist/components/workflow-run-list/types.d.ts +23 -24
- package/dist/components/workflow-run-list/types.d.ts.map +1 -1
- package/dist/components/workflow-run-list/types.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts +22 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +68 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts +18 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js +236 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts +8 -4
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-content.js +40 -12
- package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts +29 -2
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +103 -44
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +60 -43
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.js +20 -8
- package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts +19 -9
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +179 -112
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-number-label.d.ts +15 -0
- package/dist/components/workflow-run-number-label.d.ts.map +1 -0
- package/dist/components/workflow-run-number-label.js +41 -0
- package/dist/components/workflow-run-number-label.js.map +1 -0
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts +3 -3
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +8 -7
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +3 -3
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +16 -7
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts +2 -2
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.js +4 -4
- package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +1 -1
- package/dist/components/workflow-run-view/job-card.d.ts +2 -2
- package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-card.js +51 -32
- package/dist/components/workflow-run-view/job-card.js.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.js +20 -8
- package/dist/components/workflow-run-view/job-execution-switcher.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts +3 -2
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +15 -11
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-status/status-visuals.d.ts +4 -4
- package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
- package/dist/components/workflow-status/status-visuals.js +7 -0
- package/dist/components/workflow-status/status-visuals.js.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -4
- package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.js +6 -4
- package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +10 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js +11 -0
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +4 -2
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js +8 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +52 -4
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +22 -3
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/workflow-run.d.ts +6 -6
- 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 +7 -7
- package/dist/feature.js +5 -5
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts +4 -3
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +31 -3
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/hooks/api/workflow-runs.d.ts +4 -4
- package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
- package/dist/hooks/api/workflow-runs.js +12 -4
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/pages/project-workflows-page.js +46 -1
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-detail-page.d.ts +11 -0
- package/dist/pages/workflow-run-detail-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-detail-page.js +30 -0
- package/dist/pages/workflow-run-detail-page.js.map +1 -0
- package/dist/pages/workflow-run-list-page.d.ts +10 -0
- package/dist/pages/workflow-run-list-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-list-page.js +43 -0
- package/dist/pages/workflow-run-list-page.js.map +1 -0
- package/dist/routes/inputs.d.ts +61 -5
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +150 -13
- package/dist/routes/inputs.js.map +1 -1
- package/dist/routes/project-route.d.ts +6 -0
- package/dist/routes/project-route.d.ts.map +1 -0
- package/dist/routes/project-route.js +25 -0
- package/dist/routes/project-route.js.map +1 -0
- package/dist/routes/run-detail.d.ts.map +1 -1
- package/dist/routes/run-detail.js +12 -7
- package/dist/routes/run-detail.js.map +1 -1
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +11 -6
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +6 -5
- package/dist/routes/workflows.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/components/job-graph/job-duration-format.ts +11 -2
- package/src/components/job-graph/job-graph-view.test.tsx +19 -8
- package/src/components/job-graph/job-graph.stories.tsx +23 -19
- package/src/components/job-graph/job-node.stories.tsx +10 -5
- package/src/components/job-graph/job-node.test.tsx +55 -10
- package/src/components/job-graph/job-node.tsx +16 -9
- package/src/components/step-list/step-list.stories.tsx +2 -3
- package/src/components/step-list/step-list.tsx +3 -2
- package/src/components/workflow-run-list/job-status-strip.tsx +189 -0
- package/src/components/workflow-run-list/run-display.test.ts +169 -30
- package/src/components/workflow-run-list/run-display.ts +116 -9
- package/src/components/workflow-run-list/types.ts +21 -17
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +100 -0
- package/src/components/workflow-run-list/workflow-run-filters.tsx +243 -0
- package/src/components/workflow-run-list/workflow-run-list-content.tsx +44 -16
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +128 -27
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +441 -104
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +73 -43
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +140 -20
- package/src/components/workflow-run-list/workflow-run-list.tsx +20 -10
- package/src/components/workflow-run-list/workflow-run-row.tsx +173 -106
- package/src/components/workflow-run-number-label.test.tsx +45 -0
- package/src/components/workflow-run-number-label.tsx +47 -0
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +7 -8
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +13 -12
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +6 -8
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +27 -2
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +19 -9
- package/src/components/workflow-run-view/agent-config-failure-callout.stories.tsx +2 -3
- package/src/components/workflow-run-view/agent-config-failure-callout.tsx +4 -4
- package/src/components/workflow-run-view/job-card.stories.tsx +11 -10
- package/src/components/workflow-run-view/job-card.test.tsx +115 -0
- package/src/components/workflow-run-view/job-card.tsx +74 -35
- package/src/components/workflow-run-view/job-execution-switcher.tsx +31 -9
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +88 -30
- package/src/components/workflow-run-view/workflow-run-view.tsx +24 -14
- package/src/components/workflow-status/status-visuals.test.ts +9 -0
- package/src/components/workflow-status/status-visuals.ts +6 -4
- package/src/components/workflow-status/workflow-status-icon.stories.tsx +10 -2
- package/src/components/workflow-status/workflow-status-icon.test.tsx +18 -0
- package/src/components/workflow-status/workflow-status-icon.tsx +17 -24
- package/src/core/entities/job-execution.ts +22 -0
- package/src/core/entities/job-status.test.ts +104 -0
- package/src/core/entities/job.ts +18 -3
- package/src/core/entities/workflow-run.ts +76 -7
- package/src/core/workflow-run.test.ts +24 -9
- package/src/core/workflow-run.ts +17 -2
- package/src/feature.ts +5 -5
- package/src/hooks/api/workflow-run-mapper.ts +30 -3
- package/src/hooks/api/workflow-runs.test.tsx +2 -0
- package/src/hooks/api/workflow-runs.ts +12 -7
- package/src/index.ts +7 -2
- package/src/page-harness-budget.test.ts +1 -1
- package/src/pages/project-workflows-page.test.tsx +59 -5
- package/src/pages/project-workflows-page.tsx +34 -1
- package/src/pages/workflow-run-detail-page.tsx +45 -0
- package/src/pages/workflow-run-list-page.tsx +65 -0
- package/src/pages/{workflow-run-page.test.tsx → workflow-run-pages.test.tsx} +211 -47
- package/src/routes/inputs.test.ts +193 -12
- package/src/routes/inputs.ts +170 -11
- package/src/routes/project-route.tsx +25 -0
- package/src/routes/run-detail.tsx +15 -8
- package/src/routes/runs.tsx +14 -7
- package/src/routes/workflows.tsx +5 -4
- package/test/fixtures/workflow-run.ts +76 -7
- package/test/pages.tsx +18 -8
- package/test/render.tsx +8 -2
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts +0 -10
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts.map +0 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.js +0 -55
- package/dist/components/workflow-run-list/workflow-run-list-header.js.map +0 -1
- package/dist/pages/workflow-run-first-time-use.d.ts +0 -6
- package/dist/pages/workflow-run-first-time-use.d.ts.map +0 -1
- package/dist/pages/workflow-run-first-time-use.js +0 -17
- package/dist/pages/workflow-run-first-time-use.js.map +0 -1
- package/dist/pages/workflow-run-page.d.ts +0 -10
- package/dist/pages/workflow-run-page.d.ts.map +0 -1
- package/dist/pages/workflow-run-page.js +0 -90
- package/dist/pages/workflow-run-page.js.map +0 -1
- package/src/components/workflow-run-list/workflow-run-list-header.tsx +0 -53
- package/src/pages/workflow-run-first-time-use.tsx +0 -17
- package/src/pages/workflow-run-page.tsx +0 -94
package/.storybook/preview.tsx
CHANGED
|
@@ -42,20 +42,20 @@ const withRelativeTime: Decorator = (Story) => (
|
|
|
42
42
|
</RelativeTimeProvider>
|
|
43
43
|
);
|
|
44
44
|
|
|
45
|
-
// Run rows render `<Link to="/
|
|
45
|
+
// Run rows render `<Link to="/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId">` and read
|
|
46
46
|
// `useParams`, both of which need a router in context. A memory router seeded at a
|
|
47
47
|
// matching path lets the rows render and resolve their hrefs without a real app shell.
|
|
48
48
|
const withRouter: Decorator = (Story) => {
|
|
49
49
|
const rootRoute = createRootRoute();
|
|
50
50
|
const runRoute = createRoute({
|
|
51
51
|
getParentRoute: () => rootRoute,
|
|
52
|
-
path: '/
|
|
52
|
+
path: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
|
|
53
53
|
component: () => <Story />,
|
|
54
54
|
});
|
|
55
55
|
const router = createRouter({
|
|
56
56
|
routeTree: rootRoute.addChildren([runRoute]),
|
|
57
57
|
history: createMemoryHistory({
|
|
58
|
-
initialEntries: ['/
|
|
58
|
+
initialEntries: ['/w/ws-demo/p/proj-demo/runs/run-1'],
|
|
59
59
|
}),
|
|
60
60
|
});
|
|
61
61
|
return <RouterProvider router={router} />;
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled:
|
|
2
|
+
Successfully compiled: 64 files with swc (399.5ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# @shipfox/client-workflows
|
|
2
2
|
|
|
3
|
+
## 13.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- e405e92: Move client routes to slug-based `/w/$workspaceSlug` and `/p/$projectSlug` URLs, enforce the new composition contract, and support bounded project-slug resolution.
|
|
8
|
+
- 13fa279: Replaces the exported workflow-run UUID short-id with API-provided workflow names and run numbers in client workflow metadata.
|
|
9
|
+
- 452e0f8: Separates workflow run list and detail into route-owned pages, removes the shared
|
|
10
|
+
`WorkflowRunPage` export, and exposes older run pages for filtering.
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- dea1ffd: Expose normalized repository references on listening execution events.
|
|
15
|
+
- 54c820e: Rebuild the run list as a full-width page: 44px rows carrying status, name, trigger, branch, commit, a per-run job status strip, duration, and time, with URL-backed search, status, branch, actor, event, and date filters.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- b5bb0c5: Derives one activity-aware workflow status across graph, execution, and job detail surfaces.
|
|
20
|
+
- a2d684e: Displays resolved job execution names in workflow run job card titles.
|
|
21
|
+
- 5d2c9cf: Carry checkout steps from workflow normalization through step materialization and surface their setup error category.
|
|
22
|
+
- f78740d: Remove Unicode dash punctuation from package prose and source comments.
|
|
23
|
+
- 9fdd5e4: Persist definition validation warnings from repository syncs and surface them on the workflow page without changing sync success or run creation behavior.
|
|
24
|
+
- edb4a18: Present resolved workflow run and job execution names across workflow run surfaces.
|
|
25
|
+
- Updated dependencies [ee2ce67]
|
|
26
|
+
- Updated dependencies [5d2c9cf]
|
|
27
|
+
- Updated dependencies [e405e92]
|
|
28
|
+
- Updated dependencies [f7939c7]
|
|
29
|
+
- Updated dependencies [89f2c18]
|
|
30
|
+
- Updated dependencies [f78740d]
|
|
31
|
+
- Updated dependencies [9e1d599]
|
|
32
|
+
- Updated dependencies [dea1ffd]
|
|
33
|
+
- Updated dependencies [adf07e7]
|
|
34
|
+
- Updated dependencies [3f781ee]
|
|
35
|
+
- Updated dependencies [9969937]
|
|
36
|
+
- Updated dependencies [9fdd5e4]
|
|
37
|
+
- Updated dependencies [285fff2]
|
|
38
|
+
- Updated dependencies [e44a279]
|
|
39
|
+
- Updated dependencies [3c73365]
|
|
40
|
+
- Updated dependencies [4eb18b8]
|
|
41
|
+
- Updated dependencies [54c820e]
|
|
42
|
+
- Updated dependencies [54c820e]
|
|
43
|
+
- Updated dependencies [35a42bd]
|
|
44
|
+
- Updated dependencies [6adc228]
|
|
45
|
+
- Updated dependencies [d77baaa]
|
|
46
|
+
- Updated dependencies [032d316]
|
|
47
|
+
- Updated dependencies [c2a8e54]
|
|
48
|
+
- Updated dependencies [cb0abfa]
|
|
49
|
+
- Updated dependencies [e1efaee]
|
|
50
|
+
- @shipfox/api-definitions-dto@12.0.0
|
|
51
|
+
- @shipfox/api-workflows-dto@12.0.0
|
|
52
|
+
- @shipfox/client-projects@13.0.0
|
|
53
|
+
- @shipfox/client-shell@13.0.0
|
|
54
|
+
- @shipfox/client-triggers@13.0.0
|
|
55
|
+
- @shipfox/client-logs@13.0.0
|
|
56
|
+
- @shipfox/client-ui@13.0.0
|
|
57
|
+
- @shipfox/react-ui@0.4.0
|
|
58
|
+
|
|
3
59
|
## 12.0.2
|
|
4
60
|
|
|
5
61
|
### Patch Changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { JobDisplayDuration, JobExecutionTime } from '#core/workflow-run.js';
|
|
1
|
+
import type { JobDisplayDuration, JobExecutionDisplayStatus, JobExecutionTime } from '#core/workflow-run.js';
|
|
2
2
|
export declare function formatJobExecutionTimeLabel(time: JobExecutionTime): string;
|
|
3
|
-
export declare function formatJobDurationAccessibleLabel(duration: JobDisplayDuration | null): string | undefined;
|
|
3
|
+
export declare function formatJobDurationAccessibleLabel(duration: JobDisplayDuration | null, displayStatus?: JobExecutionDisplayStatus): string | undefined;
|
|
4
4
|
//# sourceMappingURL=job-duration-format.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-duration-format.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-duration-format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"job-duration-format.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-duration-format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,kBAAkB,EAClB,yBAAyB,EACzB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAE/B,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAW1E;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,kBAAkB,GAAG,IAAI,EACnC,aAAa,CAAC,EAAE,yBAAyB,GACxC,MAAM,GAAG,SAAS,CAkBpB"}
|
|
@@ -12,14 +12,16 @@ export function formatJobExecutionTimeLabel(time) {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
export function formatJobDurationAccessibleLabel(duration) {
|
|
15
|
+
export function formatJobDurationAccessibleLabel(duration, displayStatus) {
|
|
16
16
|
if (duration === null) return undefined;
|
|
17
17
|
const label = formatJobExecutionTimeLabel(duration);
|
|
18
18
|
switch(duration.kind){
|
|
19
19
|
case 'queue':
|
|
20
20
|
return duration.state === 'live' ? `queueing ${label}` : `queued ${label}`;
|
|
21
21
|
case 'run':
|
|
22
|
-
|
|
22
|
+
if (duration.state !== 'live') return `ran ${label}`;
|
|
23
|
+
if (displayStatus === 'running') return `running ${label}`;
|
|
24
|
+
return displayStatus === 'pending' ? `runner assigned ${label}` : `execution elapsed ${label}`;
|
|
23
25
|
default:
|
|
24
26
|
{
|
|
25
27
|
const exhaustive = duration;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-graph/job-duration-format.ts"],"sourcesContent":["import {humanDuration} from '@shipfox/react-ui/utils';\nimport type {JobDisplayDuration
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-graph/job-duration-format.ts"],"sourcesContent":["import {humanDuration} from '@shipfox/react-ui/utils';\nimport type {\n JobDisplayDuration,\n JobExecutionDisplayStatus,\n JobExecutionTime,\n} from '#core/workflow-run.js';\n\nexport function formatJobExecutionTimeLabel(time: JobExecutionTime): string {\n switch (time.state) {\n case 'live':\n return humanDuration(time.fromIso);\n case 'fixed':\n return formatFixedDurationLabel(time.elapsed);\n default: {\n const exhaustive: never = time;\n return exhaustive;\n }\n }\n}\n\nexport function formatJobDurationAccessibleLabel(\n duration: JobDisplayDuration | null,\n displayStatus?: JobExecutionDisplayStatus,\n): string | undefined {\n if (duration === null) return undefined;\n\n const label = formatJobExecutionTimeLabel(duration);\n switch (duration.kind) {\n case 'queue':\n return duration.state === 'live' ? `queueing ${label}` : `queued ${label}`;\n case 'run':\n if (duration.state !== 'live') return `ran ${label}`;\n if (displayStatus === 'running') return `running ${label}`;\n return displayStatus === 'pending'\n ? `runner assigned ${label}`\n : `execution elapsed ${label}`;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nfunction formatFixedDurationLabel({\n years = 0,\n months = 0,\n weeks = 0,\n days = 0,\n hours = 0,\n minutes = 0,\n seconds = 0,\n}: Extract<JobExecutionTime, {state: 'fixed'}>['elapsed']): string {\n const totalDays = years * 365 + months * 30 + weeks * 7 + days;\n const totalHours = totalDays * 24 + hours;\n\n if (totalHours > 0) return `${totalHours}h ${pad2(minutes)}m`;\n if (minutes > 0) return `${minutes}m ${pad2(seconds)}s`;\n return `${seconds}s`;\n}\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, '0');\n}\n"],"names":["humanDuration","formatJobExecutionTimeLabel","time","state","fromIso","formatFixedDurationLabel","elapsed","exhaustive","formatJobDurationAccessibleLabel","duration","displayStatus","undefined","label","kind","years","months","weeks","days","hours","minutes","seconds","totalDays","totalHours","pad2","value","toString","padStart"],"mappings":"AAAA,SAAQA,aAAa,QAAO,0BAA0B;AAOtD,OAAO,SAASC,4BAA4BC,IAAsB;IAChE,OAAQA,KAAKC,KAAK;QAChB,KAAK;YACH,OAAOH,cAAcE,KAAKE,OAAO;QACnC,KAAK;YACH,OAAOC,yBAAyBH,KAAKI,OAAO;QAC9C;YAAS;gBACP,MAAMC,aAAoBL;gBAC1B,OAAOK;YACT;IACF;AACF;AAEA,OAAO,SAASC,iCACdC,QAAmC,EACnCC,aAAyC;IAEzC,IAAID,aAAa,MAAM,OAAOE;IAE9B,MAAMC,QAAQX,4BAA4BQ;IAC1C,OAAQA,SAASI,IAAI;QACnB,KAAK;YACH,OAAOJ,SAASN,KAAK,KAAK,SAAS,CAAC,SAAS,EAAES,OAAO,GAAG,CAAC,OAAO,EAAEA,OAAO;QAC5E,KAAK;YACH,IAAIH,SAASN,KAAK,KAAK,QAAQ,OAAO,CAAC,IAAI,EAAES,OAAO;YACpD,IAAIF,kBAAkB,WAAW,OAAO,CAAC,QAAQ,EAAEE,OAAO;YAC1D,OAAOF,kBAAkB,YACrB,CAAC,gBAAgB,EAAEE,OAAO,GAC1B,CAAC,kBAAkB,EAAEA,OAAO;QAClC;YAAS;gBACP,MAAML,aAAoBE;gBAC1B,OAAOF;YACT;IACF;AACF;AAEA,SAASF,yBAAyB,EAChCS,QAAQ,CAAC,EACTC,SAAS,CAAC,EACVC,QAAQ,CAAC,EACTC,OAAO,CAAC,EACRC,QAAQ,CAAC,EACTC,UAAU,CAAC,EACXC,UAAU,CAAC,EAC4C;IACvD,MAAMC,YAAYP,QAAQ,MAAMC,SAAS,KAAKC,QAAQ,IAAIC;IAC1D,MAAMK,aAAaD,YAAY,KAAKH;IAEpC,IAAII,aAAa,GAAG,OAAO,GAAGA,WAAW,EAAE,EAAEC,KAAKJ,SAAS,CAAC,CAAC;IAC7D,IAAIA,UAAU,GAAG,OAAO,GAAGA,QAAQ,EAAE,EAAEI,KAAKH,SAAS,CAAC,CAAC;IACvD,OAAO,GAAGA,QAAQ,CAAC,CAAC;AACtB;AAEA,SAASG,KAAKC,KAAa;IACzB,OAAOA,MAAMC,QAAQ,GAAGC,QAAQ,CAAC,GAAG;AACtC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { KeyboardEventHandler, Ref } from 'react';
|
|
2
|
-
import type
|
|
2
|
+
import { type WorkflowRunDetail } from '#core/workflow-run.js';
|
|
3
3
|
import type { JobGraphNode } from './graph-model.js';
|
|
4
4
|
export declare function TriggerNode({ trigger, }: {
|
|
5
5
|
trigger: Pick<WorkflowRunDetail, 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-node.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-node.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,oBAAoB,EAAE,GAAG,EAAC,MAAM,OAAO,CAAC;AAGrD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"job-node.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-node.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,oBAAoB,EAAE,GAAG,EAAC,MAAM,OAAO,CAAC;AAGrD,OAAO,EAML,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AAMnD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,GACR,EAAE;IACD,OAAO,EAAE,IAAI,CACX,iBAAiB,EACjB,qBAAqB,GAAG,cAAc,GAAG,iBAAiB,GAAG,eAAe,CAC7E,CAAC;CACH,+BA0BA;AAED,wBAAgB,OAAO,CAAC,EACtB,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,UAAU,EACV,GAAG,GACJ,EAAE;IACD,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;IACnD,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,GAAG,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC;CAC9B,+BAoDA"}
|
|
@@ -9,6 +9,7 @@ import { Code } from '@shipfox/react-ui/typography';
|
|
|
9
9
|
import { cn } from '@shipfox/react-ui/utils';
|
|
10
10
|
import { getWorkflowStatusVisual } from '#components/workflow-status/status-visuals.js';
|
|
11
11
|
import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-icon.js';
|
|
12
|
+
import { defaultJobExecution, deriveJobDisplayStatus, deriveJobExecutionDisplayStatus } from '#core/workflow-run.js';
|
|
12
13
|
import { formatJobDurationAccessibleLabel } from './job-duration-format.js';
|
|
13
14
|
import { JobDurationLabel } from './job-duration-label.js';
|
|
14
15
|
const TRIGGER_SIZE = 36;
|
|
@@ -45,12 +46,13 @@ export function TriggerNode({ trigger }) {
|
|
|
45
46
|
}
|
|
46
47
|
export function JobNode({ node, selected, onSelect, onKeyDown, onHoverStart, onHoverEnd, ref }) {
|
|
47
48
|
useTimeTick();
|
|
48
|
-
const status = node
|
|
49
|
+
const status = deriveJobDisplayStatus(node);
|
|
50
|
+
const execution = defaultJobExecution(node);
|
|
49
51
|
const visual = getWorkflowStatusVisual(status);
|
|
50
52
|
const accessibleLabel = [
|
|
51
53
|
node.displayName,
|
|
52
54
|
visual.label,
|
|
53
|
-
formatJobDurationAccessibleLabel(node.displayDuration),
|
|
55
|
+
formatJobDurationAccessibleLabel(node.displayDuration, execution ? deriveJobExecutionDisplayStatus(execution) : undefined),
|
|
54
56
|
node.executionCountVisible ? executionCountAccessibleLabel(node.jobExecutions.length) : undefined,
|
|
55
57
|
node.carriedOver ? 'reused' : undefined
|
|
56
58
|
].filter((part)=>Boolean(part)).join(', ');
|
|
@@ -75,7 +77,6 @@ export function JobNode({ node, selected, onSelect, onKeyDown, onHoverStart, onH
|
|
|
75
77
|
children: [
|
|
76
78
|
/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
77
79
|
status: status,
|
|
78
|
-
jobMode: node.mode,
|
|
79
80
|
size: 14
|
|
80
81
|
}),
|
|
81
82
|
/*#__PURE__*/ _jsx(JobLabel, {
|
|
@@ -183,7 +184,7 @@ const EXECUTION_STATUSES = [
|
|
|
183
184
|
];
|
|
184
185
|
function executionStatusCounts(executions) {
|
|
185
186
|
return executions.reduce((counts, execution)=>{
|
|
186
|
-
counts[execution
|
|
187
|
+
counts[deriveJobExecutionDisplayStatus(execution)] += 1;
|
|
187
188
|
return counts;
|
|
188
189
|
}, {
|
|
189
190
|
pending: 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-graph/job-node.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {KeyboardEventHandler, Ref} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport type {JobExecution, JobExecutionStatus, WorkflowRunDetail} from '#core/workflow-run.js';\nimport type {JobGraphNode} from './graph-model.js';\nimport {formatJobDurationAccessibleLabel} from './job-duration-format.js';\nimport {JobDurationLabel} from './job-duration-label.js';\n\nconst TRIGGER_SIZE = 36;\n\nexport function TriggerNode({\n trigger,\n}: {\n trigger: Pick<\n WorkflowRunDetail,\n 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'\n >;\n}) {\n const label = trigger.triggerDisplayLabel || 'trigger';\n const tooltip = trigger.triggerLabel || label;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={label}\n className=\"flex items-center justify-center rounded-full border border-border-neutral-base bg-background-components-base transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none\"\n style={{width: TRIGGER_SIZE, height: TRIGGER_SIZE}}\n >\n <TriggerSourceIcon\n provider={trigger.triggerProvider}\n source={trigger.triggerSource}\n aria-hidden\n className=\"size-14 shrink-0 text-foreground-neutral-muted\"\n />\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <span>{tooltip}</span>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nexport function JobNode({\n node,\n selected,\n onSelect,\n onKeyDown,\n onHoverStart,\n onHoverEnd,\n ref,\n}: {\n node: JobGraphNode;\n selected: boolean;\n onSelect: () => void;\n onKeyDown: KeyboardEventHandler<HTMLButtonElement>;\n onHoverStart: () => void;\n onHoverEnd: () => void;\n ref?: Ref<HTMLButtonElement>;\n}) {\n useTimeTick();\n const status =\n node.status === 'pending' &&\n node.jobExecutions.some((jobExecution) => jobExecution.status === 'running')\n ? 'running'\n : node.status;\n const visual = getWorkflowStatusVisual(status);\n const accessibleLabel = [\n node.displayName,\n visual.label,\n formatJobDurationAccessibleLabel(node.displayDuration),\n node.executionCountVisible\n ? executionCountAccessibleLabel(node.jobExecutions.length)\n : undefined,\n node.carriedOver ? 'reused' : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ');\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-pressed={selected}\n aria-label={accessibleLabel}\n data-job-id={node.id}\n onClick={onSelect}\n onKeyDown={onKeyDown}\n onPointerEnter={onHoverStart}\n onPointerLeave={onHoverEnd}\n className={cn(\n 'group relative flex h-48 w-208 items-center gap-8 rounded-8 border border-border-neutral-base bg-background-components-base px-10 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n selected && 'bg-background-components-hover',\n node.carriedOver && 'opacity-[0.55]',\n )}\n >\n {selected ? (\n <span\n aria-hidden=\"true\"\n className=\"absolute inset-y-6 left-0 w-3 rounded-full bg-border-highlights-interactive\"\n />\n ) : null}\n <div className=\"flex min-w-0 flex-1 items-center gap-8\">\n <WorkflowStatusIcon status={status} jobMode={node.mode} size={14} />\n <JobLabel label={node.displayName} />\n </div>\n <JobDurationLabel duration={node.displayDuration} />\n {node.executionCountVisible ? <ExecutionCountText executions={node.jobExecutions} /> : null}\n {node.carriedOver ? <CarriedOverBadge /> : null}\n </button>\n );\n}\n\nfunction ExecutionCountText({executions}: {executions: JobExecution[]}) {\n const count = executions.length;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span\n aria-hidden=\"true\"\n className=\"inline-flex h-20 min-w-28 shrink-0 items-center justify-end gap-4 text-foreground-neutral-muted\"\n >\n <Icon name=\"loopRightLine\" className=\"size-12\" />\n <Code as=\"span\" variant=\"label\" className=\"text-current\">\n {count}\n </Code>\n </span>\n </TooltipTrigger>\n <TooltipContent>{executionCountTooltip(executions)}</TooltipContent>\n </Tooltip>\n );\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobLabel({label}: {label: string}) {\n const {ref: labelRef, isTruncated} = useIsTextTruncated<HTMLSpanElement>(label);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span ref={labelRef} className=\"block min-w-0 truncate\">\n <Code as=\"span\" variant=\"label\" bold className=\"text-foreground-neutral-base\">\n {label}\n </Code>\n </span>\n </TooltipTrigger>\n {isTruncated ? <TooltipContent>{label}</TooltipContent> : null}\n </Tooltip>\n );\n}\n\nfunction executionCountAccessibleLabel(count: number): string {\n return `${count} ${count === 1 ? 'execution' : 'executions'}`;\n}\n\nfunction executionCountTooltip(executions: JobExecution[]): string {\n const counts = executionStatusCounts(executions);\n const parts = EXECUTION_STATUSES.map((status) =>\n counts[status] > 0 ? `${counts[status]} ${status}` : undefined,\n ).filter((part): part is string => part !== undefined);\n return parts.length > 0 ? parts.join(', ') : 'No executions';\n}\n\nconst EXECUTION_STATUSES = [\n 'pending',\n 'running',\n 'succeeded',\n 'failed',\n 'cancelled',\n] as const satisfies readonly JobExecutionStatus[];\n\nfunction executionStatusCounts(executions: JobExecution[]) {\n return executions.reduce(\n (counts, execution) => {\n counts[execution.status] += 1;\n return counts;\n },\n {pending: 0, running: 0, succeeded: 0, failed: 0, cancelled: 0},\n );\n}\n"],"names":["TriggerSourceIcon","Badge","useIsTextTruncated","Icon","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","cn","getWorkflowStatusVisual","WorkflowStatusIcon","formatJobDurationAccessibleLabel","JobDurationLabel","TRIGGER_SIZE","TriggerNode","trigger","label","triggerDisplayLabel","tooltip","triggerLabel","asChild","button","type","aria-label","className","style","width","height","provider","triggerProvider","source","triggerSource","aria-hidden","span","JobNode","node","selected","onSelect","onKeyDown","onHoverStart","onHoverEnd","ref","status","jobExecutions","some","jobExecution","visual","accessibleLabel","displayName","displayDuration","executionCountVisible","executionCountAccessibleLabel","length","undefined","carriedOver","filter","part","Boolean","join","aria-pressed","data-job-id","id","onClick","onPointerEnter","onPointerLeave","div","jobMode","mode","size","JobLabel","duration","ExecutionCountText","executions","CarriedOverBadge","count","name","as","variant","executionCountTooltip","labelRef","isTruncated","bold","counts","executionStatusCounts","parts","EXECUTION_STATUSES","map","reduce","execution","pending","running","succeeded","failed","cancelled"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-graph/job-node.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {KeyboardEventHandler, Ref} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n defaultJobExecution,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n type JobExecution,\n type JobExecutionStatus,\n type WorkflowRunDetail,\n} from '#core/workflow-run.js';\nimport type {JobGraphNode} from './graph-model.js';\nimport {formatJobDurationAccessibleLabel} from './job-duration-format.js';\nimport {JobDurationLabel} from './job-duration-label.js';\n\nconst TRIGGER_SIZE = 36;\n\nexport function TriggerNode({\n trigger,\n}: {\n trigger: Pick<\n WorkflowRunDetail,\n 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'\n >;\n}) {\n const label = trigger.triggerDisplayLabel || 'trigger';\n const tooltip = trigger.triggerLabel || label;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={label}\n className=\"flex items-center justify-center rounded-full border border-border-neutral-base bg-background-components-base transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none\"\n style={{width: TRIGGER_SIZE, height: TRIGGER_SIZE}}\n >\n <TriggerSourceIcon\n provider={trigger.triggerProvider}\n source={trigger.triggerSource}\n aria-hidden\n className=\"size-14 shrink-0 text-foreground-neutral-muted\"\n />\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <span>{tooltip}</span>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nexport function JobNode({\n node,\n selected,\n onSelect,\n onKeyDown,\n onHoverStart,\n onHoverEnd,\n ref,\n}: {\n node: JobGraphNode;\n selected: boolean;\n onSelect: () => void;\n onKeyDown: KeyboardEventHandler<HTMLButtonElement>;\n onHoverStart: () => void;\n onHoverEnd: () => void;\n ref?: Ref<HTMLButtonElement>;\n}) {\n useTimeTick();\n const status = deriveJobDisplayStatus(node);\n const execution = defaultJobExecution(node);\n const visual = getWorkflowStatusVisual(status);\n const accessibleLabel = [\n node.displayName,\n visual.label,\n formatJobDurationAccessibleLabel(\n node.displayDuration,\n execution ? deriveJobExecutionDisplayStatus(execution) : undefined,\n ),\n node.executionCountVisible\n ? executionCountAccessibleLabel(node.jobExecutions.length)\n : undefined,\n node.carriedOver ? 'reused' : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ');\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-pressed={selected}\n aria-label={accessibleLabel}\n data-job-id={node.id}\n onClick={onSelect}\n onKeyDown={onKeyDown}\n onPointerEnter={onHoverStart}\n onPointerLeave={onHoverEnd}\n className={cn(\n 'group relative flex h-48 w-208 items-center gap-8 rounded-8 border border-border-neutral-base bg-background-components-base px-10 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n selected && 'bg-background-components-hover',\n node.carriedOver && 'opacity-[0.55]',\n )}\n >\n {selected ? (\n <span\n aria-hidden=\"true\"\n className=\"absolute inset-y-6 left-0 w-3 rounded-full bg-border-highlights-interactive\"\n />\n ) : null}\n <div className=\"flex min-w-0 flex-1 items-center gap-8\">\n <WorkflowStatusIcon status={status} size={14} />\n <JobLabel label={node.displayName} />\n </div>\n <JobDurationLabel duration={node.displayDuration} />\n {node.executionCountVisible ? <ExecutionCountText executions={node.jobExecutions} /> : null}\n {node.carriedOver ? <CarriedOverBadge /> : null}\n </button>\n );\n}\n\nfunction ExecutionCountText({executions}: {executions: JobExecution[]}) {\n const count = executions.length;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span\n aria-hidden=\"true\"\n className=\"inline-flex h-20 min-w-28 shrink-0 items-center justify-end gap-4 text-foreground-neutral-muted\"\n >\n <Icon name=\"loopRightLine\" className=\"size-12\" />\n <Code as=\"span\" variant=\"label\" className=\"text-current\">\n {count}\n </Code>\n </span>\n </TooltipTrigger>\n <TooltipContent>{executionCountTooltip(executions)}</TooltipContent>\n </Tooltip>\n );\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobLabel({label}: {label: string}) {\n const {ref: labelRef, isTruncated} = useIsTextTruncated<HTMLSpanElement>(label);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span ref={labelRef} className=\"block min-w-0 truncate\">\n <Code as=\"span\" variant=\"label\" bold className=\"text-foreground-neutral-base\">\n {label}\n </Code>\n </span>\n </TooltipTrigger>\n {isTruncated ? <TooltipContent>{label}</TooltipContent> : null}\n </Tooltip>\n );\n}\n\nfunction executionCountAccessibleLabel(count: number): string {\n return `${count} ${count === 1 ? 'execution' : 'executions'}`;\n}\n\nfunction executionCountTooltip(executions: JobExecution[]): string {\n const counts = executionStatusCounts(executions);\n const parts = EXECUTION_STATUSES.map((status) =>\n counts[status] > 0 ? `${counts[status]} ${status}` : undefined,\n ).filter((part): part is string => part !== undefined);\n return parts.length > 0 ? parts.join(', ') : 'No executions';\n}\n\nconst EXECUTION_STATUSES = [\n 'pending',\n 'running',\n 'succeeded',\n 'failed',\n 'cancelled',\n] as const satisfies readonly JobExecutionStatus[];\n\nfunction executionStatusCounts(executions: JobExecution[]) {\n return executions.reduce(\n (counts, execution) => {\n counts[deriveJobExecutionDisplayStatus(execution)] += 1;\n return counts;\n },\n {pending: 0, running: 0, succeeded: 0, failed: 0, cancelled: 0},\n );\n}\n"],"names":["TriggerSourceIcon","Badge","useIsTextTruncated","Icon","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","cn","getWorkflowStatusVisual","WorkflowStatusIcon","defaultJobExecution","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","formatJobDurationAccessibleLabel","JobDurationLabel","TRIGGER_SIZE","TriggerNode","trigger","label","triggerDisplayLabel","tooltip","triggerLabel","asChild","button","type","aria-label","className","style","width","height","provider","triggerProvider","source","triggerSource","aria-hidden","span","JobNode","node","selected","onSelect","onKeyDown","onHoverStart","onHoverEnd","ref","status","execution","visual","accessibleLabel","displayName","displayDuration","undefined","executionCountVisible","executionCountAccessibleLabel","jobExecutions","length","carriedOver","filter","part","Boolean","join","aria-pressed","data-job-id","id","onClick","onPointerEnter","onPointerLeave","div","size","JobLabel","duration","ExecutionCountText","executions","CarriedOverBadge","count","name","as","variant","executionCountTooltip","labelRef","isTruncated","bold","counts","executionStatusCounts","parts","EXECUTION_STATUSES","map","reduce","pending","running","succeeded","failed","cancelled"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import { type Job, type JobExecution, type Step, type StepSourceLocation } from '#core/workflow-run.js';
|
|
2
|
+
import { type Job, type JobDisplayStatus, type JobExecution, type Step, type StepSourceLocation } from '#core/workflow-run.js';
|
|
3
3
|
export interface StepExpandedContext {
|
|
4
4
|
step: Step;
|
|
5
5
|
stepId: string;
|
|
@@ -14,7 +14,7 @@ export interface StepExpandedContext {
|
|
|
14
14
|
export interface StepListEmptyState {
|
|
15
15
|
title: string;
|
|
16
16
|
description: string;
|
|
17
|
-
status?:
|
|
17
|
+
status?: JobDisplayStatus | undefined;
|
|
18
18
|
}
|
|
19
19
|
export interface StepListProps {
|
|
20
20
|
job: Job;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step-list.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,OAAO,EAEL,KAAK,GAAG,EACR,KAAK,YAAY,EACjB,KAAK,IAAI,EACT,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAW/B,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"step-list.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,OAAO,EAEL,KAAK,GAAG,EACR,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,IAAI,EACT,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAW/B,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACxC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,uBAAuB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAChF,uBAAuB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9C,UAAU,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC5C,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,mBAAmB,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IAC/E,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,uBAA+B,EAC/B,UAAU,EACV,kBAAkB,EAClB,UAAiB,EACjB,SAAS,GACV,EAAE,aAAa,+BAqBf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/step-list/step-list.tsx"],"sourcesContent":["import {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@shipfox/react-ui/accordion';\nimport {Badge, type BadgeVariant} from '@shipfox/react-ui/badge';\nimport {Dot} from '@shipfox/react-ui/dot';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider, useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useId, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n isWorkflowStatus,\n type Job,\n type JobExecution,\n type Step,\n type StepSourceLocation,\n} from '#core/workflow-run.js';\nimport {formatJobExecutionTimeLabel} from '../job-graph/job-duration-format.js';\nimport {\n buildStepListModel,\n defaultStepListJobExecution,\n humanizeStatus,\n type StepAttemptModel,\n type StepListEntryModel,\n type StepListModel,\n} from './step-list-model.js';\n\nexport interface StepExpandedContext {\n step: Step;\n stepId: string;\n stepLabel: string;\n sourceLocation: StepSourceLocation | null;\n attempt: number;\n attemptId: string;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n carriedOver: boolean;\n}\n\nexport interface StepListEmptyState {\n title: string;\n description: string;\n status?: Job['status'] | undefined;\n}\n\nexport interface StepListProps {\n job: Job;\n jobExecution?: JobExecution | undefined;\n selectedAttemptId?: string | null | undefined;\n defaultSelectedAttemptId?: string | undefined;\n onSelectedAttemptChange?: ((attemptId: string | undefined) => void) | undefined;\n autoSelectActiveAttempt?: boolean | undefined;\n emptyState?: StepListEmptyState | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n showHeader?: boolean | undefined;\n className?: string | undefined;\n}\n\nexport function StepList({\n job,\n jobExecution,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n autoSelectActiveAttempt = false,\n emptyState,\n renderExpandedStep,\n showHeader = true,\n className,\n}: StepListProps) {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const model = useMemo(\n () => buildStepListModel({job, jobExecution: selectedJobExecution}),\n [job, selectedJobExecution],\n );\n\n return (\n <StepListContent\n key={model.jobExecutionId}\n model={model}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={defaultSelectedAttemptId}\n onSelectedAttemptChange={onSelectedAttemptChange}\n autoSelectActiveAttempt={autoSelectActiveAttempt}\n emptyState={emptyState}\n renderExpandedStep={renderExpandedStep}\n showHeader={showHeader}\n className={className}\n />\n );\n}\n\nfunction StepListContent({\n model,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n autoSelectActiveAttempt,\n emptyState,\n renderExpandedStep,\n showHeader,\n className,\n}: Omit<StepListProps, 'job' | 'jobExecution'> & {model: StepListModel}) {\n const titleId = useId();\n const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState<string[]>(() =>\n selectedAttemptId\n ? [selectedAttemptId]\n : defaultSelectedAttemptId\n ? [defaultSelectedAttemptId]\n : [],\n );\n const [userSelectedAttempt, setUserSelectedAttempt] = useState(false);\n const lastNotifiedSelectedAttemptId = useRef<string | null>(null);\n const shouldUseControlledCollapsedState =\n selectedAttemptId === null && lastNotifiedSelectedAttemptId.current === null;\n const autoSelectedAttemptIds =\n selectedAttemptId === undefined &&\n autoSelectActiveAttempt &&\n !userSelectedAttempt &&\n model.activeEntryId\n ? [model.activeEntryId]\n : [];\n const selectedAttemptIds = shouldUseControlledCollapsedState\n ? []\n : localSelectedAttemptIds.length > 0\n ? localSelectedAttemptIds\n : autoSelectedAttemptIds;\n const hasExpandedContent = renderExpandedStep !== undefined;\n\n useEffect(() => {\n if (selectedAttemptId !== undefined) return;\n\n setLocalSelectedAttemptIds(defaultSelectedAttemptId ? [defaultSelectedAttemptId] : []);\n setUserSelectedAttempt(false);\n }, [defaultSelectedAttemptId, selectedAttemptId]);\n\n useEffect(() => {\n if (selectedAttemptId === undefined) return;\n\n const nextSelectedAttemptId = selectedAttemptId ?? null;\n if (lastNotifiedSelectedAttemptId.current === nextSelectedAttemptId) {\n lastNotifiedSelectedAttemptId.current = null;\n return;\n }\n\n setLocalSelectedAttemptIds(selectedAttemptId ? [selectedAttemptId] : []);\n setUserSelectedAttempt(true);\n }, [selectedAttemptId]);\n\n function selectAttempt(nextAttemptIds: string[]) {\n const nextAttemptId = nextSelectedAttemptId(selectedAttemptIds, nextAttemptIds);\n setUserSelectedAttempt(true);\n setLocalSelectedAttemptIds(nextAttemptIds);\n lastNotifiedSelectedAttemptId.current = nextAttemptId ?? null;\n onSelectedAttemptChange?.(nextAttemptId);\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={showHeader ? titleId : undefined}\n className={cn(\n 'flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base',\n className,\n )}\n >\n {showHeader ? (\n <div className=\"flex min-h-40 items-center border-b border-border-neutral-base px-16 py-8\">\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"text-foreground-neutral-base\">\n {model.jobName}\n </Text>\n </div>\n ) : null}\n\n {model.entries.length === 0 ? (\n <StepListEmptyStateView emptyState={emptyState} />\n ) : (\n <Accordion\n type=\"multiple\"\n value={selectedAttemptIds}\n onValueChange={selectAttempt}\n asChild\n >\n <ol>\n {model.entries.map((entry) => {\n const selected = selectedAttemptIds.includes(entry.id);\n return (\n <StepRow\n key={entry.id}\n entry={entry}\n selected={selected}\n hasExpandedContent={hasExpandedContent}\n onSelect={() => {\n selectAttempt(\n hasExpandedContent\n ? toggleAttemptId(selectedAttemptIds, entry.id)\n : selected\n ? []\n : [entry.id],\n );\n }}\n expandedContent={\n selected\n ? renderExpandedStep?.({\n step: entry.step,\n stepId: entry.step.id,\n stepLabel: entry.step.label,\n sourceLocation: entry.step.sourceLocation,\n attempt: entry.attempt,\n attemptId: entry.id,\n attemptError: entry.error,\n attemptStatus: entry.statusVisual.kind,\n carriedOver: entry.carriedOver,\n })\n : null\n }\n />\n );\n })}\n </ol>\n </Accordion>\n )}\n </section>\n </TimeTickerProvider>\n );\n}\n\nfunction StepListEmptyStateView({\n emptyState = {\n title: 'No steps recorded',\n description: 'This job has not recorded any steps.',\n },\n}: {\n emptyState?: StepListEmptyState | undefined;\n}) {\n if (!emptyState.status) {\n return (\n <EmptyState\n className=\"min-h-120 px-16 py-20\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n }\n\n return (\n <div className=\"flex min-h-120 flex-col items-center justify-center gap-10 px-16 py-20\">\n <StepListEmptyStateIcon status={emptyState.status} />\n <div className=\"text-center\">\n <Text size=\"sm\" className=\"text-foreground-neutral-subtle\">\n {emptyState.title}\n </Text>\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {emptyState.description}\n </Text>\n </div>\n </div>\n );\n}\n\nfunction StepListEmptyStateIcon({status}: {status: Job['status']}) {\n if (status !== 'running') {\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-8\">\n <WorkflowStatusIcon status={status} size={20} tooltip={false} />\n </div>\n );\n }\n\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-8 text-foreground-neutral-muted\">\n <Icon name=\"timerLine\" size={18} aria-hidden=\"true\" />\n </div>\n );\n}\n\nfunction StepRow({\n entry,\n selected,\n hasExpandedContent,\n onSelect,\n expandedContent,\n}: {\n entry: StepListEntryModel;\n selected: boolean;\n hasExpandedContent: boolean;\n onSelect: () => void;\n expandedContent: ReactNode;\n}) {\n const shouldShowLabelTooltip = entry.step.label.length > 32;\n const rowContent = (\n <>\n <Icon\n name=\"chevronRight\"\n aria-hidden=\"true\"\n className={cn(\n 'size-14 shrink-0 text-foreground-neutral-muted transition-transform',\n selected && 'rotate-90',\n )}\n />\n <StepStatusIcon entry={entry} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-center gap-8\">\n <Text size=\"sm\" bold className=\"truncate text-foreground-neutral-base\">\n {entry.step.label}\n </Text>\n {entry.step.attempts.length > 1 ? <StepAttemptChip attempt={entry} /> : null}\n {entry.carriedOver ? <CarriedOverBadge /> : null}\n </div>\n </div>\n <StepAttemptDurationLabel attempt={entry} />\n </>\n );\n const rowClasses = cn(\n 'group grid min-h-44 w-full grid-cols-[14px_14px_minmax(0,1fr)_auto] items-center gap-x-8 px-12 py-6 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n selected && 'bg-background-components-hover',\n entry.carriedOver && 'opacity-[0.55]',\n );\n const button = hasExpandedContent ? (\n <AccordionTrigger\n showIcon={false}\n aria-label={entryAccessibleLabel(entry)}\n className={rowClasses}\n >\n {rowContent}\n </AccordionTrigger>\n ) : (\n <button\n type=\"button\"\n aria-expanded={false}\n aria-label={entryAccessibleLabel(entry)}\n onClick={onSelect}\n className={rowClasses}\n >\n {rowContent}\n </button>\n );\n const triggerNode = shouldShowLabelTooltip ? (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent>\n <span className=\"block max-w-320 break-words\">{entry.step.label}</span>\n </TooltipContent>\n </Tooltip>\n ) : (\n button\n );\n const row = (\n <>\n {triggerNode}\n {selected && expandedContent ? (\n <AccordionContent className=\"border-t border-border-neutral-base bg-background-neutral-base px-12 py-12\">\n <div className=\"grid grid-cols-[14px_14px_minmax(0,1fr)_auto] gap-x-8\">\n <span aria-hidden=\"true\" />\n <span aria-hidden=\"true\" />\n <div className=\"min-w-0\">{expandedContent}</div>\n <span aria-hidden=\"true\" />\n </div>\n </AccordionContent>\n ) : null}\n </>\n );\n\n if (hasExpandedContent) {\n return (\n <AccordionItem value={entry.id} asChild>\n <li>{row}</li>\n </AccordionItem>\n );\n }\n\n return <li className=\"border-b border-border-neutral-base last:border-b-0\">{row}</li>;\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt.\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction toggleAttemptId(selectedAttemptIds: readonly string[], attemptId: string): string[] {\n if (selectedAttemptIds.includes(attemptId)) {\n return selectedAttemptIds.filter((selectedAttemptId) => selectedAttemptId !== attemptId);\n }\n return [...selectedAttemptIds, attemptId];\n}\n\nfunction nextSelectedAttemptId(\n selectedAttemptIds: readonly string[],\n nextAttemptIds: readonly string[],\n): string | undefined {\n if (nextAttemptIds.length === 0) return undefined;\n\n const openedAttemptId = nextAttemptIds.find(\n (attemptId) => !selectedAttemptIds.includes(attemptId),\n );\n return openedAttemptId ?? nextAttemptIds.at(-1);\n}\n\nfunction StepStatusIcon({entry}: {entry: StepListEntryModel}) {\n if (isWorkflowStatus(entry.statusVisual.kind)) {\n return (\n <WorkflowStatusIcon\n status={entry.statusVisual.kind}\n ripple={entry.statusVisual.ripple}\n size={14}\n />\n );\n }\n\n return (\n <span role=\"img\" aria-label={entry.statusVisual.label} className=\"inline-flex shrink-0\">\n <Dot\n variant={entry.statusVisual.dot}\n ripple={entry.statusVisual.ripple}\n className=\"size-12 shrink-0\"\n />\n </span>\n );\n}\n\nconst attemptChipClasses: Record<NonNullable<BadgeVariant>, string> = {\n neutral: 'bg-tag-neutral-bg border-tag-neutral-border',\n info: 'bg-tag-blue-bg border-tag-blue-border',\n feature: 'bg-tag-purple-bg border-tag-purple-border',\n success: 'bg-tag-success-bg border-tag-success-border',\n warning: 'bg-tag-warning-bg border-tag-warning-border',\n error: 'bg-tag-error-bg border-tag-error-border',\n};\n\nfunction StepAttemptChip({attempt}: {attempt: StepAttemptModel}) {\n return (\n <div className=\"flex shrink-0 items-center gap-4\" aria-hidden=\"true\">\n <span\n className={cn(\n 'inline-flex h-18 min-w-24 items-center justify-center rounded-4 border px-5 font-code text-xs leading-16 text-foreground-neutral-base',\n attemptChipClasses[attempt.statusVisual.badge ?? 'neutral'],\n )}\n >\n #{attempt.attempt}\n </span>\n </div>\n );\n}\n\nfunction entryAccessibleLabel(entry: StepListEntryModel): string {\n const parts = [entry.step.label, entry.statusVisual.label, `attempt ${entry.attempt}`];\n if (entry.step.error?.category) parts.push(humanizeStatus(entry.step.error.category));\n return parts.join(', ');\n}\n\nfunction StepAttemptDurationLabel({attempt}: {attempt: StepAttemptModel}) {\n const duration = attempt.displayDuration;\n if (duration === null) return null;\n\n if (duration.state === 'live') {\n return <LiveDurationText fromIso={duration.fromIso} />;\n }\n\n return <DurationText>{formatJobExecutionTimeLabel(duration)}</DurationText>;\n}\n\nfunction LiveDurationText({fromIso}: {fromIso: string}) {\n useTimeTick();\n return <DurationText>{humanDuration(fromIso)}</DurationText>;\n}\n\nfunction DurationText({children}: {children: string}) {\n return (\n <Code as=\"span\" variant=\"label\" className=\"shrink-0 tabular-nums text-foreground-neutral-muted\">\n {children}\n </Code>\n );\n}\n"],"names":["Accordion","AccordionContent","AccordionItem","AccordionTrigger","Badge","Dot","EmptyState","Icon","TimeTickerProvider","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","humanDuration","useEffect","useId","useMemo","useRef","useState","WorkflowStatusIcon","isWorkflowStatus","formatJobExecutionTimeLabel","buildStepListModel","defaultStepListJobExecution","humanizeStatus","StepList","job","jobExecution","selectedAttemptId","defaultSelectedAttemptId","onSelectedAttemptChange","autoSelectActiveAttempt","emptyState","renderExpandedStep","showHeader","className","selectedJobExecution","model","StepListContent","jobExecutionId","titleId","localSelectedAttemptIds","setLocalSelectedAttemptIds","userSelectedAttempt","setUserSelectedAttempt","lastNotifiedSelectedAttemptId","shouldUseControlledCollapsedState","current","autoSelectedAttemptIds","undefined","activeEntryId","selectedAttemptIds","length","hasExpandedContent","nextSelectedAttemptId","selectAttempt","nextAttemptIds","nextAttemptId","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","div","as","id","size","bold","jobName","entries","StepListEmptyStateView","type","value","onValueChange","asChild","ol","map","entry","selected","includes","StepRow","onSelect","toggleAttemptId","expandedContent","step","stepId","stepLabel","label","sourceLocation","attempt","attemptId","attemptError","error","attemptStatus","statusVisual","kind","carriedOver","title","description","status","icon","variant","StepListEmptyStateIcon","tooltip","name","aria-hidden","shouldShowLabelTooltip","rowContent","StepStatusIcon","attempts","StepAttemptChip","CarriedOverBadge","StepAttemptDurationLabel","rowClasses","button","showIcon","aria-label","entryAccessibleLabel","aria-expanded","onClick","triggerNode","span","row","li","filter","openedAttemptId","find","at","ripple","role","dot","attemptChipClasses","neutral","info","feature","success","warning","badge","parts","category","push","join","duration","displayDuration","state","LiveDurationText","fromIso","DurationText","children"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/step-list/step-list.tsx"],"sourcesContent":["import {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@shipfox/react-ui/accordion';\nimport {Badge, type BadgeVariant} from '@shipfox/react-ui/badge';\nimport {Dot} from '@shipfox/react-ui/dot';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider, useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useId, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n isWorkflowStatus,\n type Job,\n type JobDisplayStatus,\n type JobExecution,\n type Step,\n type StepSourceLocation,\n} from '#core/workflow-run.js';\nimport {formatJobExecutionTimeLabel} from '../job-graph/job-duration-format.js';\nimport {\n buildStepListModel,\n defaultStepListJobExecution,\n humanizeStatus,\n type StepAttemptModel,\n type StepListEntryModel,\n type StepListModel,\n} from './step-list-model.js';\n\nexport interface StepExpandedContext {\n step: Step;\n stepId: string;\n stepLabel: string;\n sourceLocation: StepSourceLocation | null;\n attempt: number;\n attemptId: string;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n carriedOver: boolean;\n}\n\nexport interface StepListEmptyState {\n title: string;\n description: string;\n status?: JobDisplayStatus | undefined;\n}\n\nexport interface StepListProps {\n job: Job;\n jobExecution?: JobExecution | undefined;\n selectedAttemptId?: string | null | undefined;\n defaultSelectedAttemptId?: string | undefined;\n onSelectedAttemptChange?: ((attemptId: string | undefined) => void) | undefined;\n autoSelectActiveAttempt?: boolean | undefined;\n emptyState?: StepListEmptyState | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n showHeader?: boolean | undefined;\n className?: string | undefined;\n}\n\nexport function StepList({\n job,\n jobExecution,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n autoSelectActiveAttempt = false,\n emptyState,\n renderExpandedStep,\n showHeader = true,\n className,\n}: StepListProps) {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const model = useMemo(\n () => buildStepListModel({job, jobExecution: selectedJobExecution}),\n [job, selectedJobExecution],\n );\n\n return (\n <StepListContent\n key={model.jobExecutionId}\n model={model}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={defaultSelectedAttemptId}\n onSelectedAttemptChange={onSelectedAttemptChange}\n autoSelectActiveAttempt={autoSelectActiveAttempt}\n emptyState={emptyState}\n renderExpandedStep={renderExpandedStep}\n showHeader={showHeader}\n className={className}\n />\n );\n}\n\nfunction StepListContent({\n model,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n autoSelectActiveAttempt,\n emptyState,\n renderExpandedStep,\n showHeader,\n className,\n}: Omit<StepListProps, 'job' | 'jobExecution'> & {model: StepListModel}) {\n const titleId = useId();\n const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState<string[]>(() =>\n selectedAttemptId\n ? [selectedAttemptId]\n : defaultSelectedAttemptId\n ? [defaultSelectedAttemptId]\n : [],\n );\n const [userSelectedAttempt, setUserSelectedAttempt] = useState(false);\n const lastNotifiedSelectedAttemptId = useRef<string | null>(null);\n const shouldUseControlledCollapsedState =\n selectedAttemptId === null && lastNotifiedSelectedAttemptId.current === null;\n const autoSelectedAttemptIds =\n selectedAttemptId === undefined &&\n autoSelectActiveAttempt &&\n !userSelectedAttempt &&\n model.activeEntryId\n ? [model.activeEntryId]\n : [];\n const selectedAttemptIds = shouldUseControlledCollapsedState\n ? []\n : localSelectedAttemptIds.length > 0\n ? localSelectedAttemptIds\n : autoSelectedAttemptIds;\n const hasExpandedContent = renderExpandedStep !== undefined;\n\n useEffect(() => {\n if (selectedAttemptId !== undefined) return;\n\n setLocalSelectedAttemptIds(defaultSelectedAttemptId ? [defaultSelectedAttemptId] : []);\n setUserSelectedAttempt(false);\n }, [defaultSelectedAttemptId, selectedAttemptId]);\n\n useEffect(() => {\n if (selectedAttemptId === undefined) return;\n\n const nextSelectedAttemptId = selectedAttemptId ?? null;\n if (lastNotifiedSelectedAttemptId.current === nextSelectedAttemptId) {\n lastNotifiedSelectedAttemptId.current = null;\n return;\n }\n\n setLocalSelectedAttemptIds(selectedAttemptId ? [selectedAttemptId] : []);\n setUserSelectedAttempt(true);\n }, [selectedAttemptId]);\n\n function selectAttempt(nextAttemptIds: string[]) {\n const nextAttemptId = nextSelectedAttemptId(selectedAttemptIds, nextAttemptIds);\n setUserSelectedAttempt(true);\n setLocalSelectedAttemptIds(nextAttemptIds);\n lastNotifiedSelectedAttemptId.current = nextAttemptId ?? null;\n onSelectedAttemptChange?.(nextAttemptId);\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={showHeader ? titleId : undefined}\n className={cn(\n 'flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base',\n className,\n )}\n >\n {showHeader ? (\n <div className=\"flex min-h-40 items-center border-b border-border-neutral-base px-16 py-8\">\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"text-foreground-neutral-base\">\n {model.jobName}\n </Text>\n </div>\n ) : null}\n\n {model.entries.length === 0 ? (\n <StepListEmptyStateView emptyState={emptyState} />\n ) : (\n <Accordion\n type=\"multiple\"\n value={selectedAttemptIds}\n onValueChange={selectAttempt}\n asChild\n >\n <ol>\n {model.entries.map((entry) => {\n const selected = selectedAttemptIds.includes(entry.id);\n return (\n <StepRow\n key={entry.id}\n entry={entry}\n selected={selected}\n hasExpandedContent={hasExpandedContent}\n onSelect={() => {\n selectAttempt(\n hasExpandedContent\n ? toggleAttemptId(selectedAttemptIds, entry.id)\n : selected\n ? []\n : [entry.id],\n );\n }}\n expandedContent={\n selected\n ? renderExpandedStep?.({\n step: entry.step,\n stepId: entry.step.id,\n stepLabel: entry.step.label,\n sourceLocation: entry.step.sourceLocation,\n attempt: entry.attempt,\n attemptId: entry.id,\n attemptError: entry.error,\n attemptStatus: entry.statusVisual.kind,\n carriedOver: entry.carriedOver,\n })\n : null\n }\n />\n );\n })}\n </ol>\n </Accordion>\n )}\n </section>\n </TimeTickerProvider>\n );\n}\n\nfunction StepListEmptyStateView({\n emptyState = {\n title: 'No steps recorded',\n description: 'This job has not recorded any steps.',\n },\n}: {\n emptyState?: StepListEmptyState | undefined;\n}) {\n if (!emptyState.status) {\n return (\n <EmptyState\n className=\"min-h-120 px-16 py-20\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n }\n\n return (\n <div className=\"flex min-h-120 flex-col items-center justify-center gap-10 px-16 py-20\">\n <StepListEmptyStateIcon status={emptyState.status} />\n <div className=\"text-center\">\n <Text size=\"sm\" className=\"text-foreground-neutral-subtle\">\n {emptyState.title}\n </Text>\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {emptyState.description}\n </Text>\n </div>\n </div>\n );\n}\n\nfunction StepListEmptyStateIcon({status}: {status: JobDisplayStatus}) {\n if (status !== 'running') {\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-8\">\n <WorkflowStatusIcon status={status} size={20} tooltip={false} />\n </div>\n );\n }\n\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-8 text-foreground-neutral-muted\">\n <Icon name=\"timerLine\" size={18} aria-hidden=\"true\" />\n </div>\n );\n}\n\nfunction StepRow({\n entry,\n selected,\n hasExpandedContent,\n onSelect,\n expandedContent,\n}: {\n entry: StepListEntryModel;\n selected: boolean;\n hasExpandedContent: boolean;\n onSelect: () => void;\n expandedContent: ReactNode;\n}) {\n const shouldShowLabelTooltip = entry.step.label.length > 32;\n const rowContent = (\n <>\n <Icon\n name=\"chevronRight\"\n aria-hidden=\"true\"\n className={cn(\n 'size-14 shrink-0 text-foreground-neutral-muted transition-transform',\n selected && 'rotate-90',\n )}\n />\n <StepStatusIcon entry={entry} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-center gap-8\">\n <Text size=\"sm\" bold className=\"truncate text-foreground-neutral-base\">\n {entry.step.label}\n </Text>\n {entry.step.attempts.length > 1 ? <StepAttemptChip attempt={entry} /> : null}\n {entry.carriedOver ? <CarriedOverBadge /> : null}\n </div>\n </div>\n <StepAttemptDurationLabel attempt={entry} />\n </>\n );\n const rowClasses = cn(\n 'group grid min-h-44 w-full grid-cols-[14px_14px_minmax(0,1fr)_auto] items-center gap-x-8 px-12 py-6 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n selected && 'bg-background-components-hover',\n entry.carriedOver && 'opacity-[0.55]',\n );\n const button = hasExpandedContent ? (\n <AccordionTrigger\n showIcon={false}\n aria-label={entryAccessibleLabel(entry)}\n className={rowClasses}\n >\n {rowContent}\n </AccordionTrigger>\n ) : (\n <button\n type=\"button\"\n aria-expanded={false}\n aria-label={entryAccessibleLabel(entry)}\n onClick={onSelect}\n className={rowClasses}\n >\n {rowContent}\n </button>\n );\n const triggerNode = shouldShowLabelTooltip ? (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent>\n <span className=\"block max-w-320 break-words\">{entry.step.label}</span>\n </TooltipContent>\n </Tooltip>\n ) : (\n button\n );\n const row = (\n <>\n {triggerNode}\n {selected && expandedContent ? (\n <AccordionContent className=\"border-t border-border-neutral-base bg-background-neutral-base px-12 py-12\">\n <div className=\"grid grid-cols-[14px_14px_minmax(0,1fr)_auto] gap-x-8\">\n <span aria-hidden=\"true\" />\n <span aria-hidden=\"true\" />\n <div className=\"min-w-0\">{expandedContent}</div>\n <span aria-hidden=\"true\" />\n </div>\n </AccordionContent>\n ) : null}\n </>\n );\n\n if (hasExpandedContent) {\n return (\n <AccordionItem value={entry.id} asChild>\n <li>{row}</li>\n </AccordionItem>\n );\n }\n\n return <li className=\"border-b border-border-neutral-base last:border-b-0\">{row}</li>;\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt.\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction toggleAttemptId(selectedAttemptIds: readonly string[], attemptId: string): string[] {\n if (selectedAttemptIds.includes(attemptId)) {\n return selectedAttemptIds.filter((selectedAttemptId) => selectedAttemptId !== attemptId);\n }\n return [...selectedAttemptIds, attemptId];\n}\n\nfunction nextSelectedAttemptId(\n selectedAttemptIds: readonly string[],\n nextAttemptIds: readonly string[],\n): string | undefined {\n if (nextAttemptIds.length === 0) return undefined;\n\n const openedAttemptId = nextAttemptIds.find(\n (attemptId) => !selectedAttemptIds.includes(attemptId),\n );\n return openedAttemptId ?? nextAttemptIds.at(-1);\n}\n\nfunction StepStatusIcon({entry}: {entry: StepListEntryModel}) {\n if (isWorkflowStatus(entry.statusVisual.kind)) {\n return (\n <WorkflowStatusIcon\n status={entry.statusVisual.kind}\n ripple={entry.statusVisual.ripple}\n size={14}\n />\n );\n }\n\n return (\n <span role=\"img\" aria-label={entry.statusVisual.label} className=\"inline-flex shrink-0\">\n <Dot\n variant={entry.statusVisual.dot}\n ripple={entry.statusVisual.ripple}\n className=\"size-12 shrink-0\"\n />\n </span>\n );\n}\n\nconst attemptChipClasses: Record<NonNullable<BadgeVariant>, string> = {\n neutral: 'bg-tag-neutral-bg border-tag-neutral-border',\n info: 'bg-tag-blue-bg border-tag-blue-border',\n feature: 'bg-tag-purple-bg border-tag-purple-border',\n success: 'bg-tag-success-bg border-tag-success-border',\n warning: 'bg-tag-warning-bg border-tag-warning-border',\n error: 'bg-tag-error-bg border-tag-error-border',\n};\n\nfunction StepAttemptChip({attempt}: {attempt: StepAttemptModel}) {\n return (\n <div className=\"flex shrink-0 items-center gap-4\" aria-hidden=\"true\">\n <span\n className={cn(\n 'inline-flex h-18 min-w-24 items-center justify-center rounded-4 border px-5 font-code text-xs leading-16 text-foreground-neutral-base',\n attemptChipClasses[attempt.statusVisual.badge ?? 'neutral'],\n )}\n >\n #{attempt.attempt}\n </span>\n </div>\n );\n}\n\nfunction entryAccessibleLabel(entry: StepListEntryModel): string {\n const parts = [entry.step.label, entry.statusVisual.label, `attempt ${entry.attempt}`];\n if (entry.step.error?.category) parts.push(humanizeStatus(entry.step.error.category));\n return parts.join(', ');\n}\n\nfunction StepAttemptDurationLabel({attempt}: {attempt: StepAttemptModel}) {\n const duration = attempt.displayDuration;\n if (duration === null) return null;\n\n if (duration.state === 'live') {\n return <LiveDurationText fromIso={duration.fromIso} />;\n }\n\n return <DurationText>{formatJobExecutionTimeLabel(duration)}</DurationText>;\n}\n\nfunction LiveDurationText({fromIso}: {fromIso: string}) {\n useTimeTick();\n return <DurationText>{humanDuration(fromIso)}</DurationText>;\n}\n\nfunction DurationText({children}: {children: string}) {\n return (\n <Code as=\"span\" variant=\"label\" className=\"shrink-0 tabular-nums text-foreground-neutral-muted\">\n {children}\n </Code>\n );\n}\n"],"names":["Accordion","AccordionContent","AccordionItem","AccordionTrigger","Badge","Dot","EmptyState","Icon","TimeTickerProvider","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","humanDuration","useEffect","useId","useMemo","useRef","useState","WorkflowStatusIcon","isWorkflowStatus","formatJobExecutionTimeLabel","buildStepListModel","defaultStepListJobExecution","humanizeStatus","StepList","job","jobExecution","selectedAttemptId","defaultSelectedAttemptId","onSelectedAttemptChange","autoSelectActiveAttempt","emptyState","renderExpandedStep","showHeader","className","selectedJobExecution","model","StepListContent","jobExecutionId","titleId","localSelectedAttemptIds","setLocalSelectedAttemptIds","userSelectedAttempt","setUserSelectedAttempt","lastNotifiedSelectedAttemptId","shouldUseControlledCollapsedState","current","autoSelectedAttemptIds","undefined","activeEntryId","selectedAttemptIds","length","hasExpandedContent","nextSelectedAttemptId","selectAttempt","nextAttemptIds","nextAttemptId","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","div","as","id","size","bold","jobName","entries","StepListEmptyStateView","type","value","onValueChange","asChild","ol","map","entry","selected","includes","StepRow","onSelect","toggleAttemptId","expandedContent","step","stepId","stepLabel","label","sourceLocation","attempt","attemptId","attemptError","error","attemptStatus","statusVisual","kind","carriedOver","title","description","status","icon","variant","StepListEmptyStateIcon","tooltip","name","aria-hidden","shouldShowLabelTooltip","rowContent","StepStatusIcon","attempts","StepAttemptChip","CarriedOverBadge","StepAttemptDurationLabel","rowClasses","button","showIcon","aria-label","entryAccessibleLabel","aria-expanded","onClick","triggerNode","span","row","li","filter","openedAttemptId","find","at","ripple","role","dot","attemptChipClasses","neutral","info","feature","success","warning","badge","parts","category","push","join","duration","displayDuration","state","LiveDurationText","fromIso","DurationText","children"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { WorkflowRunJobs } from '#core/workflow-run.js';
|
|
2
|
+
export interface JobStatusStripProps {
|
|
3
|
+
jobs: WorkflowRunJobs;
|
|
4
|
+
className?: string | undefined;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* A run's jobs as a row of status glyphs, in graph order.
|
|
8
|
+
*
|
|
9
|
+
* This is the reason the list is worth its width: it answers "where did this run fail"
|
|
10
|
+
* without opening the run. Glyphs are silent to assistive tech and carry no tooltip of their
|
|
11
|
+
* own; the strip speaks once, as a summary, so a 40-job run does not read out 40 labels.
|
|
12
|
+
*
|
|
13
|
+
* What it draws comes from the preview; what it says comes from the counts, which cover every
|
|
14
|
+
* job including those the server never sent. That split is what lets the overflow glyph
|
|
15
|
+
* report a failure sitting past the preview instead of quietly dropping it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function JobStatusStrip({ jobs, className }: JobStatusStripProps): import("react").JSX.Element | null;
|
|
18
|
+
/** "12 jobs: 9 succeeded, 2 failed, 1 running" — a count is not an accessible name on its own. */
|
|
19
|
+
export declare function jobStatusSummary(jobs: WorkflowRunJobs): string;
|
|
20
|
+
export declare function overflowCountLabel(hiddenCount: number): string;
|
|
21
|
+
//# sourceMappingURL=job-status-strip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-status-strip.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAmC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAyC7F,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,mBAAmB,sCAqDpE;AAgCD,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAO9D;AAyBD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAI9D"}
|