@shipfox/client-workflows 18.0.0 → 20.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +25 -0
- package/dist/components/workflow-run-duration-label.d.ts +8 -7
- package/dist/components/workflow-run-duration-label.d.ts.map +1 -1
- package/dist/components/workflow-run-duration-label.js +15 -31
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +20 -7
- package/dist/components/workflow-run-list/job-status-strip.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 +9 -6
- 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 +7 -34
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
- package/dist/components/workflow-status/status-visuals.js +0 -9
- package/dist/components/workflow-status/status-visuals.js.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.js +0 -11
- package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
- package/dist/core/entities/job.d.ts +5 -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 +15 -57
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +1 -94
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/workflow-run.d.ts +2 -2
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js +1 -1
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +14 -2
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/hooks/api/workflow-runs.js +1 -0
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/components/job-graph/job-node.test.tsx +2 -2
- package/src/components/workflow-run-duration-label.tsx +21 -44
- package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +121 -0
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +31 -0
- package/src/components/workflow-run-list/workflow-run-row.tsx +7 -6
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +0 -38
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +73 -21
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +6 -27
- package/src/components/workflow-status/status-visuals.ts +0 -4
- package/src/components/workflow-status/workflow-status-icon.stories.tsx +0 -1
- package/src/components/workflow-status/workflow-status-icon.tsx +0 -18
- package/src/core/entities/job-status.test.ts +47 -1
- package/src/core/entities/job.ts +15 -3
- package/src/core/entities/workflow-run.ts +21 -134
- package/src/core/workflow-run.test.ts +15 -0
- package/src/core/workflow-run.ts +0 -8
- package/src/hooks/api/workflow-run-mapper.ts +20 -1
- package/src/hooks/api/workflow-runs.ts +1 -1
- package/test/fixtures/workflow-run.ts +60 -13
- package/tsconfig.build.tsbuildinfo +1 -1
- package/src/core/entities/workflow-run-display.test.ts +0 -159
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled: 86 files with swc (
|
|
2
|
+
Successfully compiled: 86 files with swc (666.85ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @shipfox/client-workflows
|
|
2
2
|
|
|
3
|
+
## 20.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4df5e37: Workflow run list items and run detail now carry `has_started_job_execution`, reporting whether any
|
|
8
|
+
job execution of the attempt reached a runner. Run surfaces no longer show a client-derived `Queued`
|
|
9
|
+
state: the run list and run detail present the API attempt status and duration, and read the new
|
|
10
|
+
field to label a finished run's duration as run or elapsed time.
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies [4df5e37]
|
|
15
|
+
- @shipfox/api-workflows-dto@12.7.0
|
|
16
|
+
|
|
17
|
+
## 19.0.0
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- 53b87f0: Distinguish queued and executing jobs in workflow run list status strips.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [53b87f0]
|
|
26
|
+
- @shipfox/api-workflows-dto@12.6.0
|
|
27
|
+
|
|
3
28
|
## 18.0.0
|
|
4
29
|
|
|
5
30
|
### Minor Changes
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import type { WorkflowRunAttemptDisplayDuration
|
|
1
|
+
import type { WorkflowRunAttemptDisplayDuration } from '#core/workflow-run.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* A run reports one span: how long its attempt has been under way. Queueing is a job
|
|
4
|
+
* execution's own affair, and the job surfaces report it there.
|
|
5
5
|
*/
|
|
6
|
-
export type WorkflowRunDurationDisplay = WorkflowRunAttemptDisplayDuration
|
|
7
|
-
export declare function WorkflowRunDurationLabel({ duration, className, }: {
|
|
6
|
+
export type WorkflowRunDurationDisplay = WorkflowRunAttemptDisplayDuration;
|
|
7
|
+
export declare function WorkflowRunDurationLabel({ duration, hasStarted, className, }: {
|
|
8
8
|
duration: WorkflowRunDurationDisplay | null;
|
|
9
|
+
hasStarted: boolean;
|
|
9
10
|
className?: string | undefined;
|
|
10
11
|
}): import("react").JSX.Element | null;
|
|
11
|
-
export declare function useWorkflowRunDurationAccessibleLabel(duration: WorkflowRunDurationDisplay | null): string | undefined;
|
|
12
|
-
export declare function workflowRunDurationAccessibleLabel(duration: WorkflowRunDurationDisplay | null): string | undefined;
|
|
12
|
+
export declare function useWorkflowRunDurationAccessibleLabel(duration: WorkflowRunDurationDisplay | null, hasStarted: boolean): string | undefined;
|
|
13
|
+
export declare function workflowRunDurationAccessibleLabel(duration: WorkflowRunDurationDisplay | null, hasStarted: boolean): string | undefined;
|
|
13
14
|
//# sourceMappingURL=workflow-run-duration-label.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-run-duration-label.d.ts","sourceRoot":"","sources":["../../src/components/workflow-run-duration-label.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"workflow-run-duration-label.d.ts","sourceRoot":"","sources":["../../src/components/workflow-run-duration-label.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,iCAAiC,EAAC,MAAM,uBAAuB,CAAC;AAE7E;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG,iCAAiC,CAAC;AAE3E,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,UAAU,EACV,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAC5C,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,sCAoBA;AAED,wBAAgB,qCAAqC,CACnD,QAAQ,EAAE,0BAA0B,GAAG,IAAI,EAC3C,UAAU,EAAE,OAAO,GAClB,MAAM,GAAG,SAAS,CAGpB;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,0BAA0B,GAAG,IAAI,EAC3C,UAAU,EAAE,OAAO,GAClB,MAAM,GAAG,SAAS,CAepB"}
|
|
@@ -3,36 +3,22 @@ import { Icon } from '@shipfox/react-ui/icon';
|
|
|
3
3
|
import { useTimeTick } from '@shipfox/react-ui/time-ticker';
|
|
4
4
|
import { Code } from '@shipfox/react-ui/typography';
|
|
5
5
|
import { cn, humanDuration } from '@shipfox/react-ui/utils';
|
|
6
|
-
|
|
7
|
-
queue: 'hourglassLine',
|
|
8
|
-
run: 'timerLine'
|
|
9
|
-
};
|
|
10
|
-
const LIVE_VERB_BY_KIND = {
|
|
11
|
-
queue: 'queued',
|
|
12
|
-
run: 'running'
|
|
13
|
-
};
|
|
14
|
-
const FIXED_VERB_BY_KIND = {
|
|
15
|
-
queue: 'queued',
|
|
16
|
-
run: 'ran'
|
|
17
|
-
};
|
|
18
|
-
export function WorkflowRunDurationLabel({ duration, className }) {
|
|
6
|
+
export function WorkflowRunDurationLabel({ duration, hasStarted, className }) {
|
|
19
7
|
if (duration === null) return null;
|
|
20
|
-
const kind = durationKind(duration);
|
|
21
8
|
switch(duration.state){
|
|
22
9
|
case 'fixed':
|
|
23
10
|
{
|
|
24
11
|
const display = formatFixedDurationLabel(duration.elapsed);
|
|
12
|
+
const verb = hasStarted ? 'ran' : 'lasted';
|
|
25
13
|
return /*#__PURE__*/ _jsx(DurationText, {
|
|
26
14
|
className: className,
|
|
27
|
-
|
|
28
|
-
ariaLabel: `${FIXED_VERB_BY_KIND[kind]} ${display}`,
|
|
15
|
+
ariaLabel: `${verb} ${display}`,
|
|
29
16
|
children: display
|
|
30
17
|
});
|
|
31
18
|
}
|
|
32
19
|
case 'live':
|
|
33
20
|
return /*#__PURE__*/ _jsx(LiveDurationText, {
|
|
34
21
|
duration: duration,
|
|
35
|
-
kind: kind,
|
|
36
22
|
className: className
|
|
37
23
|
});
|
|
38
24
|
default:
|
|
@@ -42,18 +28,20 @@ export function WorkflowRunDurationLabel({ duration, className }) {
|
|
|
42
28
|
}
|
|
43
29
|
}
|
|
44
30
|
}
|
|
45
|
-
export function useWorkflowRunDurationAccessibleLabel(duration) {
|
|
31
|
+
export function useWorkflowRunDurationAccessibleLabel(duration, hasStarted) {
|
|
46
32
|
useTimeTick();
|
|
47
|
-
return workflowRunDurationAccessibleLabel(duration);
|
|
33
|
+
return workflowRunDurationAccessibleLabel(duration, hasStarted);
|
|
48
34
|
}
|
|
49
|
-
export function workflowRunDurationAccessibleLabel(duration) {
|
|
35
|
+
export function workflowRunDurationAccessibleLabel(duration, hasStarted) {
|
|
50
36
|
if (duration === null) return undefined;
|
|
51
|
-
const kind = durationKind(duration);
|
|
52
37
|
switch(duration.state){
|
|
53
38
|
case 'live':
|
|
54
|
-
return
|
|
39
|
+
return `running ${humanDuration(duration.fromIso)}`;
|
|
55
40
|
case 'fixed':
|
|
56
|
-
|
|
41
|
+
{
|
|
42
|
+
const verb = hasStarted ? 'ran' : 'lasted';
|
|
43
|
+
return `${verb} ${formatFixedDurationLabel(duration.elapsed)}`;
|
|
44
|
+
}
|
|
57
45
|
default:
|
|
58
46
|
{
|
|
59
47
|
const exhaustive = duration;
|
|
@@ -61,20 +49,16 @@ export function workflowRunDurationAccessibleLabel(duration) {
|
|
|
61
49
|
}
|
|
62
50
|
}
|
|
63
51
|
}
|
|
64
|
-
function
|
|
65
|
-
return 'kind' in duration ? duration.kind : 'run';
|
|
66
|
-
}
|
|
67
|
-
function LiveDurationText({ duration, kind, className }) {
|
|
52
|
+
function LiveDurationText({ duration, className }) {
|
|
68
53
|
useTimeTick();
|
|
69
54
|
const display = humanDuration(duration.fromIso);
|
|
70
55
|
return /*#__PURE__*/ _jsx(DurationText, {
|
|
71
56
|
className: className,
|
|
72
|
-
|
|
73
|
-
ariaLabel: `${LIVE_VERB_BY_KIND[kind]} ${display}`,
|
|
57
|
+
ariaLabel: `running ${display}`,
|
|
74
58
|
children: display
|
|
75
59
|
});
|
|
76
60
|
}
|
|
77
|
-
function DurationText({ children, className,
|
|
61
|
+
function DurationText({ children, className, ariaLabel }) {
|
|
78
62
|
return /*#__PURE__*/ _jsxs(Code, {
|
|
79
63
|
as: "span",
|
|
80
64
|
variant: "label",
|
|
@@ -82,7 +66,7 @@ function DurationText({ children, className, kind, ariaLabel }) {
|
|
|
82
66
|
className: cn('inline-flex shrink-0 items-center gap-tight tabular-nums text-foreground-neutral-subtle', className),
|
|
83
67
|
children: [
|
|
84
68
|
/*#__PURE__*/ _jsx(Icon, {
|
|
85
|
-
name:
|
|
69
|
+
name: "timerLine",
|
|
86
70
|
className: "size-12 shrink-0",
|
|
87
71
|
"aria-hidden": "true"
|
|
88
72
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/workflow-run-duration-label.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {
|
|
1
|
+
{"version":3,"sources":["../../src/components/workflow-run-duration-label.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {WorkflowRunAttemptDisplayDuration} from '#core/workflow-run.js';\n\n/**\n * A run reports one span: how long its attempt has been under way. Queueing is a job\n * execution's own affair, and the job surfaces report it there.\n */\nexport type WorkflowRunDurationDisplay = WorkflowRunAttemptDisplayDuration;\n\nexport function WorkflowRunDurationLabel({\n duration,\n hasStarted,\n className,\n}: {\n duration: WorkflowRunDurationDisplay | null;\n hasStarted: boolean;\n className?: string | undefined;\n}) {\n if (duration === null) return null;\n\n switch (duration.state) {\n case 'fixed': {\n const display = formatFixedDurationLabel(duration.elapsed);\n const verb = hasStarted ? 'ran' : 'lasted';\n return (\n <DurationText className={className} ariaLabel={`${verb} ${display}`}>\n {display}\n </DurationText>\n );\n }\n case 'live':\n return <LiveDurationText duration={duration} className={className} />;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nexport function useWorkflowRunDurationAccessibleLabel(\n duration: WorkflowRunDurationDisplay | null,\n hasStarted: boolean,\n): string | undefined {\n useTimeTick();\n return workflowRunDurationAccessibleLabel(duration, hasStarted);\n}\n\nexport function workflowRunDurationAccessibleLabel(\n duration: WorkflowRunDurationDisplay | null,\n hasStarted: boolean,\n): string | undefined {\n if (duration === null) return undefined;\n\n switch (duration.state) {\n case 'live':\n return `running ${humanDuration(duration.fromIso)}`;\n case 'fixed': {\n const verb = hasStarted ? 'ran' : 'lasted';\n return `${verb} ${formatFixedDurationLabel(duration.elapsed)}`;\n }\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nfunction LiveDurationText({\n duration,\n className,\n}: {\n duration: Extract<WorkflowRunDurationDisplay, {state: 'live'}>;\n className?: string | undefined;\n}) {\n useTimeTick();\n const display = humanDuration(duration.fromIso);\n return (\n <DurationText className={className} ariaLabel={`running ${display}`}>\n {display}\n </DurationText>\n );\n}\n\nfunction DurationText({\n children,\n className,\n ariaLabel,\n}: {\n children: string;\n className?: string | undefined;\n ariaLabel?: string | undefined;\n}) {\n return (\n <Code\n as=\"span\"\n variant=\"label\"\n aria-label={ariaLabel}\n className={cn(\n 'inline-flex shrink-0 items-center gap-tight tabular-nums text-foreground-neutral-subtle',\n className,\n )}\n >\n {/* The verb rides the accessible name: the numeric columns are fixed width and the\n status badge already says whether the run is still going. */}\n <Icon name=\"timerLine\" className=\"size-12 shrink-0\" aria-hidden=\"true\" />\n {children}\n </Code>\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<WorkflowRunDurationDisplay, {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":["Icon","useTimeTick","Code","cn","humanDuration","WorkflowRunDurationLabel","duration","hasStarted","className","state","display","formatFixedDurationLabel","elapsed","verb","DurationText","ariaLabel","LiveDurationText","exhaustive","useWorkflowRunDurationAccessibleLabel","workflowRunDurationAccessibleLabel","undefined","fromIso","children","as","variant","aria-label","name","aria-hidden","years","months","weeks","days","hours","minutes","seconds","totalDays","totalHours","pad2","value","toString","padStart"],"mappings":";AAAA,SAAQA,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,EAAE,EAAEC,aAAa,QAAO,0BAA0B;AAS1D,OAAO,SAASC,yBAAyB,EACvCC,QAAQ,EACRC,UAAU,EACVC,SAAS,EAKV;IACC,IAAIF,aAAa,MAAM,OAAO;IAE9B,OAAQA,SAASG,KAAK;QACpB,KAAK;YAAS;gBACZ,MAAMC,UAAUC,yBAAyBL,SAASM,OAAO;gBACzD,MAAMC,OAAON,aAAa,QAAQ;gBAClC,qBACE,KAACO;oBAAaN,WAAWA;oBAAWO,WAAW,GAAGF,KAAK,CAAC,EAAEH,SAAS;8BAChEA;;YAGP;QACA,KAAK;YACH,qBAAO,KAACM;gBAAiBV,UAAUA;gBAAUE,WAAWA;;QAC1D;YAAS;gBACP,MAAMS,aAAoBX;gBAC1B,OAAOW;YACT;IACF;AACF;AAEA,OAAO,SAASC,sCACdZ,QAA2C,EAC3CC,UAAmB;IAEnBN;IACA,OAAOkB,mCAAmCb,UAAUC;AACtD;AAEA,OAAO,SAASY,mCACdb,QAA2C,EAC3CC,UAAmB;IAEnB,IAAID,aAAa,MAAM,OAAOc;IAE9B,OAAQd,SAASG,KAAK;QACpB,KAAK;YACH,OAAO,CAAC,QAAQ,EAAEL,cAAcE,SAASe,OAAO,GAAG;QACrD,KAAK;YAAS;gBACZ,MAAMR,OAAON,aAAa,QAAQ;gBAClC,OAAO,GAAGM,KAAK,CAAC,EAAEF,yBAAyBL,SAASM,OAAO,GAAG;YAChE;QACA;YAAS;gBACP,MAAMK,aAAoBX;gBAC1B,OAAOW;YACT;IACF;AACF;AAEA,SAASD,iBAAiB,EACxBV,QAAQ,EACRE,SAAS,EAIV;IACCP;IACA,MAAMS,UAAUN,cAAcE,SAASe,OAAO;IAC9C,qBACE,KAACP;QAAaN,WAAWA;QAAWO,WAAW,CAAC,QAAQ,EAAEL,SAAS;kBAChEA;;AAGP;AAEA,SAASI,aAAa,EACpBQ,QAAQ,EACRd,SAAS,EACTO,SAAS,EAKV;IACC,qBACE,MAACb;QACCqB,IAAG;QACHC,SAAQ;QACRC,cAAYV;QACZP,WAAWL,GACT,2FACAK;;0BAKF,KAACR;gBAAK0B,MAAK;gBAAYlB,WAAU;gBAAmBmB,eAAY;;YAC/DL;;;AAGP;AAEA,SAASX,yBAAyB,EAChCiB,QAAQ,CAAC,EACTC,SAAS,CAAC,EACVC,QAAQ,CAAC,EACTC,OAAO,CAAC,EACRC,QAAQ,CAAC,EACTC,UAAU,CAAC,EACXC,UAAU,CAAC,EACsD;IACjE,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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-status-strip.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"job-status-strip.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAwB,eAAe,EAAC,MAAM,uBAAuB,CAAC;AA0ClF,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;AA0BD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAI9D"}
|
|
@@ -4,6 +4,7 @@ import { Code, Text } from '@shipfox/react-ui/typography';
|
|
|
4
4
|
import { cn } from '@shipfox/react-ui/utils';
|
|
5
5
|
import { getWorkflowStatusVisual } from '#components/workflow-status/status-visuals.js';
|
|
6
6
|
import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-icon.js';
|
|
7
|
+
import { deriveJobDisplayStatus } from '#core/entities/job.js';
|
|
7
8
|
/**
|
|
8
9
|
* How many glyphs fit the strip's column before it starts costing the run name width.
|
|
9
10
|
*
|
|
@@ -31,6 +32,7 @@ const COMPACT_COUNT_FORMAT = new Intl.NumberFormat('en', {
|
|
|
31
32
|
const STATUS_SEVERITY = {
|
|
32
33
|
failed: 5,
|
|
33
34
|
running: 4,
|
|
35
|
+
listening: 4,
|
|
34
36
|
pending: 3,
|
|
35
37
|
cancelled: 2,
|
|
36
38
|
skipped: 1,
|
|
@@ -38,7 +40,8 @@ const STATUS_SEVERITY = {
|
|
|
38
40
|
};
|
|
39
41
|
const NOTABLE_STATUSES = [
|
|
40
42
|
'failed',
|
|
41
|
-
'running'
|
|
43
|
+
'running',
|
|
44
|
+
'listening'
|
|
42
45
|
];
|
|
43
46
|
/**
|
|
44
47
|
* A run's jobs as a row of status glyphs, in graph order.
|
|
@@ -69,7 +72,7 @@ const NOTABLE_STATUSES = [
|
|
|
69
72
|
className: "inline-flex items-center gap-[3px]",
|
|
70
73
|
children: [
|
|
71
74
|
visible.map((job)=>/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
72
|
-
status: job
|
|
75
|
+
status: displayStatus(job),
|
|
73
76
|
size: GLYPH_SIZE,
|
|
74
77
|
// One ripple per running job, on every row, would turn a calm list into a
|
|
75
78
|
// field of pulses. The run's own glyph already carries the live edge.
|
|
@@ -114,7 +117,7 @@ function JobStatusStripTooltip({ jobs, summary }) {
|
|
|
114
117
|
// left to the counts so the tooltip stays a glance rather than a list. Only previewed jobs
|
|
115
118
|
// can be named, but the count of those left over is read off the totals, so a failure past
|
|
116
119
|
// the preview is still accounted for rather than silently missing.
|
|
117
|
-
const named = jobs.preview.filter((job)=>NOTABLE_STATUSES.includes(job
|
|
120
|
+
const named = jobs.preview.filter((job)=>NOTABLE_STATUSES.includes(displayStatus(job))).slice(0, MAX_TOOLTIP_JOB_NAMES);
|
|
118
121
|
const notableTotal = NOTABLE_STATUSES.reduce((total, status)=>total + countOf(jobs, status), 0);
|
|
119
122
|
const remaining = notableTotal - named.length;
|
|
120
123
|
return /*#__PURE__*/ _jsxs("span", {
|
|
@@ -131,7 +134,7 @@ function JobStatusStripTooltip({ jobs, summary }) {
|
|
|
131
134
|
variant: "label",
|
|
132
135
|
className: "block truncate",
|
|
133
136
|
children: [
|
|
134
|
-
getWorkflowStatusVisual(job
|
|
137
|
+
getWorkflowStatusVisual(displayStatus(job)).label.toLowerCase(),
|
|
135
138
|
" · ",
|
|
136
139
|
job.name ?? job.key
|
|
137
140
|
]
|
|
@@ -164,9 +167,10 @@ function JobStatusStripTooltip({ jobs, summary }) {
|
|
|
164
167
|
count
|
|
165
168
|
]));
|
|
166
169
|
for (const job of visible){
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
+
const status = displayStatus(job);
|
|
171
|
+
const remaining = (hidden.get(status) ?? 0) - 1;
|
|
172
|
+
if (remaining > 0) hidden.set(status, remaining);
|
|
173
|
+
else hidden.delete(status);
|
|
170
174
|
}
|
|
171
175
|
let worst = null;
|
|
172
176
|
for (const [status, count] of hidden){
|
|
@@ -181,5 +185,14 @@ export function overflowCountLabel(hiddenCount) {
|
|
|
181
185
|
function countOf(jobs, status) {
|
|
182
186
|
return jobs.statusCounts.find((entry)=>entry.status === status)?.count ?? 0;
|
|
183
187
|
}
|
|
188
|
+
function displayStatus(job) {
|
|
189
|
+
return deriveJobDisplayStatus({
|
|
190
|
+
mode: job.mode,
|
|
191
|
+
status: job.status,
|
|
192
|
+
listenerStatus: job.listenerStatus,
|
|
193
|
+
executionStatus: job.executionStatus,
|
|
194
|
+
jobExecutions: []
|
|
195
|
+
});
|
|
196
|
+
}
|
|
184
197
|
|
|
185
198
|
//# sourceMappingURL=job-status-strip.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"sourcesContent":["import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport type {JobStatus, WorkflowRunJobSummary, WorkflowRunJobs} from '#core/workflow-run.js';\n\n/**\n * How many glyphs fit the strip's column before it starts costing the run name width.\n *\n * Sized so the widest case still fits the row's 160px strip column: seven 12px glyphs with\n * 3px gaps is 102px, and the overflow indicator adds a glyph plus a count of at most five\n * monospace characters, about 56px. Overshooting does not wrap, it paints over the duration\n * column. This sits under the API's preview bound, so resizing the strip is a change here.\n */\nconst MAX_VISIBLE_JOBS = 7;\nconst GLYPH_SIZE = 12;\nconst MAX_TOOLTIP_JOB_NAMES = 6;\n\n/**\n * Above this the overflow count is abbreviated.\n *\n * Nothing caps a workflow's job count, so an exact count is unbounded in width and would\n * eventually push the strip over its column. Abbreviating keeps the label at most five\n * characters for any magnitude, and the exact figure stays in the tooltip and the strip's\n * accessible name, which is where a precise number is actually read.\n */\nconst MAX_EXACT_OVERFLOW_COUNT = 999;\nconst COMPACT_COUNT_FORMAT = new Intl.NumberFormat('en', {\n notation: 'compact',\n maximumFractionDigits: 1,\n});\n\n// Worst-first, so the overflow glyph reports the most alarming thing it is standing in for.\n// A strip that hides a failure behind eight green discs would be worse than no strip at all.\nconst STATUS_SEVERITY: Record<JobStatus, number> = {\n failed: 5,\n running: 4,\n pending: 3,\n cancelled: 2,\n skipped: 1,\n succeeded: 0,\n};\n\nconst NOTABLE_STATUSES: readonly JobStatus[] = ['failed', 'running'];\n\nexport interface JobStatusStripProps {\n jobs: WorkflowRunJobs;\n className?: string | undefined;\n}\n\n/**\n * A run's jobs as a row of status glyphs, in graph order.\n *\n * This is the reason the list is worth its width: it answers \"where did this run fail\"\n * without opening the run. Glyphs are silent to assistive tech and carry no tooltip of their\n * own; the strip speaks once, as a summary, so a 40-job run does not read out 40 labels.\n *\n * What it draws comes from the preview; what it says comes from the counts, which cover every\n * job including those the server never sent. That split is what lets the overflow glyph\n * report a failure sitting past the preview instead of quietly dropping it.\n */\nexport function JobStatusStrip({jobs, className}: JobStatusStripProps) {\n if (jobs.total === 0) return null;\n\n const visible = jobs.preview.slice(0, MAX_VISIBLE_JOBS);\n const hiddenCount = jobs.total - visible.length;\n const overflowStatus = worstHiddenStatus(jobs, visible);\n const summary = jobStatusSummary(jobs);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span\n role=\"img\"\n aria-label={summary}\n className={cn('inline-flex items-center gap-[3px]', className)}\n >\n <span aria-hidden=\"true\" className=\"inline-flex items-center gap-[3px]\">\n {visible.map((job) => (\n <WorkflowStatusIcon\n key={job.id}\n status={job.status}\n size={GLYPH_SIZE}\n // One ripple per running job, on every row, would turn a calm list into a\n // field of pulses. The run's own glyph already carries the live edge.\n ripple={false}\n tooltip={false}\n />\n ))}\n {hiddenCount > 0 && overflowStatus ? (\n <span className=\"inline-flex items-center gap-[2px]\">\n <WorkflowStatusIcon\n status={overflowStatus}\n size={GLYPH_SIZE}\n ripple={false}\n tooltip={false}\n />\n <Code\n as=\"span\"\n variant=\"label\"\n className=\"tabular-nums text-foreground-neutral-muted\"\n >\n +{overflowCountLabel(hiddenCount)}\n </Code>\n </span>\n ) : null}\n </span>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n <JobStatusStripTooltip jobs={jobs} summary={summary} />\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobStatusStripTooltip({jobs, summary}: {jobs: WorkflowRunJobs; summary: string}) {\n // Naming the jobs that need attention is the whole point of hovering; succeeded jobs are\n // left to the counts so the tooltip stays a glance rather than a list. Only previewed jobs\n // can be named, but the count of those left over is read off the totals, so a failure past\n // the preview is still accounted for rather than silently missing.\n const named = jobs.preview\n .filter((job) => NOTABLE_STATUSES.includes(job.status))\n .slice(0, MAX_TOOLTIP_JOB_NAMES);\n const notableTotal = NOTABLE_STATUSES.reduce((total, status) => total + countOf(jobs, status), 0);\n const remaining = notableTotal - named.length;\n\n return (\n <span className=\"flex max-w-[280px] flex-col gap-tight\">\n <Text as=\"span\" size=\"xs\" className=\"block\">\n {summary}\n </Text>\n {named.map((job) => (\n <Code as=\"span\" variant=\"label\" key={job.id} className=\"block truncate\">\n {getWorkflowStatusVisual(job.status).label.toLowerCase()} · {job.name ?? job.key}\n </Code>\n ))}\n {remaining > 0 ? (\n <Text as=\"span\" size=\"xs\" className=\"block text-foreground-neutral-muted\">\n and {remaining} more\n </Text>\n ) : null}\n </span>\n );\n}\n\n/** \"12 jobs: 9 succeeded, 2 failed, 1 running\" — a count is not an accessible name on its own. */\nexport function jobStatusSummary(jobs: WorkflowRunJobs): string {\n const breakdown = [...jobs.statusCounts]\n .sort((left, right) => STATUS_SEVERITY[right.status] - STATUS_SEVERITY[left.status])\n .map(({status, count}) => `${count} ${getWorkflowStatusVisual(status).label.toLowerCase()}`)\n .join(', ');\n\n return `${jobs.total} ${jobs.total === 1 ? 'job' : 'jobs'}: ${breakdown}`;\n}\n\n/**\n * The worst status among the jobs the strip is not drawing, found by subtracting what it\n * draws from the run's totals rather than by inspecting jobs it was never sent.\n */\nfunction worstHiddenStatus(\n jobs: WorkflowRunJobs,\n visible: readonly WorkflowRunJobSummary[],\n): JobStatus | null {\n const hidden = new Map(jobs.statusCounts.map(({status, count}) => [status, count]));\n for (const job of visible) {\n const remaining = (hidden.get(job.status) ?? 0) - 1;\n if (remaining > 0) hidden.set(job.status, remaining);\n else hidden.delete(job.status);\n }\n\n let worst: JobStatus | null = null;\n for (const [status, count] of hidden) {\n if (count <= 0) continue;\n if (worst === null || STATUS_SEVERITY[status] > STATUS_SEVERITY[worst]) worst = status;\n }\n return worst;\n}\n\nexport function overflowCountLabel(hiddenCount: number): string {\n return hiddenCount <= MAX_EXACT_OVERFLOW_COUNT\n ? String(hiddenCount)\n : COMPACT_COUNT_FORMAT.format(hiddenCount);\n}\n\nfunction countOf(jobs: WorkflowRunJobs, status: JobStatus): number {\n return jobs.statusCounts.find((entry) => entry.status === status)?.count ?? 0;\n}\n"],"names":["Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","getWorkflowStatusVisual","WorkflowStatusIcon","MAX_VISIBLE_JOBS","GLYPH_SIZE","MAX_TOOLTIP_JOB_NAMES","MAX_EXACT_OVERFLOW_COUNT","COMPACT_COUNT_FORMAT","Intl","NumberFormat","notation","maximumFractionDigits","STATUS_SEVERITY","failed","running","pending","cancelled","skipped","succeeded","NOTABLE_STATUSES","JobStatusStrip","jobs","className","total","visible","preview","slice","hiddenCount","length","overflowStatus","worstHiddenStatus","summary","jobStatusSummary","asChild","span","role","aria-label","aria-hidden","map","job","status","size","ripple","tooltip","id","as","variant","overflowCountLabel","JobStatusStripTooltip","named","filter","includes","notableTotal","reduce","countOf","remaining","label","toLowerCase","name","key","breakdown","statusCounts","sort","left","right","count","join","hidden","Map","get","set","delete","worst","String","format","find","entry"],"mappings":";AAAA,SAAQA,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,IAAI,EAAEC,IAAI,QAAO,+BAA+B;AACxD,SAAQC,EAAE,QAAO,0BAA0B;AAC3C,SAAQC,uBAAuB,QAAO,gDAAgD;AACtF,SAAQC,kBAAkB,QAAO,sDAAsD;AAGvF;;;;;;;CAOC,GACD,MAAMC,mBAAmB;AACzB,MAAMC,aAAa;AACnB,MAAMC,wBAAwB;AAE9B;;;;;;;CAOC,GACD,MAAMC,2BAA2B;AACjC,MAAMC,uBAAuB,IAAIC,KAAKC,YAAY,CAAC,MAAM;IACvDC,UAAU;IACVC,uBAAuB;AACzB;AAEA,4FAA4F;AAC5F,6FAA6F;AAC7F,MAAMC,kBAA6C;IACjDC,QAAQ;IACRC,SAAS;IACTC,SAAS;IACTC,WAAW;IACXC,SAAS;IACTC,WAAW;AACb;AAEA,MAAMC,mBAAyC;IAAC;IAAU;CAAU;AAOpE;;;;;;;;;;CAUC,GACD,OAAO,SAASC,eAAe,EAACC,IAAI,EAAEC,SAAS,EAAsB;IACnE,IAAID,KAAKE,KAAK,KAAK,GAAG,OAAO;IAE7B,MAAMC,UAAUH,KAAKI,OAAO,CAACC,KAAK,CAAC,GAAGvB;IACtC,MAAMwB,cAAcN,KAAKE,KAAK,GAAGC,QAAQI,MAAM;IAC/C,MAAMC,iBAAiBC,kBAAkBT,MAAMG;IAC/C,MAAMO,UAAUC,iBAAiBX;IAEjC,qBACE,MAAC1B;;0BACC,KAACE;gBAAeoC,OAAO;0BACrB,cAAA,KAACC;oBACCC,MAAK;oBACLC,cAAYL;oBACZT,WAAWtB,GAAG,sCAAsCsB;8BAEpD,cAAA,MAACY;wBAAKG,eAAY;wBAAOf,WAAU;;4BAChCE,QAAQc,GAAG,CAAC,CAACC,oBACZ,KAACrC;oCAECsC,QAAQD,IAAIC,MAAM;oCAClBC,MAAMrC;oCACN,0EAA0E;oCAC1E,sEAAsE;oCACtEsC,QAAQ;oCACRC,SAAS;mCANJJ,IAAIK,EAAE;4BASdjB,cAAc,KAAKE,+BAClB,MAACK;gCAAKZ,WAAU;;kDACd,KAACpB;wCACCsC,QAAQX;wCACRY,MAAMrC;wCACNsC,QAAQ;wCACRC,SAAS;;kDAEX,MAAC7C;wCACC+C,IAAG;wCACHC,SAAQ;wCACRxB,WAAU;;4CACX;4CACGyB,mBAAmBpB;;;;iCAGvB;;;;;0BAIV,KAAC/B;0BACC,cAAA,KAACoD;oBAAsB3B,MAAMA;oBAAMU,SAASA;;;;;AAIpD;AAEA,SAASiB,sBAAsB,EAAC3B,IAAI,EAAEU,OAAO,EAA2C;IACtF,yFAAyF;IACzF,2FAA2F;IAC3F,2FAA2F;IAC3F,mEAAmE;IACnE,MAAMkB,QAAQ5B,KAAKI,OAAO,CACvByB,MAAM,CAAC,CAACX,MAAQpB,iBAAiBgC,QAAQ,CAACZ,IAAIC,MAAM,GACpDd,KAAK,CAAC,GAAGrB;IACZ,MAAM+C,eAAejC,iBAAiBkC,MAAM,CAAC,CAAC9B,OAAOiB,SAAWjB,QAAQ+B,QAAQjC,MAAMmB,SAAS;IAC/F,MAAMe,YAAYH,eAAeH,MAAMrB,MAAM;IAE7C,qBACE,MAACM;QAAKZ,WAAU;;0BACd,KAACvB;gBAAK8C,IAAG;gBAAOJ,MAAK;gBAAKnB,WAAU;0BACjCS;;YAEFkB,MAAMX,GAAG,CAAC,CAACC,oBACV,MAACzC;oBAAK+C,IAAG;oBAAOC,SAAQ;oBAAqBxB,WAAU;;wBACpDrB,wBAAwBsC,IAAIC,MAAM,EAAEgB,KAAK,CAACC,WAAW;wBAAG;wBAAIlB,IAAImB,IAAI,IAAInB,IAAIoB,GAAG;;mBAD7CpB,IAAIK,EAAE;YAI5CW,YAAY,kBACX,MAACxD;gBAAK8C,IAAG;gBAAOJ,MAAK;gBAAKnB,WAAU;;oBAAsC;oBACnEiC;oBAAU;;iBAEf;;;AAGV;AAEA,gGAAgG,GAChG,OAAO,SAASvB,iBAAiBX,IAAqB;IACpD,MAAMuC,YAAY;WAAIvC,KAAKwC,YAAY;KAAC,CACrCC,IAAI,CAAC,CAACC,MAAMC,QAAUpD,eAAe,CAACoD,MAAMxB,MAAM,CAAC,GAAG5B,eAAe,CAACmD,KAAKvB,MAAM,CAAC,EAClFF,GAAG,CAAC,CAAC,EAACE,MAAM,EAAEyB,KAAK,EAAC,GAAK,GAAGA,MAAM,CAAC,EAAEhE,wBAAwBuC,QAAQgB,KAAK,CAACC,WAAW,IAAI,EAC1FS,IAAI,CAAC;IAER,OAAO,GAAG7C,KAAKE,KAAK,CAAC,CAAC,EAAEF,KAAKE,KAAK,KAAK,IAAI,QAAQ,OAAO,EAAE,EAAEqC,WAAW;AAC3E;AAEA;;;CAGC,GACD,SAAS9B,kBACPT,IAAqB,EACrBG,OAAyC;IAEzC,MAAM2C,SAAS,IAAIC,IAAI/C,KAAKwC,YAAY,CAACvB,GAAG,CAAC,CAAC,EAACE,MAAM,EAAEyB,KAAK,EAAC,GAAK;YAACzB;YAAQyB;SAAM;IACjF,KAAK,MAAM1B,OAAOf,QAAS;QACzB,MAAM+B,YAAY,AAACY,CAAAA,OAAOE,GAAG,CAAC9B,IAAIC,MAAM,KAAK,CAAA,IAAK;QAClD,IAAIe,YAAY,GAAGY,OAAOG,GAAG,CAAC/B,IAAIC,MAAM,EAAEe;aACrCY,OAAOI,MAAM,CAAChC,IAAIC,MAAM;IAC/B;IAEA,IAAIgC,QAA0B;IAC9B,KAAK,MAAM,CAAChC,QAAQyB,MAAM,IAAIE,OAAQ;QACpC,IAAIF,SAAS,GAAG;QAChB,IAAIO,UAAU,QAAQ5D,eAAe,CAAC4B,OAAO,GAAG5B,eAAe,CAAC4D,MAAM,EAAEA,QAAQhC;IAClF;IACA,OAAOgC;AACT;AAEA,OAAO,SAASzB,mBAAmBpB,WAAmB;IACpD,OAAOA,eAAerB,2BAClBmE,OAAO9C,eACPpB,qBAAqBmE,MAAM,CAAC/C;AAClC;AAEA,SAAS2B,QAAQjC,IAAqB,EAAEmB,MAAiB;IACvD,OAAOnB,KAAKwC,YAAY,CAACc,IAAI,CAAC,CAACC,QAAUA,MAAMpC,MAAM,KAAKA,SAASyB,SAAS;AAC9E"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"sourcesContent":["import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {deriveJobDisplayStatus, type JobDisplayStatus} from '#core/entities/job.js';\nimport type {WorkflowRunJobSummary, WorkflowRunJobs} from '#core/workflow-run.js';\n\n/**\n * How many glyphs fit the strip's column before it starts costing the run name width.\n *\n * Sized so the widest case still fits the row's 160px strip column: seven 12px glyphs with\n * 3px gaps is 102px, and the overflow indicator adds a glyph plus a count of at most five\n * monospace characters, about 56px. Overshooting does not wrap, it paints over the duration\n * column. This sits under the API's preview bound, so resizing the strip is a change here.\n */\nconst MAX_VISIBLE_JOBS = 7;\nconst GLYPH_SIZE = 12;\nconst MAX_TOOLTIP_JOB_NAMES = 6;\n\n/**\n * Above this the overflow count is abbreviated.\n *\n * Nothing caps a workflow's job count, so an exact count is unbounded in width and would\n * eventually push the strip over its column. Abbreviating keeps the label at most five\n * characters for any magnitude, and the exact figure stays in the tooltip and the strip's\n * accessible name, which is where a precise number is actually read.\n */\nconst MAX_EXACT_OVERFLOW_COUNT = 999;\nconst COMPACT_COUNT_FORMAT = new Intl.NumberFormat('en', {\n notation: 'compact',\n maximumFractionDigits: 1,\n});\n\n// Worst-first, so the overflow glyph reports the most alarming thing it is standing in for.\n// A strip that hides a failure behind eight green discs would be worse than no strip at all.\nconst STATUS_SEVERITY: Record<JobDisplayStatus, number> = {\n failed: 5,\n running: 4,\n listening: 4,\n pending: 3,\n cancelled: 2,\n skipped: 1,\n succeeded: 0,\n};\n\nconst NOTABLE_STATUSES: readonly JobDisplayStatus[] = ['failed', 'running', 'listening'];\n\nexport interface JobStatusStripProps {\n jobs: WorkflowRunJobs;\n className?: string | undefined;\n}\n\n/**\n * A run's jobs as a row of status glyphs, in graph order.\n *\n * This is the reason the list is worth its width: it answers \"where did this run fail\"\n * without opening the run. Glyphs are silent to assistive tech and carry no tooltip of their\n * own; the strip speaks once, as a summary, so a 40-job run does not read out 40 labels.\n *\n * What it draws comes from the preview; what it says comes from the counts, which cover every\n * job including those the server never sent. That split is what lets the overflow glyph\n * report a failure sitting past the preview instead of quietly dropping it.\n */\nexport function JobStatusStrip({jobs, className}: JobStatusStripProps) {\n if (jobs.total === 0) return null;\n\n const visible = jobs.preview.slice(0, MAX_VISIBLE_JOBS);\n const hiddenCount = jobs.total - visible.length;\n const overflowStatus = worstHiddenStatus(jobs, visible);\n const summary = jobStatusSummary(jobs);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span\n role=\"img\"\n aria-label={summary}\n className={cn('inline-flex items-center gap-[3px]', className)}\n >\n <span aria-hidden=\"true\" className=\"inline-flex items-center gap-[3px]\">\n {visible.map((job) => (\n <WorkflowStatusIcon\n key={job.id}\n status={displayStatus(job)}\n size={GLYPH_SIZE}\n // One ripple per running job, on every row, would turn a calm list into a\n // field of pulses. The run's own glyph already carries the live edge.\n ripple={false}\n tooltip={false}\n />\n ))}\n {hiddenCount > 0 && overflowStatus ? (\n <span className=\"inline-flex items-center gap-[2px]\">\n <WorkflowStatusIcon\n status={overflowStatus}\n size={GLYPH_SIZE}\n ripple={false}\n tooltip={false}\n />\n <Code\n as=\"span\"\n variant=\"label\"\n className=\"tabular-nums text-foreground-neutral-muted\"\n >\n +{overflowCountLabel(hiddenCount)}\n </Code>\n </span>\n ) : null}\n </span>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n <JobStatusStripTooltip jobs={jobs} summary={summary} />\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobStatusStripTooltip({jobs, summary}: {jobs: WorkflowRunJobs; summary: string}) {\n // Naming the jobs that need attention is the whole point of hovering; succeeded jobs are\n // left to the counts so the tooltip stays a glance rather than a list. Only previewed jobs\n // can be named, but the count of those left over is read off the totals, so a failure past\n // the preview is still accounted for rather than silently missing.\n const named = jobs.preview\n .filter((job) => NOTABLE_STATUSES.includes(displayStatus(job)))\n .slice(0, MAX_TOOLTIP_JOB_NAMES);\n const notableTotal = NOTABLE_STATUSES.reduce((total, status) => total + countOf(jobs, status), 0);\n const remaining = notableTotal - named.length;\n\n return (\n <span className=\"flex max-w-[280px] flex-col gap-tight\">\n <Text as=\"span\" size=\"xs\" className=\"block\">\n {summary}\n </Text>\n {named.map((job) => (\n <Code as=\"span\" variant=\"label\" key={job.id} className=\"block truncate\">\n {getWorkflowStatusVisual(displayStatus(job)).label.toLowerCase()} · {job.name ?? job.key}\n </Code>\n ))}\n {remaining > 0 ? (\n <Text as=\"span\" size=\"xs\" className=\"block text-foreground-neutral-muted\">\n and {remaining} more\n </Text>\n ) : null}\n </span>\n );\n}\n\n/** \"12 jobs: 9 succeeded, 2 failed, 1 running\" — a count is not an accessible name on its own. */\nexport function jobStatusSummary(jobs: WorkflowRunJobs): string {\n const breakdown = [...jobs.statusCounts]\n .sort((left, right) => STATUS_SEVERITY[right.status] - STATUS_SEVERITY[left.status])\n .map(({status, count}) => `${count} ${getWorkflowStatusVisual(status).label.toLowerCase()}`)\n .join(', ');\n\n return `${jobs.total} ${jobs.total === 1 ? 'job' : 'jobs'}: ${breakdown}`;\n}\n\n/**\n * The worst status among the jobs the strip is not drawing, found by subtracting what it\n * draws from the run's totals rather than by inspecting jobs it was never sent.\n */\nfunction worstHiddenStatus(\n jobs: WorkflowRunJobs,\n visible: readonly WorkflowRunJobSummary[],\n): JobDisplayStatus | null {\n const hidden = new Map(jobs.statusCounts.map(({status, count}) => [status, count]));\n for (const job of visible) {\n const status = displayStatus(job);\n const remaining = (hidden.get(status) ?? 0) - 1;\n if (remaining > 0) hidden.set(status, remaining);\n else hidden.delete(status);\n }\n\n let worst: JobDisplayStatus | null = null;\n for (const [status, count] of hidden) {\n if (count <= 0) continue;\n if (worst === null || STATUS_SEVERITY[status] > STATUS_SEVERITY[worst]) worst = status;\n }\n return worst;\n}\n\nexport function overflowCountLabel(hiddenCount: number): string {\n return hiddenCount <= MAX_EXACT_OVERFLOW_COUNT\n ? String(hiddenCount)\n : COMPACT_COUNT_FORMAT.format(hiddenCount);\n}\n\nfunction countOf(jobs: WorkflowRunJobs, status: JobDisplayStatus): number {\n return jobs.statusCounts.find((entry) => entry.status === status)?.count ?? 0;\n}\n\nfunction displayStatus(job: WorkflowRunJobSummary): JobDisplayStatus {\n return deriveJobDisplayStatus({\n mode: job.mode,\n status: job.status,\n listenerStatus: job.listenerStatus,\n executionStatus: job.executionStatus,\n jobExecutions: [],\n });\n}\n"],"names":["Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","getWorkflowStatusVisual","WorkflowStatusIcon","deriveJobDisplayStatus","MAX_VISIBLE_JOBS","GLYPH_SIZE","MAX_TOOLTIP_JOB_NAMES","MAX_EXACT_OVERFLOW_COUNT","COMPACT_COUNT_FORMAT","Intl","NumberFormat","notation","maximumFractionDigits","STATUS_SEVERITY","failed","running","listening","pending","cancelled","skipped","succeeded","NOTABLE_STATUSES","JobStatusStrip","jobs","className","total","visible","preview","slice","hiddenCount","length","overflowStatus","worstHiddenStatus","summary","jobStatusSummary","asChild","span","role","aria-label","aria-hidden","map","job","status","displayStatus","size","ripple","tooltip","id","as","variant","overflowCountLabel","JobStatusStripTooltip","named","filter","includes","notableTotal","reduce","countOf","remaining","label","toLowerCase","name","key","breakdown","statusCounts","sort","left","right","count","join","hidden","Map","get","set","delete","worst","String","format","find","entry","mode","listenerStatus","executionStatus","jobExecutions"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-run-row.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/workflow-run-row.tsx"],"names":[],"mappings":"AAiBA,OAAO,EACL,KAAK,mBAAmB,
|
|
1
|
+
{"version":3,"file":"workflow-run-row.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/workflow-run-row.tsx"],"names":[],"mappings":"AAiBA,OAAO,EACL,KAAK,mBAAmB,EAIzB,MAAM,uBAAuB,CAAC;AAI/B,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,aAAa,EACb,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,mBAAmB,EAAE,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,+BAaA;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,aAAa,EACb,WAAW,GACZ,EAAE;IACD,GAAG,EAAE,mBAAmB,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,+BA4FA"}
|
|
@@ -10,7 +10,7 @@ import { useWorkflowRunDurationAccessibleLabel, WorkflowRunDurationLabel } from
|
|
|
10
10
|
import { formatWorkflowRunNumberLabel, WorkflowRunNumberLabel } from '#components/workflow-run-number-label.js';
|
|
11
11
|
import { getWorkflowStatusVisual } from '#components/workflow-status/status-visuals.js';
|
|
12
12
|
import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-icon.js';
|
|
13
|
-
import { workflowRunActor, workflowRunBranchLabel, workflowRunCommitLabel
|
|
13
|
+
import { workflowRunActor, workflowRunBranchLabel, workflowRunCommitLabel } from '#core/workflow-run.js';
|
|
14
14
|
import { withoutWorkflowRunSelectionSearch } from '#core/workflow-run-url-state.js';
|
|
15
15
|
import { JobStatusStrip, jobStatusSummary } from './job-status-strip.js';
|
|
16
16
|
export function WorkflowRunRowList({ runs, workspaceSlug, projectSlug }) {
|
|
@@ -40,9 +40,11 @@ export function WorkflowRunRowList({ runs, workspaceSlug, projectSlug }) {
|
|
|
40
40
|
* column capped at 1120px with 24px of page padding on each side: a 1280px viewport leaves
|
|
41
41
|
* the row 1072px, and a 1024px viewport leaves it 976px.
|
|
42
42
|
*/ export function WorkflowRunRow({ run, workspaceSlug, projectSlug }) {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
43
|
+
const duration = run.runAttempt.displayDuration;
|
|
44
|
+
const hasStarted = run.jobs.hasStartedJobExecution;
|
|
45
|
+
const durationLabel = useWorkflowRunDurationAccessibleLabel(duration, hasStarted);
|
|
46
|
+
const status = run.runAttempt.status;
|
|
47
|
+
const statusLabel = getWorkflowStatusVisual(status).label;
|
|
46
48
|
const runNumberLabel = formatWorkflowRunNumberLabel(run);
|
|
47
49
|
const branch = workflowRunBranchLabel(run);
|
|
48
50
|
const commit = workflowRunCommitLabel(run);
|
|
@@ -50,7 +52,7 @@ export function WorkflowRunRowList({ runs, workspaceSlug, projectSlug }) {
|
|
|
50
52
|
const body = /*#__PURE__*/ _jsxs(_Fragment, {
|
|
51
53
|
children: [
|
|
52
54
|
/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
53
|
-
status:
|
|
55
|
+
status: status,
|
|
54
56
|
size: 14,
|
|
55
57
|
className: "shrink-0"
|
|
56
58
|
}),
|
|
@@ -103,7 +105,8 @@ export function WorkflowRunRowList({ runs, workspaceSlug, projectSlug }) {
|
|
|
103
105
|
/*#__PURE__*/ _jsx("span", {
|
|
104
106
|
className: "flex w-64 justify-end",
|
|
105
107
|
children: /*#__PURE__*/ _jsx(WorkflowRunDurationLabel, {
|
|
106
|
-
duration:
|
|
108
|
+
duration: duration,
|
|
109
|
+
hasStarted: hasStarted
|
|
107
110
|
})
|
|
108
111
|
}),
|
|
109
112
|
/*#__PURE__*/ _jsx(Code, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-list/workflow-run-row.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {\n useWorkflowRunDurationAccessibleLabel,\n WorkflowRunDurationLabel,\n} from '#components/workflow-run-duration-label.js';\nimport {\n formatWorkflowRunNumberLabel,\n WorkflowRunNumberLabel,\n} from '#components/workflow-run-number-label.js';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n type WorkflowRunListItem,\n workflowRunActor,\n workflowRunBranchLabel,\n workflowRunCommitLabel,\n workflowRunListItemDisplay,\n} from '#core/workflow-run.js';\nimport {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';\nimport {JobStatusStrip, jobStatusSummary} from './job-status-strip.js';\n\nexport function WorkflowRunRowList({\n runs,\n workspaceSlug,\n projectSlug,\n}: {\n runs: WorkflowRunListItem[];\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n}) {\n return (\n // A container, not the viewport, drives the row's breakpoints. The list is capped at\n // 1120px inside a wider page, so what a row can afford is its own width; keying off the\n // viewport would keep the job strip hidden on a wide screen or crush it on a narrow one.\n <ul className=\"@container divide-y divide-border-neutral-base border-t border-border-neutral-base\">\n {runs.map((run) => (\n <li key={run.id}>\n <WorkflowRunRow run={run} workspaceSlug={workspaceSlug} projectSlug={projectSlug} />\n </li>\n ))}\n </ul>\n );\n}\n\n/**\n * One run at full width.\n *\n * A single 44px line once the list is at least 976px wide, reflowing to two lines below that:\n * the identity and the numerics keep their places on line one, and the provenance metadata\n * drops beneath. The numeric columns are fixed-width so duration and time form real columns\n * down the list rather than tracking each row's name length.\n *\n * The thresholds are the container widths the spec's viewport breakpoints produce, given a\n * column capped at 1120px with 24px of page padding on each side: a 1280px viewport leaves\n * the row 1072px, and a 1024px viewport leaves it 976px.\n */\nexport function WorkflowRunRow({\n run,\n workspaceSlug,\n projectSlug,\n}: {\n run: WorkflowRunListItem;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n}) {\n const display = workflowRunListItemDisplay(run);\n const durationLabel = useWorkflowRunDurationAccessibleLabel(display.duration);\n const statusLabel = getWorkflowStatusVisual(display.status).label;\n const runNumberLabel = formatWorkflowRunNumberLabel(run);\n const branch = workflowRunBranchLabel(run);\n const commit = workflowRunCommitLabel(run);\n const actor = workflowRunActor(run);\n\n const body = (\n <>\n <WorkflowStatusIcon status={display.status} size={14} className=\"shrink-0\" />\n\n <div className=\"flex min-w-0 flex-1 flex-col gap-tight @min-[976px]:flex-row @min-[976px]:items-center @min-[976px]:gap-cluster\">\n <span className=\"flex min-w-0 items-center gap-inline @min-[976px]:flex-1\">\n <Code variant=\"label\" bold className=\"truncate text-foreground-neutral-base\">\n {run.name}\n </Code>\n {run.number !== null ? <WorkflowRunNumberLabel run={run} /> : null}\n </span>\n\n <span className=\"flex min-w-0 flex-wrap items-center gap-inline text-foreground-neutral-subtle @min-[976px]:flex-nowrap @min-[976px]:shrink-0\">\n {run.triggerDisplayLabel ? <TriggerLabel run={run} /> : null}\n {branch ? <BranchLabel branch={branch} isPullRequest={branch.startsWith('#')} /> : null}\n {commit ? <CommitLabel commit={commit} /> : null}\n {actor ? <ActorLabel actor={actor} /> : null}\n </span>\n </div>\n\n <span className=\"flex shrink-0 items-center gap-cluster\">\n {/* Fixed at the strip's worst case (see MAX_VISIBLE_JOBS) so the overflow count cannot\n paint over the duration, and kept even when a run has no jobs planned yet, so the\n numerics stay in line down the list instead of stepping in and out. */}\n <span className=\"hidden w-160 @min-[1040px]:flex\">\n <JobStatusStrip jobs={run.jobs} />\n </span>\n <span className=\"flex w-64 justify-end\">\n <WorkflowRunDurationLabel duration={display.duration} />\n </span>\n <Code\n variant=\"label\"\n className=\"w-64 shrink-0 text-right tabular-nums text-foreground-neutral-muted\"\n >\n <RelativeTime value={run.createdAt} />\n </Code>\n </span>\n </>\n );\n\n // Rows run edge to edge inside the list's scroll container, which would clip the standard\n // outset focus ring, so this one is inset per the design system's focus-ring rule.\n const rowClassName =\n 'flex w-full min-w-0 items-center gap-inline px-row py-row text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-[inset_0_0_0_2px_var(--color-primary-500)] focus-visible:outline-none @min-[976px]:h-44 @min-[976px]:py-0';\n\n // Optimistic manual runs (temp-<uuid>) have no detail page until the canonical row\n // replaces them on the next poll, so they render non-interactively instead of as a link\n // that would navigate to a workflow run id the detail route rejects.\n if (run.isTemporary || !workspaceSlug || !projectSlug) {\n return <div className={rowClassName}>{body}</div>;\n }\n\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId: run.id}}\n search={\n ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never\n }\n // An aria-label is the link's whole accessible name, so anything inside the row that\n // carries its own label (the status glyph, the job strip) goes unspoken unless it is\n // curated in here. The job breakdown is the reason this list is worth its width, so it\n // belongs in what a tabbing user hears, not only in browse mode.\n aria-label={[\n run.name,\n runNumberLabel,\n statusLabel,\n durationLabel,\n run.triggerLabel,\n branch ? `branch ${branch}` : undefined,\n actor ? `by ${actor}` : undefined,\n run.jobs.total > 0 ? jobStatusSummary(run.jobs) : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ')}\n className={rowClassName}\n >\n {body}\n </Link>\n );\n}\n\nfunction TriggerLabel({run}: {run: WorkflowRunListItem}) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"flex min-w-0 max-w-[140px] items-center gap-tight outline-none\">\n <TriggerSourceIcon\n provider={run.triggerProvider}\n source={run.triggerSource}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0 text-foreground-neutral-muted\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {run.triggerDisplayLabel}\n </Text>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {run.triggerLabel}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction BranchLabel({branch, isPullRequest}: {branch: string; isPullRequest: boolean}) {\n return (\n <MetadataChip\n icon={isPullRequest ? 'gitPullRequestLine' : 'gitBranchLine'}\n title={isPullRequest ? `Pull request ${branch}` : `Branch ${branch}`}\n className=\"max-w-[180px]\"\n >\n {branch}\n </MetadataChip>\n );\n}\n\nfunction CommitLabel({commit}: {commit: string}) {\n return (\n <MetadataChip icon=\"gitCommitLine\" title={`Commit ${commit}`}>\n {commit}\n </MetadataChip>\n );\n}\n\nfunction ActorLabel({actor}: {actor: string}) {\n return (\n <MetadataChip\n icon=\"userLine\"\n title={`Triggered by ${actor}`}\n // Dropped once the row is two lines, where the provenance line has to stay readable on\n // a phone.\n className=\"hidden max-w-[120px] @min-[976px]:flex\"\n >\n {actor}\n </MetadataChip>\n );\n}\n\n/**\n * A ref, SHA, or handle: monospace because it is content a user copies or pattern-matches,\n * with the icon carrying the kind so the value itself never needs a written label.\n */\nfunction MetadataChip({\n icon,\n title,\n className,\n children,\n}: {\n icon: IconName;\n title: string;\n className?: string | undefined;\n children: string;\n}) {\n return (\n <span className={cn('flex min-w-0 shrink-0 items-center gap-tight', className)} title={title}>\n <Icon name={icon} className=\"size-12 shrink-0 text-foreground-neutral-muted\" aria-hidden />\n <Code as=\"span\" variant=\"label\" className=\"min-w-0 truncate\">\n {children}\n </Code>\n </span>\n );\n}\n"],"names":["TriggerSourceIcon","Icon","RelativeTime","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","Link","useWorkflowRunDurationAccessibleLabel","WorkflowRunDurationLabel","formatWorkflowRunNumberLabel","WorkflowRunNumberLabel","getWorkflowStatusVisual","WorkflowStatusIcon","workflowRunActor","workflowRunBranchLabel","workflowRunCommitLabel","workflowRunListItemDisplay","withoutWorkflowRunSelectionSearch","JobStatusStrip","jobStatusSummary","WorkflowRunRowList","runs","workspaceSlug","projectSlug","ul","className","map","run","li","WorkflowRunRow","id","display","durationLabel","duration","statusLabel","status","label","runNumberLabel","branch","commit","actor","body","size","div","span","variant","bold","name","number","triggerDisplayLabel","TriggerLabel","BranchLabel","isPullRequest","startsWith","CommitLabel","ActorLabel","jobs","value","createdAt","rowClassName","isTemporary","to","params","workflowRunId","search","previous","aria-label","triggerLabel","undefined","total","filter","part","Boolean","join","asChild","provider","triggerProvider","source","triggerSource","aria-hidden","as","MetadataChip","icon","title","children"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-list/workflow-run-row.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {\n useWorkflowRunDurationAccessibleLabel,\n WorkflowRunDurationLabel,\n} from '#components/workflow-run-duration-label.js';\nimport {\n formatWorkflowRunNumberLabel,\n WorkflowRunNumberLabel,\n} from '#components/workflow-run-number-label.js';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n type WorkflowRunListItem,\n workflowRunActor,\n workflowRunBranchLabel,\n workflowRunCommitLabel,\n} from '#core/workflow-run.js';\nimport {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';\nimport {JobStatusStrip, jobStatusSummary} from './job-status-strip.js';\n\nexport function WorkflowRunRowList({\n runs,\n workspaceSlug,\n projectSlug,\n}: {\n runs: WorkflowRunListItem[];\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n}) {\n return (\n // A container, not the viewport, drives the row's breakpoints. The list is capped at\n // 1120px inside a wider page, so what a row can afford is its own width; keying off the\n // viewport would keep the job strip hidden on a wide screen or crush it on a narrow one.\n <ul className=\"@container divide-y divide-border-neutral-base border-t border-border-neutral-base\">\n {runs.map((run) => (\n <li key={run.id}>\n <WorkflowRunRow run={run} workspaceSlug={workspaceSlug} projectSlug={projectSlug} />\n </li>\n ))}\n </ul>\n );\n}\n\n/**\n * One run at full width.\n *\n * A single 44px line once the list is at least 976px wide, reflowing to two lines below that:\n * the identity and the numerics keep their places on line one, and the provenance metadata\n * drops beneath. The numeric columns are fixed-width so duration and time form real columns\n * down the list rather than tracking each row's name length.\n *\n * The thresholds are the container widths the spec's viewport breakpoints produce, given a\n * column capped at 1120px with 24px of page padding on each side: a 1280px viewport leaves\n * the row 1072px, and a 1024px viewport leaves it 976px.\n */\nexport function WorkflowRunRow({\n run,\n workspaceSlug,\n projectSlug,\n}: {\n run: WorkflowRunListItem;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n}) {\n const duration = run.runAttempt.displayDuration;\n const hasStarted = run.jobs.hasStartedJobExecution;\n const durationLabel = useWorkflowRunDurationAccessibleLabel(duration, hasStarted);\n const status = run.runAttempt.status;\n const statusLabel = getWorkflowStatusVisual(status).label;\n const runNumberLabel = formatWorkflowRunNumberLabel(run);\n const branch = workflowRunBranchLabel(run);\n const commit = workflowRunCommitLabel(run);\n const actor = workflowRunActor(run);\n\n const body = (\n <>\n <WorkflowStatusIcon status={status} size={14} className=\"shrink-0\" />\n\n <div className=\"flex min-w-0 flex-1 flex-col gap-tight @min-[976px]:flex-row @min-[976px]:items-center @min-[976px]:gap-cluster\">\n <span className=\"flex min-w-0 items-center gap-inline @min-[976px]:flex-1\">\n <Code variant=\"label\" bold className=\"truncate text-foreground-neutral-base\">\n {run.name}\n </Code>\n {run.number !== null ? <WorkflowRunNumberLabel run={run} /> : null}\n </span>\n\n <span className=\"flex min-w-0 flex-wrap items-center gap-inline text-foreground-neutral-subtle @min-[976px]:flex-nowrap @min-[976px]:shrink-0\">\n {run.triggerDisplayLabel ? <TriggerLabel run={run} /> : null}\n {branch ? <BranchLabel branch={branch} isPullRequest={branch.startsWith('#')} /> : null}\n {commit ? <CommitLabel commit={commit} /> : null}\n {actor ? <ActorLabel actor={actor} /> : null}\n </span>\n </div>\n\n <span className=\"flex shrink-0 items-center gap-cluster\">\n {/* Fixed at the strip's worst case (see MAX_VISIBLE_JOBS) so the overflow count cannot\n paint over the duration, and kept even when a run has no jobs planned yet, so the\n numerics stay in line down the list instead of stepping in and out. */}\n <span className=\"hidden w-160 @min-[1040px]:flex\">\n <JobStatusStrip jobs={run.jobs} />\n </span>\n <span className=\"flex w-64 justify-end\">\n <WorkflowRunDurationLabel duration={duration} hasStarted={hasStarted} />\n </span>\n <Code\n variant=\"label\"\n className=\"w-64 shrink-0 text-right tabular-nums text-foreground-neutral-muted\"\n >\n <RelativeTime value={run.createdAt} />\n </Code>\n </span>\n </>\n );\n\n // Rows run edge to edge inside the list's scroll container, which would clip the standard\n // outset focus ring, so this one is inset per the design system's focus-ring rule.\n const rowClassName =\n 'flex w-full min-w-0 items-center gap-inline px-row py-row text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-[inset_0_0_0_2px_var(--color-primary-500)] focus-visible:outline-none @min-[976px]:h-44 @min-[976px]:py-0';\n\n // Optimistic manual runs (temp-<uuid>) have no detail page until the canonical row\n // replaces them on the next poll, so they render non-interactively instead of as a link\n // that would navigate to a workflow run id the detail route rejects.\n if (run.isTemporary || !workspaceSlug || !projectSlug) {\n return <div className={rowClassName}>{body}</div>;\n }\n\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId: run.id}}\n search={\n ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never\n }\n // An aria-label is the link's whole accessible name, so anything inside the row that\n // carries its own label (the status glyph, the job strip) goes unspoken unless it is\n // curated in here. The job breakdown is the reason this list is worth its width, so it\n // belongs in what a tabbing user hears, not only in browse mode.\n aria-label={[\n run.name,\n runNumberLabel,\n statusLabel,\n durationLabel,\n run.triggerLabel,\n branch ? `branch ${branch}` : undefined,\n actor ? `by ${actor}` : undefined,\n run.jobs.total > 0 ? jobStatusSummary(run.jobs) : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ')}\n className={rowClassName}\n >\n {body}\n </Link>\n );\n}\n\nfunction TriggerLabel({run}: {run: WorkflowRunListItem}) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"flex min-w-0 max-w-[140px] items-center gap-tight outline-none\">\n <TriggerSourceIcon\n provider={run.triggerProvider}\n source={run.triggerSource}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0 text-foreground-neutral-muted\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {run.triggerDisplayLabel}\n </Text>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {run.triggerLabel}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction BranchLabel({branch, isPullRequest}: {branch: string; isPullRequest: boolean}) {\n return (\n <MetadataChip\n icon={isPullRequest ? 'gitPullRequestLine' : 'gitBranchLine'}\n title={isPullRequest ? `Pull request ${branch}` : `Branch ${branch}`}\n className=\"max-w-[180px]\"\n >\n {branch}\n </MetadataChip>\n );\n}\n\nfunction CommitLabel({commit}: {commit: string}) {\n return (\n <MetadataChip icon=\"gitCommitLine\" title={`Commit ${commit}`}>\n {commit}\n </MetadataChip>\n );\n}\n\nfunction ActorLabel({actor}: {actor: string}) {\n return (\n <MetadataChip\n icon=\"userLine\"\n title={`Triggered by ${actor}`}\n // Dropped once the row is two lines, where the provenance line has to stay readable on\n // a phone.\n className=\"hidden max-w-[120px] @min-[976px]:flex\"\n >\n {actor}\n </MetadataChip>\n );\n}\n\n/**\n * A ref, SHA, or handle: monospace because it is content a user copies or pattern-matches,\n * with the icon carrying the kind so the value itself never needs a written label.\n */\nfunction MetadataChip({\n icon,\n title,\n className,\n children,\n}: {\n icon: IconName;\n title: string;\n className?: string | undefined;\n children: string;\n}) {\n return (\n <span className={cn('flex min-w-0 shrink-0 items-center gap-tight', className)} title={title}>\n <Icon name={icon} className=\"size-12 shrink-0 text-foreground-neutral-muted\" aria-hidden />\n <Code as=\"span\" variant=\"label\" className=\"min-w-0 truncate\">\n {children}\n </Code>\n </span>\n );\n}\n"],"names":["TriggerSourceIcon","Icon","RelativeTime","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","Link","useWorkflowRunDurationAccessibleLabel","WorkflowRunDurationLabel","formatWorkflowRunNumberLabel","WorkflowRunNumberLabel","getWorkflowStatusVisual","WorkflowStatusIcon","workflowRunActor","workflowRunBranchLabel","workflowRunCommitLabel","withoutWorkflowRunSelectionSearch","JobStatusStrip","jobStatusSummary","WorkflowRunRowList","runs","workspaceSlug","projectSlug","ul","className","map","run","li","WorkflowRunRow","id","duration","runAttempt","displayDuration","hasStarted","jobs","hasStartedJobExecution","durationLabel","status","statusLabel","label","runNumberLabel","branch","commit","actor","body","size","div","span","variant","bold","name","number","triggerDisplayLabel","TriggerLabel","BranchLabel","isPullRequest","startsWith","CommitLabel","ActorLabel","value","createdAt","rowClassName","isTemporary","to","params","workflowRunId","search","previous","aria-label","triggerLabel","undefined","total","filter","part","Boolean","join","asChild","provider","triggerProvider","source","triggerSource","aria-hidden","as","MetadataChip","icon","title","children"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-run-summary.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-summary/workflow-run-summary.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,
|
|
1
|
+
{"version":3,"file":"workflow-run-summary.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-summary/workflow-run-summary.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAC;AAa/B,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,GAAG,EAAE,iBAAiB,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC7D,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED,wBAAgB,kBAAkB,CAAC,EACjC,aAAa,EACb,WAAW,EACX,GAAG,EACH,UAAkB,EAClB,QAAQ,EACR,YAAoB,EACpB,OAAO,EACP,aAAa,GACd,EAAE,uBAAuB,+BAoIzB"}
|
|
@@ -8,22 +8,18 @@ import { useIsTextTruncated } from '@shipfox/react-ui/hooks';
|
|
|
8
8
|
import { RelativeTime } from '@shipfox/react-ui/relative-time';
|
|
9
9
|
import { TimeTickerProvider } from '@shipfox/react-ui/time-ticker';
|
|
10
10
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@shipfox/react-ui/tooltip';
|
|
11
|
-
import {
|
|
11
|
+
import { Header, Text } from '@shipfox/react-ui/typography';
|
|
12
12
|
import { useId } from 'react';
|
|
13
13
|
import { WorkflowRunNumberLabel } from '#components/workflow-run-number-label.js';
|
|
14
|
-
import { isWorkflowRunTerminal, WORKFLOW_RUN_STATUSES
|
|
14
|
+
import { isWorkflowRunTerminal, WORKFLOW_RUN_STATUSES } from '#core/workflow-run.js';
|
|
15
15
|
import { WorkflowRunDurationLabel } from '../workflow-run-duration-label.js';
|
|
16
16
|
import { getWorkflowStatusVisual } from '../workflow-status/status-visuals.js';
|
|
17
17
|
import { WorkflowRunAttemptSwitcher } from './workflow-run-attempt-switcher.js';
|
|
18
|
-
const STATUS_BADGE_LABEL_WIDTH_CH = Math.max(...
|
|
19
|
-
...WORKFLOW_RUN_STATUSES,
|
|
20
|
-
'queued'
|
|
21
|
-
].map((status)=>getWorkflowStatusVisual(status).label.length));
|
|
18
|
+
const STATUS_BADGE_LABEL_WIDTH_CH = Math.max(...WORKFLOW_RUN_STATUSES.map((status)=>getWorkflowStatusVisual(status).label.length));
|
|
22
19
|
const RERUN_BUTTON_SURFACE_CLASS_NAME = 'bg-background-neutral-base hover:bg-background-neutral-hover active:bg-background-neutral-pressed';
|
|
23
20
|
export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling = false, onCancel, rerunPending = false, onRerun, latestAttempt }) {
|
|
24
21
|
const headingId = useId();
|
|
25
|
-
const
|
|
26
|
-
const status = getWorkflowStatusVisual(display.status);
|
|
22
|
+
const status = getWorkflowStatusVisual(run.runAttempt.status);
|
|
27
23
|
const action = workflowRunActionForRun(run);
|
|
28
24
|
const hasAction = canRenderWorkflowRunAction(action, onCancel, onRerun);
|
|
29
25
|
const attemptSwitcher = latestAttempt && latestAttempt > 1 && workspaceSlug && projectSlug ? {
|
|
@@ -31,10 +27,8 @@ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling
|
|
|
31
27
|
projectSlug,
|
|
32
28
|
latestAttempt
|
|
33
29
|
} : null;
|
|
34
|
-
const displayDuration =
|
|
35
|
-
|
|
36
|
-
// and the graph already say which one it is.
|
|
37
|
-
const blockingJob = display.status === 'queued' && run.jobs.length > 1 ? workflowRunBlockingJob(run.jobs) : null;
|
|
30
|
+
const displayDuration = run.runAttempt.displayDuration;
|
|
31
|
+
const hasStarted = run.hasStartedJobExecution;
|
|
38
32
|
const { ref: headingTextRef, isTruncated: isHeadingTruncated } = useIsTextTruncated(run.name);
|
|
39
33
|
return /*#__PURE__*/ _jsx(TimeTickerProvider, {
|
|
40
34
|
intervalMs: 1000,
|
|
@@ -169,31 +163,10 @@ export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling
|
|
|
169
163
|
/*#__PURE__*/ _jsx(MetadataSeparator, {}),
|
|
170
164
|
/*#__PURE__*/ _jsx(WorkflowRunDurationLabel, {
|
|
171
165
|
duration: displayDuration,
|
|
166
|
+
hasStarted: hasStarted,
|
|
172
167
|
className: "text-foreground-neutral-subtle"
|
|
173
168
|
})
|
|
174
169
|
]
|
|
175
|
-
}) : null,
|
|
176
|
-
blockingJob ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
177
|
-
children: [
|
|
178
|
-
/*#__PURE__*/ _jsx(MetadataSeparator, {}),
|
|
179
|
-
/*#__PURE__*/ _jsxs("span", {
|
|
180
|
-
className: "flex min-w-0 items-center gap-tight",
|
|
181
|
-
children: [
|
|
182
|
-
/*#__PURE__*/ _jsx(Text, {
|
|
183
|
-
as: "span",
|
|
184
|
-
size: "xs",
|
|
185
|
-
className: "shrink-0",
|
|
186
|
-
children: "waiting on"
|
|
187
|
-
}),
|
|
188
|
-
/*#__PURE__*/ _jsx(Code, {
|
|
189
|
-
as: "span",
|
|
190
|
-
variant: "label",
|
|
191
|
-
className: "min-w-0 truncate",
|
|
192
|
-
children: blockingJob.displayName
|
|
193
|
-
})
|
|
194
|
-
]
|
|
195
|
-
})
|
|
196
|
-
]
|
|
197
170
|
}) : null
|
|
198
171
|
]
|
|
199
172
|
})
|