@shipfox/client-workflows 15.0.0 → 17.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.css +4 -0
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +41 -0
- package/dist/components/identifier/identifier.js +2 -2
- package/dist/components/identifier/identifier.js.map +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.js +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -1
- package/dist/components/job-detail/job-context-panel.d.ts +6 -0
- package/dist/components/job-detail/job-context-panel.d.ts.map +1 -0
- package/dist/components/job-detail/job-context-panel.js +224 -0
- package/dist/components/job-detail/job-context-panel.js.map +1 -0
- package/dist/components/job-detail/job-detail-header.d.ts +10 -1
- package/dist/components/job-detail/job-detail-header.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-header.js +58 -39
- package/dist/components/job-detail/job-detail-header.js.map +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-view.js +92 -41
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/job-empty-states.d.ts.map +1 -1
- package/dist/components/job-detail/job-empty-states.js +19 -10
- package/dist/components/job-detail/job-empty-states.js.map +1 -1
- package/dist/components/job-detail/job-execution-switcher.js +4 -4
- package/dist/components/job-detail/job-execution-switcher.js.map +1 -1
- package/dist/components/job-detail/json-code.d.ts +14 -0
- package/dist/components/job-detail/json-code.d.ts.map +1 -0
- package/dist/components/job-detail/json-code.js +93 -0
- package/dist/components/job-detail/json-code.js.map +1 -0
- package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.js +9 -6
- package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
- package/dist/components/job-detail/step-troubleshooting.d.ts +18 -0
- package/dist/components/job-detail/step-troubleshooting.d.ts.map +1 -0
- package/dist/components/job-detail/step-troubleshooting.js +493 -0
- package/dist/components/job-detail/step-troubleshooting.js.map +1 -0
- package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +5 -4
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/job-graph/job-node.js +3 -3
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/step-list/index.d.ts +1 -0
- package/dist/components/step-list/index.d.ts.map +1 -1
- package/dist/components/step-list/index.js.map +1 -1
- package/dist/components/step-list/step-list-model.d.ts.map +1 -1
- package/dist/components/step-list/step-list-model.js +5 -0
- package/dist/components/step-list/step-list-model.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +5 -1
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js +117 -82
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-duration-label.js +1 -1
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +6 -6
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js +4 -4
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +6 -6
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +7 -7
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +1 -1
- 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 +0 -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 +9 -13
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts +3 -0
- package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/index.js +3 -0
- package/dist/components/workflow-run-tabs/index.js.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts +16 -0
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.js +50 -0
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.d.ts +20 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.js +102 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.d.ts +39 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.js +211 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +90 -25
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -1
- package/dist/components/workflow-run-tabs/severity-visuals.d.ts +20 -0
- package/dist/components/workflow-run-tabs/severity-visuals.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/severity-visuals.js +31 -0
- package/dist/components/workflow-run-tabs/severity-visuals.js.map +1 -0
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +16 -10
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +6 -6
- package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts +1 -3
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +217 -59
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +7 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +9 -0
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/step-attempt.d.ts +29 -0
- package/dist/core/entities/step-attempt.d.ts.map +1 -1
- package/dist/core/entities/step-attempt.js.map +1 -1
- package/dist/core/entities/step.d.ts +3 -1
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/run-annotation.d.ts +79 -0
- package/dist/core/run-annotation.d.ts.map +1 -0
- package/dist/core/run-annotation.js +125 -0
- package/dist/core/run-annotation.js.map +1 -0
- package/dist/core/workflow-run.d.ts +1 -1
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/hooks/api/annotations.d.ts +63 -0
- package/dist/hooks/api/annotations.d.ts.map +1 -0
- package/dist/hooks/api/annotations.js +145 -0
- package/dist/hooks/api/annotations.js.map +1 -0
- package/dist/hooks/api/run-annotation-mapper.d.ts +10 -0
- package/dist/hooks/api/run-annotation-mapper.d.ts.map +1 -0
- package/dist/hooks/api/run-annotation-mapper.js +22 -0
- package/dist/hooks/api/run-annotation-mapper.js.map +1 -0
- package/dist/hooks/api/run-annotations.d.ts +25 -0
- package/dist/hooks/api/run-annotations.d.ts.map +1 -0
- package/dist/hooks/api/run-annotations.js +78 -0
- package/dist/hooks/api/run-annotations.js.map +1 -0
- package/dist/hooks/api/step-attempt-detail.d.ts +14 -0
- package/dist/hooks/api/step-attempt-detail.d.ts.map +1 -0
- package/dist/hooks/api/step-attempt-detail.js +43 -0
- package/dist/hooks/api/step-attempt-detail.js.map +1 -0
- package/dist/hooks/api/workflow-run-mapper.d.ts +9 -2
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +47 -0
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/pages/project-workflows-page.js +19 -19
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-list-page.js +1 -1
- package/dist/pages/workflow-run-list-page.js.map +1 -1
- package/dist/routes/inputs.d.ts +7 -2
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +5 -6
- package/dist/routes/inputs.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +10 -9
- package/src/components/identifier/identifier.tsx +2 -2
- package/src/components/job-detail/agent-config-failure-callout.tsx +5 -1
- package/src/components/job-detail/job-context-panel.test.tsx +72 -0
- package/src/components/job-detail/job-context-panel.tsx +237 -0
- package/src/components/job-detail/job-detail-header.test.tsx +3 -0
- package/src/components/job-detail/job-detail-header.tsx +65 -34
- package/src/components/job-detail/job-detail-view.stories.tsx +555 -48
- package/src/components/job-detail/job-detail-view.tsx +101 -42
- package/src/components/job-detail/job-empty-states.tsx +23 -10
- package/src/components/job-detail/job-execution-switcher.tsx +5 -5
- package/src/components/job-detail/json-code.tsx +120 -0
- package/src/components/job-detail/step-attempt-log-panel.test.tsx +1 -0
- package/src/components/job-detail/step-attempt-log-panel.tsx +17 -6
- package/src/components/job-detail/step-troubleshooting.stories.tsx +120 -0
- package/src/components/job-detail/step-troubleshooting.test.tsx +337 -0
- package/src/components/job-detail/step-troubleshooting.tsx +537 -0
- package/src/components/job-graph/job-graph-content.tsx +4 -3
- package/src/components/job-graph/job-node.tsx +3 -3
- package/src/components/step-list/index.ts +1 -0
- package/src/components/step-list/step-list-model.ts +5 -0
- package/src/components/step-list/step-list.test.tsx +75 -2
- package/src/components/step-list/step-list.tsx +73 -20
- package/src/components/workflow-run-duration-label.tsx +1 -1
- package/src/components/workflow-run-list/job-status-strip.tsx +6 -6
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-filters.tsx +4 -4
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +6 -6
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-row.tsx +7 -7
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +12 -12
- package/src/components/workflow-run-tabs/index.ts +7 -0
- package/src/components/workflow-run-tabs/run-annotation-count-chip.tsx +63 -0
- package/src/components/workflow-run-tabs/run-annotation-item.tsx +123 -0
- package/src/components/workflow-run-tabs/run-annotation-list.tsx +273 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +77 -3
- package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +95 -20
- package/src/components/workflow-run-tabs/run-annotations.stories.tsx +293 -0
- package/src/components/workflow-run-tabs/severity-visuals.ts +37 -0
- package/src/components/workflow-run-view/run-workspace-nav.test.tsx +44 -0
- package/src/components/workflow-run-view/run-workspace-nav.tsx +22 -9
- package/src/components/workflow-run-view/workflow-run-states.tsx +6 -6
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +246 -20
- package/src/components/workflow-run-view/workflow-run-view.tsx +255 -37
- package/src/core/entities/job-execution.ts +7 -0
- package/src/core/entities/job.ts +9 -0
- package/src/core/entities/step-attempt.ts +33 -0
- package/src/core/entities/step.ts +3 -1
- package/src/core/run-annotation.test.ts +315 -0
- package/src/core/run-annotation.ts +244 -0
- package/src/core/workflow-run.ts +4 -0
- package/src/hooks/api/annotations.test.ts +84 -0
- package/src/hooks/api/annotations.ts +240 -0
- package/src/hooks/api/run-annotation-mapper.ts +30 -0
- package/src/hooks/api/run-annotations.ts +137 -0
- package/src/hooks/api/step-attempt-detail.ts +67 -0
- package/src/hooks/api/workflow-run-mapper.ts +46 -0
- package/src/hooks/api/workflow-runs.test.tsx +61 -1
- package/src/pages/project-workflows-page.tsx +19 -19
- package/src/pages/workflow-job-detail-page.test.tsx +118 -1
- package/src/pages/workflow-run-list-page.tsx +1 -1
- package/src/routes/inputs.ts +7 -2
- package/test/fixtures/workflow-run.ts +7 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/core/workflow-run-tabs.d.ts +0 -9
- package/dist/core/workflow-run-tabs.d.ts.map +0 -1
- package/dist/core/workflow-run-tabs.js +0 -3
- package/dist/core/workflow-run-tabs.js.map +0 -1
- package/src/core/workflow-run-tabs.ts +0 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-detail/job-detail-header.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {formatTimestamp} 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 {\n defaultJobExecution,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n type Job,\n type JobExecution,\n} from '#core/workflow-run.js';\nimport {JobExecutionSwitcher} from './job-execution-switcher.js';\nimport {JobExecutionTimeText} from './job-execution-time-text.js';\n\nexport interface JobDetailHeaderProps {\n job: Job;\n selectedJobExecution: JobExecution | undefined;\n onSelectedJobExecutionChange: (jobExecutionId: string) => void;\n}\n\nexport function JobDetailHeader({\n job,\n selectedJobExecution,\n onSelectedJobExecutionChange,\n}: JobDetailHeaderProps) {\n const selectedStatus = selectedExecutionStatus(job, selectedJobExecution);\n const jobStatus = getWorkflowStatusVisual(selectedStatus);\n\n return (\n <header className=\"border-b border-border-neutral-base px-
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-detail/job-detail-header.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {formatTimestamp} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {\n defaultJobExecution,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n type Job,\n type JobExecution,\n} from '#core/workflow-run.js';\nimport {RunAnnotationCountChip} from '../workflow-run-tabs/index.js';\nimport {JobExecutionSwitcher} from './job-execution-switcher.js';\nimport {JobExecutionTimeText} from './job-execution-time-text.js';\n\nexport interface JobDetailHeaderProps {\n job: Job;\n selectedJobExecution: JobExecution | undefined;\n onSelectedJobExecutionChange: (jobExecutionId: string) => void;\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n runAttempt?: number | undefined;\n /** Counts for this job only. Renders a link into the run's Annotations section, never a body. */\n annotationSummary?: RunAnnotationSummary | undefined;\n jobContext?: ReactNode;\n}\n\nexport function JobDetailHeader({\n job,\n selectedJobExecution,\n onSelectedJobExecutionChange,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n annotationSummary,\n jobContext,\n}: JobDetailHeaderProps) {\n const selectedStatus = selectedExecutionStatus(job, selectedJobExecution);\n const jobStatus = getWorkflowStatusVisual(selectedStatus);\n\n return (\n <header className=\"border-b border-border-neutral-base px-row py-row\">\n <div className=\"flex min-w-0 items-start justify-between gap-cluster\">\n <div className=\"flex min-w-0 flex-col gap-inline\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <WorkflowStatusIcon\n status={selectedStatus}\n size={14}\n tooltip\n ariaLabel={`Job status: ${jobStatus.label}`}\n />\n <Code\n as=\"h1\"\n variant=\"paragraph\"\n bold\n tabIndex={-1}\n className=\"min-w-0 truncate text-lg leading-24 text-foreground-neutral-base outline-none\"\n data-job-heading\n >\n {job.displayName}\n </Code>\n </div>\n\n {selectedJobExecution || annotationSummary?.total ? (\n <div className=\"flex min-w-0 flex-wrap items-center gap-inline text-foreground-neutral-muted\">\n {selectedJobExecution && job.executionCountVisible ? (\n <JobExecutionSwitcher\n job={job}\n selectedJobExecution={selectedJobExecution.id}\n onSelectedJobExecutionChange={onSelectedJobExecutionChange}\n variant=\"title\"\n />\n ) : null}\n {selectedJobExecution ? (\n <>\n <JobDurationMeta execution={selectedJobExecution} kind=\"queue\" />\n <JobDurationMeta execution={selectedJobExecution} kind=\"run\" />\n </>\n ) : null}\n <RunAnnotationCountChip\n summary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n runAttempt={runAttempt}\n jobId={job.id}\n />\n </div>\n ) : null}\n </div>\n {jobContext}\n </div>\n </header>\n );\n}\n\nfunction selectedExecutionStatus(job: Job, execution: JobExecution | undefined) {\n if (!execution) return deriveJobDisplayStatus(job);\n const defaultExecution = defaultJobExecution(job);\n return execution.id === defaultExecution?.id\n ? deriveJobDisplayStatus(job)\n : deriveJobExecutionDisplayStatus(execution);\n}\n\nfunction JobDurationMeta({execution, kind}: {execution: JobExecution; kind: 'queue' | 'run'}) {\n const time = kind === 'queue' ? execution.queueTime : execution.runTime;\n const from = kind === 'queue' ? execution.queuedAt : execution.startedAt;\n if (!time || !from) return null;\n\n const to = kind === 'queue' ? execution.startedAt : execution.finishedAt;\n const live = time.state === 'live';\n const label = kind === 'queue' ? 'queued' : live ? 'running' : 'ran';\n const tooltipLabel = kind === 'queue' ? 'Queued' : live ? 'Running' : 'Ran';\n const tooltip = `${tooltipLabel} ${formatTimestamp(from)}${to ? ` – ${formatTimestamp(to)}` : ' – now'}`;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex items-center gap-tight whitespace-nowrap font-code text-xs leading-20 tabular-nums\">\n <Icon\n name={kind === 'queue' ? 'hourglassLine' : 'timerLine'}\n size={12}\n aria-hidden=\"true\"\n />\n <span>{label} </span>\n <JobExecutionTimeText time={time} />\n </span>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\">\n {tooltip}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n"],"names":["Icon","Tooltip","TooltipContent","TooltipTrigger","Code","Text","formatTimestamp","getWorkflowStatusVisual","WorkflowStatusIcon","defaultJobExecution","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","RunAnnotationCountChip","JobExecutionSwitcher","JobExecutionTimeText","JobDetailHeader","job","selectedJobExecution","onSelectedJobExecutionChange","workspaceSlug","projectSlug","workflowRunId","runAttempt","annotationSummary","jobContext","selectedStatus","selectedExecutionStatus","jobStatus","header","className","div","status","size","tooltip","ariaLabel","label","as","variant","bold","tabIndex","data-job-heading","displayName","total","executionCountVisible","id","JobDurationMeta","execution","kind","summary","jobId","defaultExecution","time","queueTime","runTime","from","queuedAt","startedAt","to","finishedAt","live","state","tooltipLabel","asChild","span","name","aria-hidden"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Job } from '#core/workflow-run.js';
|
|
2
2
|
import type { useWorkflowRunAttemptQuery } from '#hooks/api/workflow-runs.js';
|
|
3
3
|
import { type WorkflowJobSearch } from '#routes/inputs.js';
|
|
4
4
|
export interface JobDetailViewProps {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-detail-view.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-detail-view.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"job-detail-view.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-detail-view.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAwB,KAAK,GAAG,EAAoB,MAAM,uBAAuB,CAAC;AAGzF,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EACL,KAAK,iBAAiB,EAGvB,MAAM,mBAAmB,CAAC;AAwB3B,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,EAAE,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAC;IACrD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;IAC3B,iBAAiB,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAgB,aAAa,CAAC,EAC5B,aAAa,EACb,WAAW,EACX,aAAa,EACb,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,iBAAiB,GAClB,EAAE,kBAAkB,+BAqQpB"}
|
|
@@ -9,20 +9,49 @@ import { Skeleton } from '@shipfox/react-ui/skeleton';
|
|
|
9
9
|
import { TimeTickerProvider } from '@shipfox/react-ui/time-ticker';
|
|
10
10
|
import { Text } from '@shipfox/react-ui/typography';
|
|
11
11
|
import { Link } from '@tanstack/react-router';
|
|
12
|
-
import { useEffect, useRef } from 'react';
|
|
12
|
+
import { useEffect, useRef, useState } from 'react';
|
|
13
|
+
import { summarizeJobAnnotations } from '#core/run-annotation.js';
|
|
14
|
+
import { isWorkflowRunTerminal } from '#core/workflow-run.js';
|
|
15
|
+
import { useWorkflowRunAnnotationSummaryQuery } from '#hooks/api/annotations.js';
|
|
16
|
+
import { useRunAnnotationsQuery } from '#hooks/api/run-annotations.js';
|
|
13
17
|
import { workflowJobSearchParams, workflowRunSearchParams } from '#routes/inputs.js';
|
|
14
18
|
import { StepList } from '../step-list/index.js';
|
|
15
19
|
import { WorkflowRunNotFound, WorkflowRunStaleError } from '../workflow-run-view/workflow-run-states.js';
|
|
16
|
-
import {
|
|
20
|
+
import { JobContextPanel } from './job-context-panel.js';
|
|
17
21
|
import { JobDetailHeader } from './job-detail-header.js';
|
|
18
|
-
import { CarriedOverStepPanel, emptyStateForJob, emptyStateForMissingExecution,
|
|
22
|
+
import { CarriedOverStepPanel, emptyStateForJob, emptyStateForMissingExecution, jobSucceededSummary } from './job-empty-states.js';
|
|
19
23
|
import { resolveWorkflowJobSelection, workflowJobLandingSelection } from './job-selection.js';
|
|
20
24
|
import { StepAttemptLogPanel } from './step-attempt-log-panel.js';
|
|
25
|
+
import { StepInspectorSheet } from './step-troubleshooting.js';
|
|
21
26
|
export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId, search, query, newerAttempt, newerJob, onSelectionChange }) {
|
|
22
27
|
const rootRef = useRef(null);
|
|
23
28
|
const pageScrollRef = useRef(null);
|
|
24
29
|
const landingSelectionRef = useRef(undefined);
|
|
25
30
|
const hasLoadedData = query.data !== undefined;
|
|
31
|
+
// Reuse the run workspace's bounded annotation read for the job header chip. The separate
|
|
32
|
+
// summary query below stays counts-only and is scoped to the inspector's selected execution.
|
|
33
|
+
const annotations = useRunAnnotationsQuery({
|
|
34
|
+
workflowRunId,
|
|
35
|
+
runAttempt: query.data?.runAttempt.attempt
|
|
36
|
+
});
|
|
37
|
+
const jobAnnotationSummary = annotations.annotations ? summarizeJobAnnotations(annotations.annotations, jobId, {
|
|
38
|
+
truncated: annotations.summary?.truncated ?? false
|
|
39
|
+
}) : undefined;
|
|
40
|
+
const inspectorResetKey = `${jobId}:${search.jobExecutionId ?? ''}`;
|
|
41
|
+
const [inspectorState, setInspectorState] = useState(()=>({
|
|
42
|
+
key: inspectorResetKey,
|
|
43
|
+
attemptId: null
|
|
44
|
+
}));
|
|
45
|
+
const inspectorOpenAttemptId = inspectorState.key === inspectorResetKey ? inspectorState.attemptId : null;
|
|
46
|
+
const annotationJob = query.data?.jobs.find((candidate)=>candidate.id === jobId);
|
|
47
|
+
const annotationExecutionId = annotationJob ? resolveWorkflowJobSelection({
|
|
48
|
+
job: annotationJob,
|
|
49
|
+
selection: search
|
|
50
|
+
}).jobExecution?.id : search.jobExecutionId;
|
|
51
|
+
const annotationPolling = query.data ? !isWorkflowRunTerminal(query.data.runAttempt.status) : true;
|
|
52
|
+
const annotationSummaryQuery = useWorkflowRunAnnotationSummaryQuery(query.data?.id, query.data?.runAttempt.attempt, annotationExecutionId, {
|
|
53
|
+
polling: annotationPolling
|
|
54
|
+
});
|
|
26
55
|
useEffect(()=>{
|
|
27
56
|
if (!jobId || !hasLoadedData) return;
|
|
28
57
|
const heading = rootRef.current?.querySelector('[data-job-heading]');
|
|
@@ -118,6 +147,12 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
|
|
|
118
147
|
stepAttemptId: match.attemptId
|
|
119
148
|
});
|
|
120
149
|
}
|
|
150
|
+
function onInspectorOpenChange(attemptId) {
|
|
151
|
+
setInspectorState({
|
|
152
|
+
key: inspectorResetKey,
|
|
153
|
+
attemptId
|
|
154
|
+
});
|
|
155
|
+
}
|
|
121
156
|
function retargetToRunningStep() {
|
|
122
157
|
if (!runningSelection || !selectedJobExecution) return;
|
|
123
158
|
onSelectionChange({
|
|
@@ -146,10 +181,10 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
|
|
|
146
181
|
}) : null,
|
|
147
182
|
/*#__PURE__*/ _jsx("div", {
|
|
148
183
|
ref: pageScrollRef,
|
|
149
|
-
className: "@container min-h-0 flex-1 overflow-auto pb-
|
|
184
|
+
className: "@container min-h-0 flex-1 overflow-auto pb-panel",
|
|
150
185
|
children: /*#__PURE__*/ _jsxs("section", {
|
|
151
186
|
"aria-label": `${job.displayName} logs`,
|
|
152
|
-
className: "
|
|
187
|
+
className: "flex w-full flex-col",
|
|
153
188
|
children: [
|
|
154
189
|
/*#__PURE__*/ _jsxs("section", {
|
|
155
190
|
className: "min-w-0 overflow-hidden",
|
|
@@ -157,7 +192,16 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
|
|
|
157
192
|
/*#__PURE__*/ _jsx(JobDetailHeader, {
|
|
158
193
|
job: job,
|
|
159
194
|
selectedJobExecution: selectedJobExecution,
|
|
160
|
-
onSelectedJobExecutionChange: selectExecution
|
|
195
|
+
onSelectedJobExecutionChange: selectExecution,
|
|
196
|
+
workspaceSlug: workspaceSlug,
|
|
197
|
+
projectSlug: projectSlug,
|
|
198
|
+
workflowRunId: run.id,
|
|
199
|
+
runAttempt: run.runAttempt.attempt,
|
|
200
|
+
annotationSummary: jobAnnotationSummary,
|
|
201
|
+
jobContext: selectedJobExecution ? /*#__PURE__*/ _jsx(JobContextPanel, {
|
|
202
|
+
job: job,
|
|
203
|
+
execution: selectedJobExecution
|
|
204
|
+
}) : undefined
|
|
161
205
|
}),
|
|
162
206
|
/*#__PURE__*/ _jsx(Text, {
|
|
163
207
|
as: "h2",
|
|
@@ -170,14 +214,26 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
|
|
|
170
214
|
selectedAttemptId: selectedAttemptId,
|
|
171
215
|
defaultSelectedAttemptId: landingSelection?.attemptId,
|
|
172
216
|
onSelectedAttemptChange: selectAttempt,
|
|
217
|
+
inspectorOpenAttemptId: inspectorOpenAttemptId,
|
|
218
|
+
onInspectorOpenChange: onInspectorOpenChange,
|
|
173
219
|
autoSelectActiveAttempt: true,
|
|
174
220
|
emptyState: emptyStateForJob(job, selectedJobExecution),
|
|
175
221
|
showHeader: false,
|
|
176
|
-
className: "rounded-none border-0",
|
|
222
|
+
className: "rounded-none border-0 bg-transparent",
|
|
177
223
|
renderExpandedStep: (context)=>/*#__PURE__*/ _jsx(ExpandedStep, {
|
|
178
224
|
context: context,
|
|
179
|
-
workspaceSlug: workspaceSlug,
|
|
180
225
|
pageScrollRef: pageScrollRef
|
|
226
|
+
}),
|
|
227
|
+
renderInspector: (entry)=>/*#__PURE__*/ _jsx(StepInspectorSheet, {
|
|
228
|
+
entry: entry,
|
|
229
|
+
open: true,
|
|
230
|
+
onOpenChange: (open)=>onInspectorOpenChange(open ? entry.id : null),
|
|
231
|
+
workspaceSlug: workspaceSlug,
|
|
232
|
+
projectSlug: projectSlug,
|
|
233
|
+
workflowRunId: run.id,
|
|
234
|
+
runAttempt: run.runAttempt.attempt,
|
|
235
|
+
jobId: job.id,
|
|
236
|
+
annotationCount: annotationCountForStep(annotationSummaryQuery.data, entry.step.id, entry.attempt)
|
|
181
237
|
})
|
|
182
238
|
}) : /*#__PURE__*/ _jsx(EmptyStateForMissingExecution, {
|
|
183
239
|
job: job
|
|
@@ -187,7 +243,7 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
|
|
|
187
243
|
showRetargetNotice && runningSelection ? /*#__PURE__*/ _jsxs("div", {
|
|
188
244
|
role: "status",
|
|
189
245
|
"aria-live": "polite",
|
|
190
|
-
className: "flex min-w-0 items-center justify-between gap-
|
|
246
|
+
className: "flex min-w-0 items-center justify-between gap-inline border-t border-border-neutral-base px-row py-row",
|
|
191
247
|
children: [
|
|
192
248
|
/*#__PURE__*/ _jsxs(Text, {
|
|
193
249
|
size: "xs",
|
|
@@ -204,7 +260,7 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
|
|
|
204
260
|
}),
|
|
205
261
|
/*#__PURE__*/ _jsx("button", {
|
|
206
262
|
type: "button",
|
|
207
|
-
className: "shrink-0 rounded-4 px-
|
|
263
|
+
className: "shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active",
|
|
208
264
|
onClick: retargetToRunningStep,
|
|
209
265
|
children: "Jump to it"
|
|
210
266
|
})
|
|
@@ -212,7 +268,7 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
|
|
|
212
268
|
}) : null,
|
|
213
269
|
succeededSummary ? /*#__PURE__*/ _jsx(Text, {
|
|
214
270
|
size: "xs",
|
|
215
|
-
className: "px-
|
|
271
|
+
className: "px-row py-row text-foreground-neutral-muted",
|
|
216
272
|
children: succeededSummary
|
|
217
273
|
}) : null
|
|
218
274
|
]
|
|
@@ -222,35 +278,27 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
|
|
|
222
278
|
})
|
|
223
279
|
});
|
|
224
280
|
}
|
|
225
|
-
function ExpandedStep({ context,
|
|
281
|
+
function ExpandedStep({ context, pageScrollRef }) {
|
|
226
282
|
if (context.carriedOver) return /*#__PURE__*/ _jsx(CarriedOverStepPanel, {});
|
|
227
|
-
|
|
228
|
-
return /*#__PURE__*/ _jsxs("section", {
|
|
283
|
+
return /*#__PURE__*/ _jsx("section", {
|
|
229
284
|
role: "region",
|
|
230
285
|
tabIndex: 0,
|
|
231
286
|
"aria-label": `${context.stepLabel} output, attempt ${context.attempt}`,
|
|
232
|
-
className: "flex min-w-0 flex-col
|
|
233
|
-
children:
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
attempt: context.attempt,
|
|
242
|
-
attemptStatus: context.attemptStatus,
|
|
243
|
-
attemptStartedAt: context.attemptStartedAt,
|
|
244
|
-
pageScrollRef: pageScrollRef,
|
|
245
|
-
surfaceClassName: "rounded-8"
|
|
246
|
-
})
|
|
247
|
-
]
|
|
287
|
+
className: "flex min-w-0 flex-col border-t border-border-neutral-base bg-background-neutral-base outline-none focus-visible:shadow-border-interactive-with-active dark:bg-background-contrast-subtle",
|
|
288
|
+
children: /*#__PURE__*/ _jsx(StepAttemptLogPanel, {
|
|
289
|
+
stepId: context.stepId,
|
|
290
|
+
attempt: context.attempt,
|
|
291
|
+
attemptStatus: context.attemptStatus,
|
|
292
|
+
attemptStartedAt: context.attemptStartedAt,
|
|
293
|
+
pageScrollRef: pageScrollRef,
|
|
294
|
+
surfaceClassName: "rounded-none border-0 bg-transparent shadow-none"
|
|
295
|
+
})
|
|
248
296
|
});
|
|
249
297
|
}
|
|
250
298
|
function EmptyStateForMissingExecution({ job }) {
|
|
251
299
|
const emptyState = emptyStateForMissingExecution(job);
|
|
252
300
|
return /*#__PURE__*/ _jsx(EmptyState, {
|
|
253
|
-
className: "min-h-120
|
|
301
|
+
className: "min-h-120 p-panel",
|
|
254
302
|
icon: "componentLine",
|
|
255
303
|
title: emptyState.title,
|
|
256
304
|
description: emptyState.description,
|
|
@@ -259,7 +307,7 @@ function EmptyStateForMissingExecution({ job }) {
|
|
|
259
307
|
}
|
|
260
308
|
function JobNotFoundState({ workspaceSlug, projectSlug, workflowRunId, search }) {
|
|
261
309
|
return /*#__PURE__*/ _jsxs("div", {
|
|
262
|
-
className: "flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-
|
|
310
|
+
className: "flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-cluster p-panel",
|
|
263
311
|
children: [
|
|
264
312
|
/*#__PURE__*/ _jsx(EmptyState, {
|
|
265
313
|
icon: "componentLine",
|
|
@@ -278,7 +326,7 @@ function JobNotFoundState({ workspaceSlug, projectSlug, workflowRunId, search })
|
|
|
278
326
|
}, {
|
|
279
327
|
runAttempt: search.runAttempt
|
|
280
328
|
}),
|
|
281
|
-
className: "inline-flex items-center gap-
|
|
329
|
+
className: "inline-flex items-center gap-inline rounded-6 px-tight py-[6px] text-sm text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active",
|
|
282
330
|
children: [
|
|
283
331
|
/*#__PURE__*/ _jsx(Icon, {
|
|
284
332
|
name: "arrowLeftLine",
|
|
@@ -294,9 +342,9 @@ function JobNotFoundState({ workspaceSlug, projectSlug, workflowRunId, search })
|
|
|
294
342
|
function NewerAttemptNotice({ workspaceSlug, projectSlug, runId, jobId, attempt }) {
|
|
295
343
|
return /*#__PURE__*/ _jsx("div", {
|
|
296
344
|
role: "status",
|
|
297
|
-
className: "border-b border-border-neutral-base px-
|
|
345
|
+
className: "border-b border-border-neutral-base px-row py-row",
|
|
298
346
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
299
|
-
className: "
|
|
347
|
+
className: "flex items-center justify-between gap-inline",
|
|
300
348
|
children: [
|
|
301
349
|
/*#__PURE__*/ _jsx(Text, {
|
|
302
350
|
size: "xs",
|
|
@@ -314,7 +362,7 @@ function NewerAttemptNotice({ workspaceSlug, projectSlug, runId, jobId, attempt
|
|
|
314
362
|
search: workflowJobSearchParams({
|
|
315
363
|
runAttempt: attempt
|
|
316
364
|
}),
|
|
317
|
-
className: "shrink-0 rounded-4 px-
|
|
365
|
+
className: "shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active",
|
|
318
366
|
children: [
|
|
319
367
|
"View attempt #",
|
|
320
368
|
attempt
|
|
@@ -353,14 +401,17 @@ function findAttempt(jobExecution, attemptId) {
|
|
|
353
401
|
}
|
|
354
402
|
return undefined;
|
|
355
403
|
}
|
|
404
|
+
function annotationCountForStep(summary, stepId, attempt) {
|
|
405
|
+
return summary?.stepCounts?.find((entry)=>entry.stepId === stepId && entry.attempt === attempt)?.total;
|
|
406
|
+
}
|
|
356
407
|
function JobDetailSkeleton() {
|
|
357
408
|
return /*#__PURE__*/ _jsx("div", {
|
|
358
|
-
className: "min-h-0 min-w-0 flex-1 overflow-auto
|
|
409
|
+
className: "min-h-0 min-w-0 flex-1 overflow-auto pb-panel",
|
|
359
410
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
360
|
-
className: "
|
|
411
|
+
className: "w-full",
|
|
361
412
|
children: [
|
|
362
413
|
/*#__PURE__*/ _jsxs("header", {
|
|
363
|
-
className: "flex items-center gap-
|
|
414
|
+
className: "flex items-center gap-cluster border-b border-border-neutral-base px-row py-row",
|
|
364
415
|
children: [
|
|
365
416
|
/*#__PURE__*/ _jsx(Skeleton, {
|
|
366
417
|
className: "size-20 rounded-full"
|
|
@@ -377,9 +428,9 @@ function JobDetailSkeleton() {
|
|
|
377
428
|
]
|
|
378
429
|
}),
|
|
379
430
|
/*#__PURE__*/ _jsx("div", {
|
|
380
|
-
className: "p-
|
|
431
|
+
className: "p-panel-compact",
|
|
381
432
|
children: JOB_DETAIL_SKELETON_ROWS.map((row)=>/*#__PURE__*/ _jsxs("div", {
|
|
382
|
-
className: "flex min-h-44 items-center gap-
|
|
433
|
+
className: "flex min-h-44 items-center gap-inline border-b border-border-neutral-base last:border-b-0",
|
|
383
434
|
children: [
|
|
384
435
|
/*#__PURE__*/ _jsx(Skeleton, {
|
|
385
436
|
className: "size-14 rounded-full"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-detail/job-detail-view.tsx"],"sourcesContent":["// biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.\n// biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.\n\nimport {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {type RefObject, useEffect, useRef} from 'react';\nimport type {Job, JobExecution} from '#core/workflow-run.js';\nimport type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {type StepExpandedContext, StepList} from '../step-list/index.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunStaleError,\n} from '../workflow-run-view/workflow-run-states.js';\nimport {AgentConfigFailureCallout} from './agent-config-failure-callout.js';\nimport {JobDetailHeader} from './job-detail-header.js';\nimport {\n CarriedOverStepPanel,\n emptyStateForJob,\n emptyStateForMissingExecution,\n isAgentConfigFailure,\n jobSucceededSummary,\n toSelectedAttemptError,\n} from './job-empty-states.js';\nimport {\n resolveWorkflowJobSelection,\n type WorkflowJobLandingSelection,\n workflowJobLandingSelection,\n} from './job-selection.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\n\nexport interface JobDetailViewProps {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n jobId: string;\n search: WorkflowJobSearch;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n newerAttempt?: number | undefined;\n newerJob?: Job | undefined;\n onSelectionChange: (selection: WorkflowJobSearch) => void;\n}\n\nexport function JobDetailView({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n jobId,\n search,\n query,\n newerAttempt,\n newerJob,\n onSelectionChange,\n}: JobDetailViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const pageScrollRef = useRef<HTMLDivElement>(null);\n const landingSelectionRef = useRef<FrozenLandingSelection | undefined>(undefined);\n const hasLoadedData = query.data !== undefined;\n\n useEffect(() => {\n if (!jobId || !hasLoadedData) return;\n const heading = rootRef.current?.querySelector<HTMLElement>('[data-job-heading]');\n // The heading is intentionally the route-change focus target: it announces the new job\n // after rail navigation without putting focus on a decorative status element.\n heading?.focus({preventScroll: true});\n }, [hasLoadedData, jobId]);\n\n if (query.isPending) return <JobDetailSkeleton />;\n\n if (query.isError && query.data === undefined) {\n if (query.error instanceof ApiError && query.error.status === 404) {\n return <WorkflowRunNotFound />;\n }\n return <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />;\n }\n\n if (query.data === undefined) return <JobDetailSkeleton />;\n\n const run = query.data;\n const job = run.jobs.find((candidate) => candidate.id === jobId);\n if (!job) {\n return (\n <JobNotFoundState\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n );\n }\n\n const resolvedSelection = resolveWorkflowJobSelection({job, selection: search});\n const selectedJobExecution = resolvedSelection.jobExecution;\n const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);\n const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);\n if (selectedJobExecution) {\n const frozenLanding = landingSelectionRef.current;\n if (\n !frozenLanding ||\n frozenLanding.jobId !== job.id ||\n frozenLanding.jobExecutionId !== selectedJobExecution.id\n ) {\n landingSelectionRef.current = {\n jobId: job.id,\n jobExecutionId: selectedJobExecution.id,\n selection: currentLandingSelection,\n hasSelection: currentLandingSelection !== undefined,\n };\n } else if (!frozenLanding.hasSelection && currentLandingSelection) {\n landingSelectionRef.current = {\n ...frozenLanding,\n selection: currentLandingSelection,\n hasSelection: true,\n };\n }\n } else {\n landingSelectionRef.current = undefined;\n }\n const landingSelection = landingSelectionRef.current?.selection;\n const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;\n const runningSelection = runningStepSelection(selectedJobExecution);\n const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;\n const selectedAttemptForNotice = hasExplicitStep\n ? resolvedSelection.selectedAttemptId\n : landingSelection?.attemptId;\n const showRetargetNotice =\n runningSelection !== undefined &&\n (selectedStepId !== runningSelection.stepId ||\n selectedAttemptForNotice !== runningSelection.attemptId);\n const succeededSummary =\n !hasExplicitStep && landingSelection === undefined && selectedJobExecution\n ? jobSucceededSummary(job, selectedJobExecution)\n : undefined;\n\n function selectExecution(jobExecutionId: string) {\n onSelectionChange({\n ...search,\n jobExecutionId,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectedJobExecution) return;\n if (!attemptId) {\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n return;\n }\n\n const match = findAttempt(selectedJobExecution, attemptId);\n if (!match) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.step.id,\n stepAttemptId: match.attemptId,\n });\n }\n\n function retargetToRunningStep() {\n if (!runningSelection || !selectedJobExecution) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: runningSelection.stepId,\n stepAttemptId: runningSelection.attemptId,\n });\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div ref={rootRef} className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n {newerJob && newerAttempt && newerAttempt > run.runAttempt.attempt ? (\n <NewerAttemptNotice\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n jobId={newerJob.id}\n attempt={newerAttempt}\n />\n ) : null}\n <div ref={pageScrollRef} className=\"@container min-h-0 flex-1 overflow-auto pb-24\">\n <section\n aria-label={`${job.displayName} logs`}\n className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-8 px-24\"\n >\n <section className=\"min-w-0 overflow-hidden\">\n <JobDetailHeader\n job={job}\n selectedJobExecution={selectedJobExecution}\n onSelectedJobExecutionChange={selectExecution}\n />\n <Text as=\"h2\" className=\"sr-only\">\n Logs\n </Text>\n {selectedJobExecution ? (\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={landingSelection?.attemptId}\n onSelectedAttemptChange={selectAttempt}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0\"\n renderExpandedStep={(context) => (\n <ExpandedStep\n context={context}\n workspaceSlug={workspaceSlug}\n pageScrollRef={pageScrollRef}\n />\n )}\n />\n ) : (\n <EmptyStateForMissingExecution job={job} />\n )}\n </section>\n {showRetargetNotice && runningSelection ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex min-w-0 items-center justify-between gap-8 rounded-8 border border-border-neutral-base bg-background-components-base px-10 py-8\"\n >\n <Text size=\"xs\" className=\"min-w-0 text-foreground-neutral-muted\">\n Run moved on to{' '}\n <span className=\"font-code text-foreground-neutral-base\">\n {runningSelection.stepLabel}\n </span>\n .\n </Text>\n <button\n type=\"button\"\n className=\"shrink-0 rounded-4 px-6 py-4 text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n onClick={retargetToRunningStep}\n >\n Jump to it\n </button>\n </div>\n ) : null}\n {succeededSummary ? (\n <Text size=\"xs\" className=\"px-8 text-foreground-neutral-muted\">\n {succeededSummary}\n </Text>\n ) : null}\n </section>\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction ExpandedStep({\n context,\n workspaceSlug,\n pageScrollRef,\n}: {\n context: StepExpandedContext;\n workspaceSlug: string;\n pageScrollRef: RefObject<HTMLDivElement | null>;\n}) {\n if (context.carriedOver) return <CarriedOverStepPanel />;\n\n const selectedAttemptError =\n toSelectedAttemptError(context.step, context.attemptError) ?? context.step.error;\n\n return (\n <section\n role=\"region\"\n tabIndex={0}\n aria-label={`${context.stepLabel} output, attempt ${context.attempt}`}\n className=\"flex min-w-0 flex-col gap-10 border-t border-border-neutral-base bg-background-neutral-subtle px-16 py-12 outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n {isAgentConfigFailure(context.step, selectedAttemptError) ? (\n <AgentConfigFailureCallout\n workspaceSlug={workspaceSlug}\n config={context.step.agentConfig}\n error={selectedAttemptError}\n />\n ) : null}\n <StepAttemptLogPanel\n stepId={context.stepId}\n attempt={context.attempt}\n attemptStatus={context.attemptStatus}\n attemptStartedAt={context.attemptStartedAt}\n pageScrollRef={pageScrollRef}\n surfaceClassName=\"rounded-8\"\n />\n </section>\n );\n}\n\nfunction EmptyStateForMissingExecution({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\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\nfunction JobNotFoundState({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n search: WorkflowJobSearch;\n}) {\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-12 p-24\">\n <EmptyState\n icon=\"componentLine\"\n title=\"Job not found\"\n description=\"This job is not part of the selected workflow run.\"\n />\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {runAttempt: search.runAttempt},\n {runAttempt: search.runAttempt},\n ) as never\n }\n className=\"inline-flex items-center gap-6 rounded-6 px-8 py-6 text-sm text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name=\"arrowLeftLine\" size={14} aria-hidden=\"true\" />\n Back to run summary\n </Link>\n </div>\n );\n}\n\nfunction NewerAttemptNotice({\n workspaceSlug,\n projectSlug,\n runId,\n jobId,\n attempt,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n jobId: string;\n attempt: number;\n}) {\n return (\n <div role=\"status\" className=\"border-b border-border-neutral-base px-16 py-8\">\n <div className=\"mx-auto flex max-w-[1120px] items-center justify-between gap-8 rounded-8 border border-border-neutral-base bg-background-components-base px-10 py-8\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n A newer run attempt is available.\n </Text>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId, jobId}}\n search={workflowJobSearchParams({runAttempt: attempt}) as never}\n className=\"shrink-0 rounded-4 px-6 py-4 text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n View attempt #{attempt}\n </Link>\n </div>\n </div>\n );\n}\n\nfunction runningStepSelection(jobExecution: JobExecution | undefined) {\n if (!jobExecution) return undefined;\n const steps = [...jobExecution.steps].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {\n const step = steps[stepIndex];\n if (!step) continue;\n const attempt = [...step.attempts]\n .sort((left, right) => left.attempt - right.attempt || left.id.localeCompare(right.id))\n .reverse()\n .find((candidate) => candidate.status === 'running');\n if (attempt)\n return {stepId: step.id, attemptId: attempt.id, stepLabel: step.name || step.key || step.id};\n }\n return undefined;\n}\n\nfunction findAttempt(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {step, attemptId: attempt.id};\n }\n return undefined;\n}\n\nfunction JobDetailSkeleton() {\n return (\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto px-24 pb-24\">\n <div className=\"mx-auto max-w-[1120px] overflow-hidden\">\n <header className=\"flex items-center gap-12 border-b border-border-neutral-base px-16 py-12\">\n <Skeleton className=\"size-20 rounded-full\" />\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-24 w-72 rounded-6\" />\n <Skeleton className=\"h-24 w-180 rounded-4\" />\n </header>\n <div className=\"p-16\">\n {JOB_DETAIL_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex min-h-44 items-center gap-8 border-b border-border-neutral-base last:border-b-0\"\n >\n <Skeleton className=\"size-14 rounded-full\" />\n <Skeleton className=\"h-16 w-180 rounded-4\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst JOB_DETAIL_SKELETON_ROWS = ['step-1', 'step-2', 'step-3', 'step-4', 'step-5'];\n\ninterface FrozenLandingSelection {\n jobId: string;\n jobExecutionId: string;\n selection: WorkflowJobLandingSelection | undefined;\n hasSelection: boolean;\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","Icon","Skeleton","TimeTickerProvider","Text","Link","useEffect","useRef","workflowJobSearchParams","workflowRunSearchParams","StepList","WorkflowRunNotFound","WorkflowRunStaleError","AgentConfigFailureCallout","JobDetailHeader","CarriedOverStepPanel","emptyStateForJob","emptyStateForMissingExecution","isAgentConfigFailure","jobSucceededSummary","toSelectedAttemptError","resolveWorkflowJobSelection","workflowJobLandingSelection","StepAttemptLogPanel","JobDetailView","workspaceSlug","projectSlug","workflowRunId","jobId","search","query","newerAttempt","newerJob","onSelectionChange","rootRef","pageScrollRef","landingSelectionRef","undefined","hasLoadedData","data","heading","current","querySelector","focus","preventScroll","isPending","JobDetailSkeleton","isError","error","status","subject","icon","run","job","jobs","find","candidate","id","JobNotFoundState","resolvedSelection","selection","selectedJobExecution","jobExecution","hasExplicitStep","Boolean","stepId","step","currentLandingSelection","frozenLanding","jobExecutionId","hasSelection","landingSelection","selectedAttemptId","runningSelection","runningStepSelection","selectedStepId","selectedAttemptForNotice","attemptId","showRetargetNotice","succeededSummary","selectExecution","stepAttemptId","selectAttempt","match","findAttempt","retargetToRunningStep","intervalMs","reducedMotionIntervalMs","div","ref","className","runAttempt","attempt","NewerAttemptNotice","runId","section","aria-label","displayName","onSelectedJobExecutionChange","as","defaultSelectedAttemptId","onSelectedAttemptChange","autoSelectActiveAttempt","emptyState","showHeader","renderExpandedStep","context","ExpandedStep","EmptyStateForMissingExecution","role","aria-live","size","span","stepLabel","button","type","onClick","carriedOver","selectedAttemptError","attemptError","tabIndex","config","agentConfig","attemptStatus","attemptStartedAt","surfaceClassName","title","description","variant","to","params","name","aria-hidden","steps","sort","left","right","position","localeCompare","stepIndex","length","attempts","reverse","key","header","JOB_DETAIL_SKELETON_ROWS","map","row"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-detail/job-detail-view.tsx"],"sourcesContent":["// biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.\n// biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.\n\nimport {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {type RefObject, useEffect, useRef, useState} from 'react';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {summarizeJobAnnotations} from '#core/run-annotation.js';\nimport {isWorkflowRunTerminal, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {useWorkflowRunAnnotationSummaryQuery} from '#hooks/api/annotations.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {type StepExpandedContext, StepList} from '../step-list/index.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunStaleError,\n} from '../workflow-run-view/workflow-run-states.js';\nimport {JobContextPanel} from './job-context-panel.js';\nimport {JobDetailHeader} from './job-detail-header.js';\nimport {\n CarriedOverStepPanel,\n emptyStateForJob,\n emptyStateForMissingExecution,\n jobSucceededSummary,\n} from './job-empty-states.js';\nimport {\n resolveWorkflowJobSelection,\n type WorkflowJobLandingSelection,\n workflowJobLandingSelection,\n} from './job-selection.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\nimport {StepInspectorSheet} from './step-troubleshooting.js';\n\ntype InspectorState = {key: string; attemptId: string | null};\n\nexport interface JobDetailViewProps {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n jobId: string;\n search: WorkflowJobSearch;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n newerAttempt?: number | undefined;\n newerJob?: Job | undefined;\n onSelectionChange: (selection: WorkflowJobSearch) => void;\n}\n\nexport function JobDetailView({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n jobId,\n search,\n query,\n newerAttempt,\n newerJob,\n onSelectionChange,\n}: JobDetailViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const pageScrollRef = useRef<HTMLDivElement>(null);\n const landingSelectionRef = useRef<FrozenLandingSelection | undefined>(undefined);\n const hasLoadedData = query.data !== undefined;\n // Reuse the run workspace's bounded annotation read for the job header chip. The separate\n // summary query below stays counts-only and is scoped to the inspector's selected execution.\n const annotations = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: query.data?.runAttempt.attempt,\n });\n const jobAnnotationSummary = annotations.annotations\n ? summarizeJobAnnotations(annotations.annotations, jobId, {\n truncated: annotations.summary?.truncated ?? false,\n })\n : undefined;\n const inspectorResetKey = `${jobId}:${search.jobExecutionId ?? ''}`;\n const [inspectorState, setInspectorState] = useState<InspectorState>(() => ({\n key: inspectorResetKey,\n attemptId: null,\n }));\n const inspectorOpenAttemptId =\n inspectorState.key === inspectorResetKey ? inspectorState.attemptId : null;\n const annotationJob = query.data?.jobs.find((candidate) => candidate.id === jobId);\n const annotationExecutionId = annotationJob\n ? resolveWorkflowJobSelection({job: annotationJob, selection: search}).jobExecution?.id\n : search.jobExecutionId;\n const annotationPolling = query.data\n ? !isWorkflowRunTerminal(query.data.runAttempt.status)\n : true;\n const annotationSummaryQuery = useWorkflowRunAnnotationSummaryQuery(\n query.data?.id,\n query.data?.runAttempt.attempt,\n annotationExecutionId,\n {polling: annotationPolling},\n );\n\n useEffect(() => {\n if (!jobId || !hasLoadedData) return;\n const heading = rootRef.current?.querySelector<HTMLElement>('[data-job-heading]');\n // The heading is intentionally the route-change focus target: it announces the new job\n // after rail navigation without putting focus on a decorative status element.\n heading?.focus({preventScroll: true});\n }, [hasLoadedData, jobId]);\n\n if (query.isPending) return <JobDetailSkeleton />;\n\n if (query.isError && query.data === undefined) {\n if (query.error instanceof ApiError && query.error.status === 404) {\n return <WorkflowRunNotFound />;\n }\n return <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />;\n }\n\n if (query.data === undefined) return <JobDetailSkeleton />;\n\n const run = query.data;\n const job = run.jobs.find((candidate) => candidate.id === jobId);\n if (!job) {\n return (\n <JobNotFoundState\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n );\n }\n\n const resolvedSelection = resolveWorkflowJobSelection({job, selection: search});\n const selectedJobExecution = resolvedSelection.jobExecution;\n const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);\n const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);\n if (selectedJobExecution) {\n const frozenLanding = landingSelectionRef.current;\n if (\n !frozenLanding ||\n frozenLanding.jobId !== job.id ||\n frozenLanding.jobExecutionId !== selectedJobExecution.id\n ) {\n landingSelectionRef.current = {\n jobId: job.id,\n jobExecutionId: selectedJobExecution.id,\n selection: currentLandingSelection,\n hasSelection: currentLandingSelection !== undefined,\n };\n } else if (!frozenLanding.hasSelection && currentLandingSelection) {\n landingSelectionRef.current = {\n ...frozenLanding,\n selection: currentLandingSelection,\n hasSelection: true,\n };\n }\n } else {\n landingSelectionRef.current = undefined;\n }\n const landingSelection = landingSelectionRef.current?.selection;\n const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;\n const runningSelection = runningStepSelection(selectedJobExecution);\n const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;\n const selectedAttemptForNotice = hasExplicitStep\n ? resolvedSelection.selectedAttemptId\n : landingSelection?.attemptId;\n const showRetargetNotice =\n runningSelection !== undefined &&\n (selectedStepId !== runningSelection.stepId ||\n selectedAttemptForNotice !== runningSelection.attemptId);\n const succeededSummary =\n !hasExplicitStep && landingSelection === undefined && selectedJobExecution\n ? jobSucceededSummary(job, selectedJobExecution)\n : undefined;\n\n function selectExecution(jobExecutionId: string) {\n onSelectionChange({\n ...search,\n jobExecutionId,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectedJobExecution) return;\n if (!attemptId) {\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n return;\n }\n\n const match = findAttempt(selectedJobExecution, attemptId);\n if (!match) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.step.id,\n stepAttemptId: match.attemptId,\n });\n }\n\n function onInspectorOpenChange(attemptId: string | null) {\n setInspectorState({key: inspectorResetKey, attemptId});\n }\n\n function retargetToRunningStep() {\n if (!runningSelection || !selectedJobExecution) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: runningSelection.stepId,\n stepAttemptId: runningSelection.attemptId,\n });\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div ref={rootRef} className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n {newerJob && newerAttempt && newerAttempt > run.runAttempt.attempt ? (\n <NewerAttemptNotice\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n jobId={newerJob.id}\n attempt={newerAttempt}\n />\n ) : null}\n <div ref={pageScrollRef} className=\"@container min-h-0 flex-1 overflow-auto pb-panel\">\n <section aria-label={`${job.displayName} logs`} className=\"flex w-full flex-col\">\n <section className=\"min-w-0 overflow-hidden\">\n <JobDetailHeader\n job={job}\n selectedJobExecution={selectedJobExecution}\n onSelectedJobExecutionChange={selectExecution}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n annotationSummary={jobAnnotationSummary}\n jobContext={\n selectedJobExecution ? (\n <JobContextPanel job={job} execution={selectedJobExecution} />\n ) : undefined\n }\n />\n <Text as=\"h2\" className=\"sr-only\">\n Logs\n </Text>\n {selectedJobExecution ? (\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={landingSelection?.attemptId}\n onSelectedAttemptChange={selectAttempt}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent\"\n renderExpandedStep={(context) => (\n <ExpandedStep context={context} pageScrollRef={pageScrollRef} />\n )}\n renderInspector={(entry) => (\n <StepInspectorSheet\n entry={entry}\n open\n onOpenChange={(open) => onInspectorOpenChange(open ? entry.id : null)}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n jobId={job.id}\n annotationCount={annotationCountForStep(\n annotationSummaryQuery.data,\n entry.step.id,\n entry.attempt,\n )}\n />\n )}\n />\n ) : (\n <EmptyStateForMissingExecution job={job} />\n )}\n </section>\n {showRetargetNotice && runningSelection ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex min-w-0 items-center justify-between gap-inline border-t border-border-neutral-base px-row py-row\"\n >\n <Text size=\"xs\" className=\"min-w-0 text-foreground-neutral-muted\">\n Run moved on to{' '}\n <span className=\"font-code text-foreground-neutral-base\">\n {runningSelection.stepLabel}\n </span>\n .\n </Text>\n <button\n type=\"button\"\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n onClick={retargetToRunningStep}\n >\n Jump to it\n </button>\n </div>\n ) : null}\n {succeededSummary ? (\n <Text size=\"xs\" className=\"px-row py-row text-foreground-neutral-muted\">\n {succeededSummary}\n </Text>\n ) : null}\n </section>\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction ExpandedStep({\n context,\n pageScrollRef,\n}: {\n context: StepExpandedContext;\n pageScrollRef: RefObject<HTMLDivElement | null>;\n}) {\n if (context.carriedOver) return <CarriedOverStepPanel />;\n\n return (\n <section\n role=\"region\"\n tabIndex={0}\n aria-label={`${context.stepLabel} output, attempt ${context.attempt}`}\n className=\"flex min-w-0 flex-col border-t border-border-neutral-base bg-background-neutral-base outline-none focus-visible:shadow-border-interactive-with-active dark:bg-background-contrast-subtle\"\n >\n <StepAttemptLogPanel\n stepId={context.stepId}\n attempt={context.attempt}\n attemptStatus={context.attemptStatus}\n attemptStartedAt={context.attemptStartedAt}\n pageScrollRef={pageScrollRef}\n surfaceClassName=\"rounded-none border-0 bg-transparent shadow-none\"\n />\n </section>\n );\n}\n\nfunction EmptyStateForMissingExecution({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n}\n\nfunction JobNotFoundState({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n search: WorkflowJobSearch;\n}) {\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-cluster p-panel\">\n <EmptyState\n icon=\"componentLine\"\n title=\"Job not found\"\n description=\"This job is not part of the selected workflow run.\"\n />\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {runAttempt: search.runAttempt},\n {runAttempt: search.runAttempt},\n ) as never\n }\n className=\"inline-flex items-center gap-inline rounded-6 px-tight py-[6px] text-sm text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name=\"arrowLeftLine\" size={14} aria-hidden=\"true\" />\n Back to run summary\n </Link>\n </div>\n );\n}\n\nfunction NewerAttemptNotice({\n workspaceSlug,\n projectSlug,\n runId,\n jobId,\n attempt,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n jobId: string;\n attempt: number;\n}) {\n return (\n <div role=\"status\" className=\"border-b border-border-neutral-base px-row py-row\">\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n A newer run attempt is available.\n </Text>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId, jobId}}\n search={workflowJobSearchParams({runAttempt: attempt}) as never}\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n View attempt #{attempt}\n </Link>\n </div>\n </div>\n );\n}\n\nfunction runningStepSelection(jobExecution: JobExecution | undefined) {\n if (!jobExecution) return undefined;\n const steps = [...jobExecution.steps].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {\n const step = steps[stepIndex];\n if (!step) continue;\n const attempt = [...step.attempts]\n .sort((left, right) => left.attempt - right.attempt || left.id.localeCompare(right.id))\n .reverse()\n .find((candidate) => candidate.status === 'running');\n if (attempt)\n return {stepId: step.id, attemptId: attempt.id, stepLabel: step.name || step.key || step.id};\n }\n return undefined;\n}\n\nfunction findAttempt(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {step, attemptId: attempt.id};\n }\n return undefined;\n}\n\nfunction annotationCountForStep(\n summary: RunAnnotationSummary | undefined,\n stepId: string,\n attempt: number,\n): number | undefined {\n return summary?.stepCounts?.find((entry) => entry.stepId === stepId && entry.attempt === attempt)\n ?.total;\n}\n\nfunction JobDetailSkeleton() {\n return (\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto pb-panel\">\n <div className=\"w-full\">\n <header className=\"flex items-center gap-cluster border-b border-border-neutral-base px-row py-row\">\n <Skeleton className=\"size-20 rounded-full\" />\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-24 w-72 rounded-6\" />\n <Skeleton className=\"h-24 w-180 rounded-4\" />\n </header>\n <div className=\"p-panel-compact\">\n {JOB_DETAIL_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex min-h-44 items-center gap-inline border-b border-border-neutral-base last:border-b-0\"\n >\n <Skeleton className=\"size-14 rounded-full\" />\n <Skeleton className=\"h-16 w-180 rounded-4\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst JOB_DETAIL_SKELETON_ROWS = ['step-1', 'step-2', 'step-3', 'step-4', 'step-5'];\n\ninterface FrozenLandingSelection {\n jobId: string;\n jobExecutionId: string;\n selection: WorkflowJobLandingSelection | undefined;\n hasSelection: boolean;\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","Icon","Skeleton","TimeTickerProvider","Text","Link","useEffect","useRef","useState","summarizeJobAnnotations","isWorkflowRunTerminal","useWorkflowRunAnnotationSummaryQuery","useRunAnnotationsQuery","workflowJobSearchParams","workflowRunSearchParams","StepList","WorkflowRunNotFound","WorkflowRunStaleError","JobContextPanel","JobDetailHeader","CarriedOverStepPanel","emptyStateForJob","emptyStateForMissingExecution","jobSucceededSummary","resolveWorkflowJobSelection","workflowJobLandingSelection","StepAttemptLogPanel","StepInspectorSheet","JobDetailView","workspaceSlug","projectSlug","workflowRunId","jobId","search","query","newerAttempt","newerJob","onSelectionChange","rootRef","pageScrollRef","landingSelectionRef","undefined","hasLoadedData","data","annotations","runAttempt","attempt","jobAnnotationSummary","truncated","summary","inspectorResetKey","jobExecutionId","inspectorState","setInspectorState","key","attemptId","inspectorOpenAttemptId","annotationJob","jobs","find","candidate","id","annotationExecutionId","job","selection","jobExecution","annotationPolling","status","annotationSummaryQuery","polling","heading","current","querySelector","focus","preventScroll","isPending","JobDetailSkeleton","isError","error","subject","icon","run","JobNotFoundState","resolvedSelection","selectedJobExecution","hasExplicitStep","Boolean","stepId","step","currentLandingSelection","frozenLanding","hasSelection","landingSelection","selectedAttemptId","runningSelection","runningStepSelection","selectedStepId","selectedAttemptForNotice","showRetargetNotice","succeededSummary","selectExecution","stepAttemptId","selectAttempt","match","findAttempt","onInspectorOpenChange","retargetToRunningStep","intervalMs","reducedMotionIntervalMs","div","ref","className","NewerAttemptNotice","runId","section","aria-label","displayName","onSelectedJobExecutionChange","annotationSummary","jobContext","execution","as","defaultSelectedAttemptId","onSelectedAttemptChange","autoSelectActiveAttempt","emptyState","showHeader","renderExpandedStep","context","ExpandedStep","renderInspector","entry","open","onOpenChange","annotationCount","annotationCountForStep","EmptyStateForMissingExecution","role","aria-live","size","span","stepLabel","button","type","onClick","carriedOver","tabIndex","attemptStatus","attemptStartedAt","surfaceClassName","title","description","variant","to","params","name","aria-hidden","steps","sort","left","right","position","localeCompare","stepIndex","length","attempts","reverse","stepCounts","total","header","JOB_DETAIL_SKELETON_ROWS","map","row"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-empty-states.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-empty-states.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAO9E,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,uBAAuB,CAAC;AAG9D,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,YAAY,GACzB,kBAAkB,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"job-empty-states.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-empty-states.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAO9E,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,uBAAuB,CAAC;AAG9D,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,YAAY,GACzB,kBAAkB,GAAG,SAAS,CAkEhC;AAED,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,GAAG,GAAG,kBAAkB,CA8D1E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,GAAG,CAAC,cAAc,CAAC,GAAG,MAAM,CAoBzE;AAmCD,wBAAgB,oBAAoB,gCAUnC;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACpC,SAAS,GAAG,IAAI,CAgClB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAEjF;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAMzF"}
|
|
@@ -11,10 +11,11 @@ export function emptyStateForJob(job, jobExecution) {
|
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
const displayStatus = job.mode === 'listening' ? deriveJobExecutionDisplayStatus(jobExecution) : deriveJobDisplayStatus(job);
|
|
14
|
+
const runner = jobExecution.runner?.length ? jobExecution.runner : job.runner;
|
|
14
15
|
if (jobExecution.status === 'running' && displayStatus === 'pending') {
|
|
15
16
|
return {
|
|
16
|
-
title: 'Runner preparing job',
|
|
17
|
-
description:
|
|
17
|
+
title: runner?.length ? 'Runner preparing job' : 'Waiting for a runner',
|
|
18
|
+
description: runner?.length ? `Runner ${runner.join(', ')} is preparing the job. Steps will appear here when work begins.` : 'No runner has claimed this job yet. Steps will appear here when a runner starts work.',
|
|
18
19
|
status: displayStatus
|
|
19
20
|
};
|
|
20
21
|
}
|
|
@@ -42,7 +43,7 @@ export function emptyStateForJob(job, jobExecution) {
|
|
|
42
43
|
if (displayStatus === 'failed') {
|
|
43
44
|
return {
|
|
44
45
|
title: 'Job failed before its first step started',
|
|
45
|
-
description: preStepFailureDescription(jobExecution.statusReason ?? job.statusReason),
|
|
46
|
+
description: preStepFailureDescription(jobExecution.statusReason ?? job.statusReason, runner),
|
|
46
47
|
status: displayStatus
|
|
47
48
|
};
|
|
48
49
|
}
|
|
@@ -98,6 +99,13 @@ export function emptyStateForMissingExecution(job) {
|
|
|
98
99
|
status: 'cancelled'
|
|
99
100
|
};
|
|
100
101
|
}
|
|
102
|
+
if (job.status === 'failed') {
|
|
103
|
+
return {
|
|
104
|
+
title: 'Job failed before an execution was created',
|
|
105
|
+
description: preStepFailureDescription(job.statusReason, job.runner),
|
|
106
|
+
status: 'failed'
|
|
107
|
+
};
|
|
108
|
+
}
|
|
101
109
|
return {
|
|
102
110
|
title: 'Execution details unavailable',
|
|
103
111
|
description: 'This job finished, but no job execution record is available.',
|
|
@@ -125,17 +133,18 @@ export function skippedJobDescription(reason) {
|
|
|
125
133
|
return 'This job did not start.';
|
|
126
134
|
}
|
|
127
135
|
}
|
|
128
|
-
function preStepFailureDescription(reason) {
|
|
136
|
+
function preStepFailureDescription(reason, runner = null) {
|
|
137
|
+
const runnerCopy = runner?.length ? ` Required runner labels: ${runner.join(', ')}.` : '';
|
|
129
138
|
switch(reason){
|
|
130
139
|
case 'runner_lost':
|
|
131
|
-
return
|
|
140
|
+
return `The runner stopped responding before work began.${runnerCopy} Check runner availability before re-running the workflow.`;
|
|
132
141
|
case 'timed_out':
|
|
133
|
-
return
|
|
142
|
+
return `The job timed out before work began.${runnerCopy} Check runner capacity and timeout configuration before re-running the workflow.`;
|
|
134
143
|
case 'user_cancelled':
|
|
135
144
|
case 'run_cancelled':
|
|
136
145
|
return 'The execution ended while the run was being cancelled.';
|
|
137
146
|
case 'step_failed':
|
|
138
|
-
return
|
|
147
|
+
return `The execution failed before step details were recorded.${runnerCopy} Review run annotations before re-running the workflow.`;
|
|
139
148
|
case 'condition_errored':
|
|
140
149
|
return 'The job condition could not be evaluated. Review run annotations before re-running the workflow.';
|
|
141
150
|
case 'dependency_not_completed':
|
|
@@ -146,9 +155,9 @@ function preStepFailureDescription(reason) {
|
|
|
146
155
|
return 'The job condition did not match, so no work started.';
|
|
147
156
|
case 'unknown':
|
|
148
157
|
case null:
|
|
149
|
-
return
|
|
158
|
+
return `Shipfox did not record a failure reason.${runnerCopy} Re-run only after checking the runner and workflow configuration.`;
|
|
150
159
|
default:
|
|
151
|
-
return `The execution ended because ${humanizeFailureReason(reason)}
|
|
160
|
+
return `The execution ended because ${humanizeFailureReason(reason)}.${runnerCopy} Resolve that condition before re-running the workflow.`;
|
|
152
161
|
}
|
|
153
162
|
}
|
|
154
163
|
function humanizeFailureReason(reason) {
|
|
@@ -156,7 +165,7 @@ function humanizeFailureReason(reason) {
|
|
|
156
165
|
}
|
|
157
166
|
export function CarriedOverStepPanel() {
|
|
158
167
|
return /*#__PURE__*/ _jsx(EmptyState, {
|
|
159
|
-
className: "min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base
|
|
168
|
+
className: "min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base p-panel",
|
|
160
169
|
icon: "componentLine",
|
|
161
170
|
title: "Carried over from a previous attempt",
|
|
162
171
|
description: "Not executed in this run.",
|