@shipfox/client-workflows 12.0.2 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/preview.tsx +3 -3
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +56 -0
- package/dist/components/job-graph/job-duration-format.d.ts +2 -2
- package/dist/components/job-graph/job-duration-format.d.ts.map +1 -1
- package/dist/components/job-graph/job-duration-format.js +4 -2
- package/dist/components/job-graph/job-duration-format.js.map +1 -1
- package/dist/components/job-graph/job-node.d.ts +1 -1
- package/dist/components/job-graph/job-node.d.ts.map +1 -1
- package/dist/components/job-graph/job-node.js +5 -4
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +2 -2
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts +21 -0
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -0
- package/dist/components/workflow-run-list/job-status-strip.js +185 -0
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -0
- package/dist/components/workflow-run-list/run-display.d.ts +27 -3
- package/dist/components/workflow-run-list/run-display.d.ts.map +1 -1
- package/dist/components/workflow-run-list/run-display.js +78 -7
- package/dist/components/workflow-run-list/run-display.js.map +1 -1
- package/dist/components/workflow-run-list/types.d.ts +23 -24
- package/dist/components/workflow-run-list/types.d.ts.map +1 -1
- package/dist/components/workflow-run-list/types.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts +22 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +68 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts +18 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js +236 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts +8 -4
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-content.js +40 -12
- package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts +29 -2
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +103 -44
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +60 -43
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.js +20 -8
- package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts +19 -9
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +179 -112
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-number-label.d.ts +15 -0
- package/dist/components/workflow-run-number-label.d.ts.map +1 -0
- package/dist/components/workflow-run-number-label.js +41 -0
- package/dist/components/workflow-run-number-label.js.map +1 -0
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts +3 -3
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +8 -7
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +3 -3
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +16 -7
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts +2 -2
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.js +4 -4
- package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +1 -1
- package/dist/components/workflow-run-view/job-card.d.ts +2 -2
- package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-card.js +51 -32
- package/dist/components/workflow-run-view/job-card.js.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.js +20 -8
- package/dist/components/workflow-run-view/job-execution-switcher.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts +3 -2
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +15 -11
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-status/status-visuals.d.ts +4 -4
- package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
- package/dist/components/workflow-status/status-visuals.js +7 -0
- package/dist/components/workflow-status/status-visuals.js.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -4
- package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.js +6 -4
- package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +10 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js +11 -0
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +4 -2
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js +8 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +52 -4
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +22 -3
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/workflow-run.d.ts +6 -6
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js +3 -3
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/feature.d.ts +7 -7
- package/dist/feature.js +5 -5
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts +4 -3
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +31 -3
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/hooks/api/workflow-runs.d.ts +4 -4
- package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
- package/dist/hooks/api/workflow-runs.js +12 -4
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/pages/project-workflows-page.js +46 -1
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-detail-page.d.ts +11 -0
- package/dist/pages/workflow-run-detail-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-detail-page.js +30 -0
- package/dist/pages/workflow-run-detail-page.js.map +1 -0
- package/dist/pages/workflow-run-list-page.d.ts +10 -0
- package/dist/pages/workflow-run-list-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-list-page.js +43 -0
- package/dist/pages/workflow-run-list-page.js.map +1 -0
- package/dist/routes/inputs.d.ts +61 -5
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +150 -13
- package/dist/routes/inputs.js.map +1 -1
- package/dist/routes/project-route.d.ts +6 -0
- package/dist/routes/project-route.d.ts.map +1 -0
- package/dist/routes/project-route.js +25 -0
- package/dist/routes/project-route.js.map +1 -0
- package/dist/routes/run-detail.d.ts.map +1 -1
- package/dist/routes/run-detail.js +12 -7
- package/dist/routes/run-detail.js.map +1 -1
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +11 -6
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +6 -5
- package/dist/routes/workflows.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/components/job-graph/job-duration-format.ts +11 -2
- package/src/components/job-graph/job-graph-view.test.tsx +19 -8
- package/src/components/job-graph/job-graph.stories.tsx +23 -19
- package/src/components/job-graph/job-node.stories.tsx +10 -5
- package/src/components/job-graph/job-node.test.tsx +55 -10
- package/src/components/job-graph/job-node.tsx +16 -9
- package/src/components/step-list/step-list.stories.tsx +2 -3
- package/src/components/step-list/step-list.tsx +3 -2
- package/src/components/workflow-run-list/job-status-strip.tsx +189 -0
- package/src/components/workflow-run-list/run-display.test.ts +169 -30
- package/src/components/workflow-run-list/run-display.ts +116 -9
- package/src/components/workflow-run-list/types.ts +21 -17
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +100 -0
- package/src/components/workflow-run-list/workflow-run-filters.tsx +243 -0
- package/src/components/workflow-run-list/workflow-run-list-content.tsx +44 -16
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +128 -27
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +441 -104
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +73 -43
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +140 -20
- package/src/components/workflow-run-list/workflow-run-list.tsx +20 -10
- package/src/components/workflow-run-list/workflow-run-row.tsx +173 -106
- package/src/components/workflow-run-number-label.test.tsx +45 -0
- package/src/components/workflow-run-number-label.tsx +47 -0
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +7 -8
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +13 -12
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +6 -8
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +27 -2
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +19 -9
- package/src/components/workflow-run-view/agent-config-failure-callout.stories.tsx +2 -3
- package/src/components/workflow-run-view/agent-config-failure-callout.tsx +4 -4
- package/src/components/workflow-run-view/job-card.stories.tsx +11 -10
- package/src/components/workflow-run-view/job-card.test.tsx +115 -0
- package/src/components/workflow-run-view/job-card.tsx +74 -35
- package/src/components/workflow-run-view/job-execution-switcher.tsx +31 -9
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +88 -30
- package/src/components/workflow-run-view/workflow-run-view.tsx +24 -14
- package/src/components/workflow-status/status-visuals.test.ts +9 -0
- package/src/components/workflow-status/status-visuals.ts +6 -4
- package/src/components/workflow-status/workflow-status-icon.stories.tsx +10 -2
- package/src/components/workflow-status/workflow-status-icon.test.tsx +18 -0
- package/src/components/workflow-status/workflow-status-icon.tsx +17 -24
- package/src/core/entities/job-execution.ts +22 -0
- package/src/core/entities/job-status.test.ts +104 -0
- package/src/core/entities/job.ts +18 -3
- package/src/core/entities/workflow-run.ts +76 -7
- package/src/core/workflow-run.test.ts +24 -9
- package/src/core/workflow-run.ts +17 -2
- package/src/feature.ts +5 -5
- package/src/hooks/api/workflow-run-mapper.ts +30 -3
- package/src/hooks/api/workflow-runs.test.tsx +2 -0
- package/src/hooks/api/workflow-runs.ts +12 -7
- package/src/index.ts +7 -2
- package/src/page-harness-budget.test.ts +1 -1
- package/src/pages/project-workflows-page.test.tsx +59 -5
- package/src/pages/project-workflows-page.tsx +34 -1
- package/src/pages/workflow-run-detail-page.tsx +45 -0
- package/src/pages/workflow-run-list-page.tsx +65 -0
- package/src/pages/{workflow-run-page.test.tsx → workflow-run-pages.test.tsx} +211 -47
- package/src/routes/inputs.test.ts +193 -12
- package/src/routes/inputs.ts +170 -11
- package/src/routes/project-route.tsx +25 -0
- package/src/routes/run-detail.tsx +15 -8
- package/src/routes/runs.tsx +14 -7
- package/src/routes/workflows.tsx +5 -4
- package/test/fixtures/workflow-run.ts +76 -7
- package/test/pages.tsx +18 -8
- package/test/render.tsx +8 -2
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts +0 -10
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts.map +0 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.js +0 -55
- package/dist/components/workflow-run-list/workflow-run-list-header.js.map +0 -1
- package/dist/pages/workflow-run-first-time-use.d.ts +0 -6
- package/dist/pages/workflow-run-first-time-use.d.ts.map +0 -1
- package/dist/pages/workflow-run-first-time-use.js +0 -17
- package/dist/pages/workflow-run-first-time-use.js.map +0 -1
- package/dist/pages/workflow-run-page.d.ts +0 -10
- package/dist/pages/workflow-run-page.d.ts.map +0 -1
- package/dist/pages/workflow-run-page.js +0 -90
- package/dist/pages/workflow-run-page.js.map +0 -1
- package/src/components/workflow-run-list/workflow-run-list-header.tsx +0 -53
- package/src/pages/workflow-run-first-time-use.tsx +0 -17
- package/src/pages/workflow-run-page.tsx +0 -94
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-view/job-card.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Text} from '@shipfox/react-ui/typography';\nimport type {ReactNode} from 'react';\nimport {Fragment, useId, useRef, useState} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {\n AGENT_CONFIG_ISSUES,\n type Job,\n type JobExecution,\n type JobExecutionTime,\n STEP_ERROR_REASONS,\n type Step,\n type StepError,\n type StepSourceLocation,\n WORKFLOW_JOB_STATUSES,\n workflowRunTriggerDisplayLabel,\n workflowRunTriggerLabel,\n} from '#core/workflow-run.js';\nimport {type StepExpandedContext, StepList, type StepListEmptyState} from '../step-list/index.js';\nimport {AgentConfigFailureCallout} from './agent-config-failure-callout.js';\nimport {JobExecutionSwitcher} from './job-execution-switcher.js';\nimport {formatJobExecutionTime, JobExecutionTimeText} from './job-execution-time-text.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\n\nconst STATUS_BADGE_LABEL_WIDTH_CH = Math.max(\n ...WORKFLOW_JOB_STATUSES.map((status) => getWorkflowStatusVisual(status).label.length),\n);\n\ninterface LocalSelectedAttempt {\n jobExecutionId: string | undefined;\n attemptId: string | null;\n}\n\nexport function JobCard({\n workspaceId,\n job,\n selectedJobExecution,\n selectedAttemptId,\n onSelectedJobExecutionChange,\n onSelectedAttemptChange,\n renderExpandedStep,\n sourcePanelId,\n sourceAvailable,\n focusedSourceStepId,\n onOpenStepSource,\n}: {\n workspaceId: string;\n job: Job;\n selectedJobExecution: JobExecution | undefined;\n selectedAttemptId: string | null | undefined;\n onSelectedJobExecutionChange: ((jobExecutionId: string | undefined) => void) | undefined;\n onSelectedAttemptChange: ((attemptId: string | undefined) => void) | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n sourcePanelId?: string | undefined;\n sourceAvailable?: boolean | undefined;\n focusedSourceStepId?: string | null | undefined;\n onOpenStepSource?:\n | ((stepId: string, location: StepSourceLocation, trigger: HTMLButtonElement | null) => void)\n | undefined;\n}) {\n const titleId = useId();\n const sourceButtonRef = useRef<HTMLButtonElement>(null);\n const [localSelectedAttempt, setLocalSelectedAttempt] = useState<LocalSelectedAttempt>({\n jobExecutionId: undefined,\n attemptId: null,\n });\n const selectedExecutionStatus = selectedJobExecution?.status ?? job.status;\n const effectiveSelectedAttemptId =\n selectedAttemptId === undefined &&\n localSelectedAttempt.jobExecutionId === selectedJobExecution?.id\n ? localSelectedAttempt.attemptId\n : selectedAttemptId;\n const selectedSourceAction = selectedStepSourceAction(\n selectedJobExecution,\n effectiveSelectedAttemptId,\n );\n const defaultRenderExpandedStep = ({\n step,\n stepId,\n attempt,\n attemptError,\n attemptStatus,\n carriedOver,\n }: StepExpandedContext) =>\n carriedOver ? (\n <CarriedOverStepPanel />\n ) : (\n <StepAttemptDetailPanel\n workspaceId={workspaceId}\n step={step}\n stepId={stepId}\n attempt={attempt}\n attemptError={attemptError}\n attemptStatus={attemptStatus}\n />\n );\n\n function selectAttempt(attemptId: string | undefined) {\n if (selectedAttemptId === undefined) {\n setLocalSelectedAttempt({\n jobExecutionId: selectedJobExecution?.id,\n attemptId: attemptId ?? null,\n });\n }\n\n onSelectedAttemptChange?.(attemptId);\n }\n\n return (\n <section\n aria-labelledby={titleId}\n className=\"flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base\"\n >\n <div className=\"grid min-h-52 min-w-0 grid-cols-1 gap-y-4 px-16 py-12\">\n <div className=\"flex min-w-0 flex-wrap items-center justify-between gap-10\">\n {job.mode === 'listening' &&\n job.jobExecutions.length > 0 &&\n onSelectedJobExecutionChange ? (\n <>\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"sr-only\">\n {job.displayName}\n </Text>\n <div className=\"flex min-w-0 items-center gap-8\">\n <JobStatusBadge status={selectedExecutionStatus} />\n <JobExecutionSwitcher\n job={job}\n selectedJobExecution={selectedJobExecution?.id ?? null}\n onSelectedJobExecutionChange={onSelectedJobExecutionChange}\n variant=\"title\"\n />\n </div>\n </>\n ) : (\n <div className=\"flex min-w-0 items-center gap-8\">\n <JobStatusBadge status={selectedExecutionStatus} />\n <Text\n as=\"h2\"\n id={titleId}\n size=\"sm\"\n bold\n className=\"min-w-0 truncate text-foreground-neutral-base\"\n >\n {job.displayName}\n </Text>\n </div>\n )}\n {sourceAvailable && selectedSourceAction && sourcePanelId && onOpenStepSource ? (\n <div className=\"flex min-w-max items-center gap-6\">\n <Button\n ref={sourceButtonRef}\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n aria-controls={sourcePanelId}\n aria-expanded={focusedSourceStepId === selectedSourceAction.stepId}\n onClick={() =>\n onOpenStepSource(\n selectedSourceAction.stepId,\n selectedSourceAction.location,\n sourceButtonRef.current,\n )\n }\n >\n View source\n </Button>\n </div>\n ) : null}\n </div>\n <JobExecutionMetadata execution={selectedJobExecution} />\n </div>\n <div className=\"min-h-0 border-t border-border-neutral-strong\">\n {selectedJobExecution ? (\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n onSelectedAttemptChange={selectAttempt}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent\"\n renderExpandedStep={renderExpandedStep ?? defaultRenderExpandedStep}\n />\n ) : (\n <JobExecutionEmptyState job={job} />\n )}\n </div>\n </section>\n );\n}\n\nfunction JobStatusBadge({status}: {status: Job['status'] | JobExecution['status']}) {\n const visual = getWorkflowStatusVisual(status);\n\n return (\n <Badge variant={visual.badge} size=\"xs\">\n <span className=\"text-center\" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>\n {visual.label}\n </span>\n </Badge>\n );\n}\n\nfunction JobExecutionEmptyState({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n\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 JobExecutionMetadata({execution}: {execution: JobExecution | undefined}) {\n if (!execution) return null;\n\n const queueTime = execution.queueTime;\n const runTime = execution.runTime;\n const executionStartedAt = execution.startedAt ?? execution.queuedAt ?? execution.createdAt;\n const items = [\n execution.triggerEvents.length > 0 ? {key: 'trigger', kind: 'trigger' as const} : null,\n {key: 'started', kind: 'started' as const, value: executionStartedAt},\n queueTime\n ? {key: 'queue', icon: 'hourglassLine' as const, kind: 'queue' as const, time: queueTime}\n : null,\n runTime ? {key: 'run', icon: 'timerLine' as const, kind: 'run' as const, time: runTime} : null,\n ].filter((item) => item !== null);\n\n if (items.length === 0) return null;\n\n return (\n <div className=\"flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-muted\">\n {items.map((item, index) => (\n <Fragment key={item.key}>\n {index > 0 ? <MetadataSeparator /> : null}\n {item.kind === 'started' ? (\n <RelativeTime\n value={item.value}\n className=\"shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-muted\"\n />\n ) : item.kind === 'trigger' ? (\n <JobExecutionTriggerMetadata execution={execution} />\n ) : (\n <JobExecutionMetadataItem icon={item.icon} kind={item.kind} time={item.time} />\n )}\n </Fragment>\n ))}\n </div>\n );\n}\n\nfunction JobExecutionTriggerMetadata({execution}: {execution: JobExecution}) {\n const triggerEvent = execution.triggerEvents[0];\n if (!triggerEvent) return null;\n\n const triggerLabel = workflowRunTriggerLabel({\n triggerSource: triggerEvent.source,\n triggerEvent: triggerEvent.event,\n });\n const triggerDisplayLabel = workflowRunTriggerDisplayLabel({\n triggerSource: triggerEvent.source,\n triggerEvent: triggerEvent.event,\n });\n const triggerCount = execution.triggerEvents.length;\n const countLabel = triggerCount > 1 ? ` (${triggerCount})` : '';\n const tooltip = triggerCount > 1 ? `${triggerLabel} (${triggerCount} events)` : triggerLabel;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={tooltip}\n className=\"inline-flex min-w-0 max-w-full cursor-default items-center gap-4 rounded-4 border-0 bg-transparent p-0 text-left text-foreground-neutral-muted outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <TriggerSourceIcon\n provider={triggerEvent.source}\n source={triggerEvent.source}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {triggerDisplayLabel}\n {countLabel}\n </Text>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {tooltip}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobExecutionMetadataItem({\n icon,\n kind,\n time,\n}: {\n icon: 'hourglassLine' | 'timerLine';\n kind: 'queue' | 'run';\n time: JobExecutionTime;\n}) {\n useTimeTick();\n\n const tooltip = `${jobExecutionTimeVerb(kind, time)} for ${formatJobExecutionTime(time)}`;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={tooltip}\n className=\"inline-flex min-w-0 cursor-default items-center gap-4 rounded-4 border-0 bg-transparent p-0 text-foreground-neutral-muted outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name={icon} className=\"size-12 shrink-0\" aria-hidden=\"true\" />\n <span className=\"shrink-0 text-xs leading-20 text-foreground-neutral-muted tabular-nums\">\n <JobExecutionTimeText time={time} />\n </span>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\">\n {tooltip}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction jobExecutionTimeVerb(kind: 'queue' | 'run', time: JobExecutionTime): string {\n if (kind === 'queue') return time.state === 'live' ? 'Queuing' : 'Queued';\n return time.state === 'live' ? 'Running' : 'Ran';\n}\n\nfunction MetadataSeparator() {\n return <span aria-hidden=\"true\" className=\"h-12 w-px shrink-0 bg-border-neutral-strong\" />;\n}\n\nfunction selectedStepSourceAction(\n jobExecution: JobExecution | undefined,\n selectedAttemptId: string | null | undefined,\n): {stepId: string; location: StepSourceLocation} | null {\n if (!jobExecution || !selectedAttemptId) return null;\n\n for (const step of jobExecution.steps) {\n const selected =\n selectedAttemptId === `carried-over:${step.id}` ||\n step.attempts.some((attempt) => attempt.id === selectedAttemptId);\n if (selected && step.sourceLocation) {\n return {stepId: step.id, location: step.sourceLocation};\n }\n }\n\n return null;\n}\n\nfunction StepAttemptDetailPanel({\n workspaceId,\n step,\n stepId,\n attempt,\n attemptError,\n attemptStatus,\n}: {\n workspaceId: string;\n step: Step;\n stepId: string;\n attempt: number;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n}) {\n const selectedAttemptError = toSelectedAttemptError(step, attemptError) ?? step.error;\n\n return (\n <div className=\"flex min-w-0 flex-col gap-10\">\n {isAgentConfigFailure(step, selectedAttemptError) ? (\n <AgentConfigFailureCallout\n workspaceId={workspaceId}\n config={step.agentConfig}\n error={selectedAttemptError}\n />\n ) : null}\n <StepAttemptLogPanel stepId={stepId} attempt={attempt} attemptStatus={attemptStatus} />\n </div>\n );\n}\n\nfunction toSelectedAttemptError(\n step: Step,\n error: Record<string, unknown> | null,\n): StepError | null {\n if (error === null) return null;\n\n const rawReason = error.reason;\n const parsedReason =\n typeof rawReason === 'string' &&\n STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)\n ? (rawReason as NonNullable<StepError['reason']>)\n : undefined;\n const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;\n const agentConfigIssue =\n typeof rawAgentConfigIssue === 'string' &&\n AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n : undefined;\n const exitCode = error.exitCode ?? error.exit_code;\n const resolvedReason = parsedReason\n ? parsedReason\n : agentConfigIssue\n ? 'agent_config_invalid'\n : undefined;\n\n if (resolvedReason === undefined) return null;\n\n return {\n message: typeof error.message === 'string' ? error.message : '',\n exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,\n signal: typeof error.signal === 'string' ? error.signal : undefined,\n reason: resolvedReason,\n agentConfigIssue,\n category: step.type === 'setup' ? 'setup' : 'user',\n };\n}\n\nfunction isAgentConfigFailure(step: Step, error: StepError | null): boolean {\n return step.type === 'agent' && error?.reason === 'agent_config_invalid';\n}\n\nfunction emptyStateForJob(job: Job, jobExecution: JobExecution): StepListEmptyState | undefined {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (jobExecution.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'Steps will appear here once the job starts.',\n status: jobExecution.status,\n };\n }\n\n if (jobExecution.status === 'running') {\n return {\n title: 'Waiting for the first step',\n description: 'This job is running, but no steps have started yet.',\n status: jobExecution.status,\n };\n }\n\n if (jobExecution.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before any step started.',\n status: jobExecution.status,\n };\n }\n\n if (jobExecution.status === 'succeeded' || jobExecution.status === 'failed') {\n return {\n title: 'No steps recorded',\n description: `Execution #${jobExecution.sequence} finished without recorded steps.`,\n status: jobExecution.status,\n };\n }\n\n return undefined;\n}\n\nfunction emptyStateForMissingExecution(job: Job): StepListEmptyState {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'listening') {\n return {\n title: 'Waiting for trigger events',\n description: 'Matching trigger events will create job executions here.',\n status: 'running',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'resolved') {\n return {\n title: 'Listener resolved without executions',\n description: 'No matching trigger event created a job execution before the listener stopped.',\n status: job.status,\n };\n }\n\n if (job.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'No execution has been created for this job yet.',\n status: 'pending',\n };\n }\n\n if (job.status === 'skipped') {\n return {\n title: 'This job was skipped',\n description: skippedJobDescription(job.statusReason),\n status: 'skipped',\n };\n }\n\n if (job.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before an execution was created.',\n status: 'cancelled',\n };\n }\n\n return {\n title: 'Execution details unavailable',\n description: 'This job finished, but no job execution record is available.',\n status: job.status,\n };\n}\n\nfunction CarriedOverStepPanel() {\n return (\n <EmptyState\n className=\"min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base px-16 py-20\"\n icon=\"componentLine\"\n title=\"Carried over from a previous attempt\"\n description=\"Not executed in this run.\"\n variant=\"compact\"\n />\n );\n}\n\nfunction skippedJobDescription(reason: Job['statusReason']): string {\n switch (reason) {\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job was skipped.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was skipped.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so this job was skipped.';\n case 'condition_errored':\n return 'The job condition could not be evaluated, so this job was skipped.';\n case 'user_cancelled':\n case 'run_cancelled':\n case 'timed_out':\n case 'runner_lost':\n case 'step_failed':\n case 'unknown':\n case null:\n return 'This job did not start.';\n }\n}\n"],"names":["TriggerSourceIcon","Badge","Button","EmptyState","Icon","RelativeTime","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Text","Fragment","useId","useRef","useState","getWorkflowStatusVisual","AGENT_CONFIG_ISSUES","STEP_ERROR_REASONS","WORKFLOW_JOB_STATUSES","workflowRunTriggerDisplayLabel","workflowRunTriggerLabel","StepList","AgentConfigFailureCallout","JobExecutionSwitcher","formatJobExecutionTime","JobExecutionTimeText","StepAttemptLogPanel","STATUS_BADGE_LABEL_WIDTH_CH","Math","max","map","status","label","length","JobCard","workspaceId","job","selectedJobExecution","selectedAttemptId","onSelectedJobExecutionChange","onSelectedAttemptChange","renderExpandedStep","sourcePanelId","sourceAvailable","focusedSourceStepId","onOpenStepSource","titleId","sourceButtonRef","localSelectedAttempt","setLocalSelectedAttempt","jobExecutionId","undefined","attemptId","selectedExecutionStatus","effectiveSelectedAttemptId","id","selectedSourceAction","selectedStepSourceAction","defaultRenderExpandedStep","step","stepId","attempt","attemptError","attemptStatus","carriedOver","CarriedOverStepPanel","StepAttemptDetailPanel","selectAttempt","section","aria-labelledby","className","div","mode","jobExecutions","as","size","bold","displayName","JobStatusBadge","variant","ref","type","aria-controls","aria-expanded","onClick","location","current","JobExecutionMetadata","execution","jobExecution","autoSelectActiveAttempt","emptyState","emptyStateForJob","showHeader","JobExecutionEmptyState","visual","badge","span","style","width","emptyStateForMissingExecution","icon","title","description","queueTime","runTime","executionStartedAt","startedAt","queuedAt","createdAt","items","triggerEvents","key","kind","value","time","filter","item","index","MetadataSeparator","JobExecutionTriggerMetadata","JobExecutionMetadataItem","triggerEvent","triggerLabel","triggerSource","source","event","triggerDisplayLabel","triggerCount","countLabel","tooltip","asChild","button","aria-label","provider","aria-hidden","jobExecutionTimeVerb","name","state","steps","selected","attempts","some","sourceLocation","selectedAttemptError","toSelectedAttemptError","error","isAgentConfigFailure","config","agentConfig","rawReason","reason","parsedReason","has","rawAgentConfigIssue","agentConfigIssue","agent_config_issue","exitCode","exit_code","resolvedReason","message","signal","category","sequence","listenerStatus","skippedJobDescription","statusReason"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-view/job-card.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Text} from '@shipfox/react-ui/typography';\nimport type {ReactNode} from 'react';\nimport {Fragment, useId, useRef, useState} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {\n AGENT_CONFIG_ISSUES,\n defaultJobExecution,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n type Job,\n type JobDisplayStatus,\n type JobExecution,\n type JobExecutionTime,\n STEP_ERROR_REASONS,\n type Step,\n type StepError,\n type StepSourceLocation,\n WORKFLOW_JOB_STATUSES,\n workflowRunTriggerDisplayLabel,\n workflowRunTriggerLabel,\n} from '#core/workflow-run.js';\nimport {type StepExpandedContext, StepList, type StepListEmptyState} from '../step-list/index.js';\nimport {AgentConfigFailureCallout} from './agent-config-failure-callout.js';\nimport {JobExecutionSwitcher} from './job-execution-switcher.js';\nimport {formatJobExecutionTime, JobExecutionTimeText} from './job-execution-time-text.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\n\nconst STATUS_BADGE_LABEL_WIDTH_CH = Math.max(\n ...[...WORKFLOW_JOB_STATUSES, 'listening' as const].map(\n (status) => getWorkflowStatusVisual(status).label.length,\n ),\n);\n\ninterface LocalSelectedAttempt {\n jobExecutionId: string | undefined;\n attemptId: string | null;\n}\n\nexport function JobCard({\n workspaceSlug,\n job,\n selectedJobExecution,\n selectedAttemptId,\n onSelectedJobExecutionChange,\n onSelectedAttemptChange,\n renderExpandedStep,\n sourcePanelId,\n sourceAvailable,\n focusedSourceStepId,\n onOpenStepSource,\n}: {\n workspaceSlug?: string | undefined;\n job: Job;\n selectedJobExecution: JobExecution | undefined;\n selectedAttemptId: string | null | undefined;\n onSelectedJobExecutionChange: ((jobExecutionId: string | undefined) => void) | undefined;\n onSelectedAttemptChange: ((attemptId: string | undefined) => void) | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n sourcePanelId?: string | undefined;\n sourceAvailable?: boolean | undefined;\n focusedSourceStepId?: string | null | undefined;\n onOpenStepSource?:\n | ((stepId: string, location: StepSourceLocation, trigger: HTMLButtonElement | null) => void)\n | undefined;\n}) {\n const titleId = useId();\n const sourceButtonRef = useRef<HTMLButtonElement>(null);\n const [localSelectedAttempt, setLocalSelectedAttempt] = useState<LocalSelectedAttempt>({\n jobExecutionId: undefined,\n attemptId: null,\n });\n const title = selectedJobExecution?.displayName ?? job.displayName;\n const isNonDefaultExecutionSelected =\n selectedJobExecution !== undefined && selectedJobExecution.id !== defaultJobExecution(job)?.id;\n const selectedExecutionStatus = isNonDefaultExecutionSelected\n ? deriveJobExecutionDisplayStatus(selectedJobExecution)\n : deriveJobDisplayStatus(job);\n const effectiveSelectedAttemptId =\n selectedAttemptId === undefined &&\n localSelectedAttempt.jobExecutionId === selectedJobExecution?.id\n ? localSelectedAttempt.attemptId\n : selectedAttemptId;\n const selectedSourceAction = selectedStepSourceAction(\n selectedJobExecution,\n effectiveSelectedAttemptId,\n );\n const defaultRenderExpandedStep = ({\n step,\n stepId,\n attempt,\n attemptError,\n attemptStatus,\n carriedOver,\n }: StepExpandedContext) =>\n carriedOver ? (\n <CarriedOverStepPanel />\n ) : (\n <StepAttemptDetailPanel\n workspaceSlug={workspaceSlug}\n step={step}\n stepId={stepId}\n attempt={attempt}\n attemptError={attemptError}\n attemptStatus={attemptStatus}\n />\n );\n\n function selectAttempt(attemptId: string | undefined) {\n if (selectedAttemptId === undefined) {\n setLocalSelectedAttempt({\n jobExecutionId: selectedJobExecution?.id,\n attemptId: attemptId ?? null,\n });\n }\n\n onSelectedAttemptChange?.(attemptId);\n }\n\n return (\n <section\n aria-labelledby={titleId}\n className=\"flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-components-base\"\n >\n <div className=\"grid min-h-52 min-w-0 grid-cols-1 gap-y-4 px-16 py-12\">\n <div className=\"flex min-w-0 flex-wrap items-center justify-between gap-10\">\n {job.mode === 'listening' &&\n job.jobExecutions.length > 0 &&\n onSelectedJobExecutionChange ? (\n <>\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"sr-only\">\n {title}\n </Text>\n <div className=\"flex min-w-0 items-center gap-8\">\n <JobStatusBadge status={selectedExecutionStatus} />\n <JobExecutionSwitcher\n job={job}\n selectedJobExecution={selectedJobExecution?.id ?? null}\n onSelectedJobExecutionChange={onSelectedJobExecutionChange}\n variant=\"title\"\n />\n </div>\n </>\n ) : (\n <div className=\"flex min-w-0 items-center gap-8\">\n <JobStatusBadge status={selectedExecutionStatus} />\n <div className=\"flex min-w-0 items-center gap-8\">\n <Text\n as=\"h2\"\n id={titleId}\n size=\"sm\"\n bold\n className=\"min-w-0 truncate text-foreground-neutral-base\"\n >\n {title}\n </Text>\n </div>\n </div>\n )}\n {sourceAvailable && selectedSourceAction && sourcePanelId && onOpenStepSource ? (\n <div className=\"flex min-w-max items-center gap-6\">\n <Button\n ref={sourceButtonRef}\n type=\"button\"\n variant=\"secondary\"\n size=\"xs\"\n aria-controls={sourcePanelId}\n aria-expanded={focusedSourceStepId === selectedSourceAction.stepId}\n onClick={() =>\n onOpenStepSource(\n selectedSourceAction.stepId,\n selectedSourceAction.location,\n sourceButtonRef.current,\n )\n }\n >\n View source\n </Button>\n </div>\n ) : null}\n </div>\n <JobExecutionMetadata execution={selectedJobExecution} />\n </div>\n <div className=\"min-h-0 border-t border-border-neutral-strong\">\n {selectedJobExecution ? (\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n onSelectedAttemptChange={selectAttempt}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent\"\n renderExpandedStep={renderExpandedStep ?? defaultRenderExpandedStep}\n />\n ) : (\n <JobExecutionEmptyState job={job} />\n )}\n </div>\n </section>\n );\n}\n\nfunction JobStatusBadge({status}: {status: JobDisplayStatus}) {\n const visual = getWorkflowStatusVisual(status);\n\n return (\n <Badge variant={visual.badge} size=\"xs\">\n <span className=\"text-center\" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>\n {visual.label}\n </span>\n </Badge>\n );\n}\n\nfunction JobExecutionEmptyState({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n\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 JobExecutionMetadata({execution}: {execution: JobExecution | undefined}) {\n if (!execution) return null;\n\n const queueTime = execution.queueTime;\n const runTime = execution.runTime;\n const executionStartedAt = execution.startedAt ?? execution.queuedAt ?? execution.createdAt;\n const items = [\n execution.triggerEvents.length > 0 ? {key: 'trigger', kind: 'trigger' as const} : null,\n {key: 'started', kind: 'started' as const, value: executionStartedAt},\n queueTime\n ? {key: 'queue', icon: 'hourglassLine' as const, kind: 'queue' as const, time: queueTime}\n : null,\n runTime ? {key: 'run', icon: 'timerLine' as const, kind: 'run' as const, time: runTime} : null,\n ].filter((item) => item !== null);\n\n if (items.length === 0) return null;\n\n return (\n <div className=\"flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-muted\">\n {items.map((item, index) => (\n <Fragment key={item.key}>\n {index > 0 ? <MetadataSeparator /> : null}\n {item.kind === 'started' ? (\n <RelativeTime\n value={item.value}\n className=\"shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-muted\"\n />\n ) : item.kind === 'trigger' ? (\n <JobExecutionTriggerMetadata execution={execution} />\n ) : (\n <JobExecutionMetadataItem\n icon={item.icon}\n kind={item.kind}\n time={item.time}\n displayStatus={deriveJobExecutionDisplayStatus(execution)}\n />\n )}\n </Fragment>\n ))}\n </div>\n );\n}\n\nfunction JobExecutionTriggerMetadata({execution}: {execution: JobExecution}) {\n const triggerEvent = execution.triggerEvents[0];\n if (!triggerEvent) return null;\n\n const triggerLabel = workflowRunTriggerLabel({\n triggerSource: triggerEvent.source,\n triggerEvent: triggerEvent.event,\n });\n const triggerDisplayLabel = workflowRunTriggerDisplayLabel({\n triggerSource: triggerEvent.source,\n triggerEvent: triggerEvent.event,\n });\n const triggerCount = execution.triggerEvents.length;\n const countLabel = triggerCount > 1 ? ` (${triggerCount})` : '';\n const tooltip = triggerCount > 1 ? `${triggerLabel} (${triggerCount} events)` : triggerLabel;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={tooltip}\n className=\"inline-flex min-w-0 max-w-full cursor-default items-center gap-4 rounded-4 border-0 bg-transparent p-0 text-left text-foreground-neutral-muted outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <TriggerSourceIcon\n provider={triggerEvent.source}\n source={triggerEvent.source}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {triggerDisplayLabel}\n {countLabel}\n </Text>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {tooltip}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobExecutionMetadataItem({\n icon,\n kind,\n time,\n displayStatus,\n}: {\n icon: 'hourglassLine' | 'timerLine';\n kind: 'queue' | 'run';\n time: JobExecutionTime;\n displayStatus: JobExecution['status'];\n}) {\n useTimeTick();\n\n const tooltip = `${jobExecutionTimeVerb(kind, time, displayStatus)} for ${formatJobExecutionTime(time)}`;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={tooltip}\n className=\"inline-flex min-w-0 cursor-default items-center gap-4 rounded-4 border-0 bg-transparent p-0 text-foreground-neutral-muted outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name={icon} className=\"size-12 shrink-0\" aria-hidden=\"true\" />\n <span className=\"shrink-0 text-xs leading-20 text-foreground-neutral-muted tabular-nums\">\n <JobExecutionTimeText time={time} />\n </span>\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\">\n {tooltip}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction jobExecutionTimeVerb(\n kind: 'queue' | 'run',\n time: JobExecutionTime,\n displayStatus: JobExecution['status'],\n): string {\n if (kind === 'queue') return time.state === 'live' ? 'Queueing' : 'Queued';\n if (time.state !== 'live') return 'Ran';\n if (displayStatus === 'running') return 'Running';\n return displayStatus === 'pending' ? 'Runner assigned' : 'Execution elapsed';\n}\n\nfunction MetadataSeparator() {\n return <span aria-hidden=\"true\" className=\"h-12 w-px shrink-0 bg-border-neutral-strong\" />;\n}\n\nfunction selectedStepSourceAction(\n jobExecution: JobExecution | undefined,\n selectedAttemptId: string | null | undefined,\n): {stepId: string; location: StepSourceLocation} | null {\n if (!jobExecution || !selectedAttemptId) return null;\n\n for (const step of jobExecution.steps) {\n const selected =\n selectedAttemptId === `carried-over:${step.id}` ||\n step.attempts.some((attempt) => attempt.id === selectedAttemptId);\n if (selected && step.sourceLocation) {\n return {stepId: step.id, location: step.sourceLocation};\n }\n }\n\n return null;\n}\n\nfunction StepAttemptDetailPanel({\n workspaceSlug,\n step,\n stepId,\n attempt,\n attemptError,\n attemptStatus,\n}: {\n workspaceSlug?: string | undefined;\n step: Step;\n stepId: string;\n attempt: number;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n}) {\n const selectedAttemptError = toSelectedAttemptError(step, attemptError) ?? step.error;\n\n return (\n <div className=\"flex min-w-0 flex-col gap-10\">\n {isAgentConfigFailure(step, selectedAttemptError) ? (\n <AgentConfigFailureCallout\n workspaceSlug={workspaceSlug}\n config={step.agentConfig}\n error={selectedAttemptError}\n />\n ) : null}\n <StepAttemptLogPanel stepId={stepId} attempt={attempt} attemptStatus={attemptStatus} />\n </div>\n );\n}\n\nfunction toSelectedAttemptError(\n step: Step,\n error: Record<string, unknown> | null,\n): StepError | null {\n if (error === null) return null;\n\n const rawReason = error.reason;\n const parsedReason =\n typeof rawReason === 'string' &&\n STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)\n ? (rawReason as NonNullable<StepError['reason']>)\n : undefined;\n const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;\n const agentConfigIssue =\n typeof rawAgentConfigIssue === 'string' &&\n AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n : undefined;\n const exitCode = error.exitCode ?? error.exit_code;\n const resolvedReason = parsedReason\n ? parsedReason\n : agentConfigIssue\n ? 'agent_config_invalid'\n : undefined;\n\n if (resolvedReason === undefined) return null;\n\n return {\n message: typeof error.message === 'string' ? error.message : '',\n exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,\n signal: typeof error.signal === 'string' ? error.signal : undefined,\n reason: resolvedReason,\n agentConfigIssue,\n category: step.type === 'setup' || step.type === 'checkout' ? 'setup' : 'user',\n };\n}\n\nfunction isAgentConfigFailure(step: Step, error: StepError | null): boolean {\n return step.type === 'agent' && error?.reason === 'agent_config_invalid';\n}\n\nfunction emptyStateForJob(job: Job, jobExecution: JobExecution): StepListEmptyState | undefined {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n const displayStatus =\n job.mode === 'listening'\n ? deriveJobExecutionDisplayStatus(jobExecution)\n : deriveJobDisplayStatus(job);\n\n if (jobExecution.status === 'running' && displayStatus === 'pending') {\n return {\n title: 'Runner preparing job',\n description: 'A runner is assigned. Steps will appear here when work begins.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'Steps will appear here once the job starts.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'running') {\n return {\n title: 'Waiting for the first step',\n description: 'The first running step will appear here shortly.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before any step started.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'succeeded' || displayStatus === 'failed') {\n return {\n title: 'No steps recorded',\n description: `Execution #${jobExecution.sequence} finished without recorded steps.`,\n status: displayStatus,\n };\n }\n\n return undefined;\n}\n\nfunction emptyStateForMissingExecution(job: Job): StepListEmptyState {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'listening') {\n return {\n title: 'Waiting for trigger events',\n description: 'Matching trigger events will create job executions here.',\n status: deriveJobDisplayStatus(job),\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'resolved') {\n return {\n title: 'Listener resolved without executions',\n description: 'No matching trigger event created a job execution before the listener stopped.',\n status: job.status,\n };\n }\n\n if (job.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'No execution has been created for this job yet.',\n status: 'pending',\n };\n }\n\n if (job.status === 'skipped') {\n return {\n title: 'This job was skipped',\n description: skippedJobDescription(job.statusReason),\n status: 'skipped',\n };\n }\n\n if (job.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before an execution was created.',\n status: 'cancelled',\n };\n }\n\n return {\n title: 'Execution details unavailable',\n description: 'This job finished, but no job execution record is available.',\n status: job.status,\n };\n}\n\nfunction CarriedOverStepPanel() {\n return (\n <EmptyState\n className=\"min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base px-16 py-20\"\n icon=\"componentLine\"\n title=\"Carried over from a previous attempt\"\n description=\"Not executed in this run.\"\n variant=\"compact\"\n />\n );\n}\n\nfunction skippedJobDescription(reason: Job['statusReason']): string {\n switch (reason) {\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job was skipped.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was skipped.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so this job was skipped.';\n case 'condition_errored':\n return 'The job condition could not be evaluated, so this job was skipped.';\n case 'user_cancelled':\n case 'run_cancelled':\n case 'timed_out':\n case 'runner_lost':\n case 'step_failed':\n case 'unknown':\n case null:\n return 'This job did not start.';\n }\n}\n"],"names":["TriggerSourceIcon","Badge","Button","EmptyState","Icon","RelativeTime","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Text","Fragment","useId","useRef","useState","getWorkflowStatusVisual","AGENT_CONFIG_ISSUES","defaultJobExecution","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","STEP_ERROR_REASONS","WORKFLOW_JOB_STATUSES","workflowRunTriggerDisplayLabel","workflowRunTriggerLabel","StepList","AgentConfigFailureCallout","JobExecutionSwitcher","formatJobExecutionTime","JobExecutionTimeText","StepAttemptLogPanel","STATUS_BADGE_LABEL_WIDTH_CH","Math","max","map","status","label","length","JobCard","workspaceSlug","job","selectedJobExecution","selectedAttemptId","onSelectedJobExecutionChange","onSelectedAttemptChange","renderExpandedStep","sourcePanelId","sourceAvailable","focusedSourceStepId","onOpenStepSource","titleId","sourceButtonRef","localSelectedAttempt","setLocalSelectedAttempt","jobExecutionId","undefined","attemptId","title","displayName","isNonDefaultExecutionSelected","id","selectedExecutionStatus","effectiveSelectedAttemptId","selectedSourceAction","selectedStepSourceAction","defaultRenderExpandedStep","step","stepId","attempt","attemptError","attemptStatus","carriedOver","CarriedOverStepPanel","StepAttemptDetailPanel","selectAttempt","section","aria-labelledby","className","div","mode","jobExecutions","as","size","bold","JobStatusBadge","variant","ref","type","aria-controls","aria-expanded","onClick","location","current","JobExecutionMetadata","execution","jobExecution","autoSelectActiveAttempt","emptyState","emptyStateForJob","showHeader","JobExecutionEmptyState","visual","badge","span","style","width","emptyStateForMissingExecution","icon","description","queueTime","runTime","executionStartedAt","startedAt","queuedAt","createdAt","items","triggerEvents","key","kind","value","time","filter","item","index","MetadataSeparator","JobExecutionTriggerMetadata","JobExecutionMetadataItem","displayStatus","triggerEvent","triggerLabel","triggerSource","source","event","triggerDisplayLabel","triggerCount","countLabel","tooltip","asChild","button","aria-label","provider","aria-hidden","jobExecutionTimeVerb","name","state","steps","selected","attempts","some","sourceLocation","selectedAttemptError","toSelectedAttemptError","error","isAgentConfigFailure","config","agentConfig","rawReason","reason","parsedReason","has","rawAgentConfigIssue","agentConfigIssue","agent_config_issue","exitCode","exit_code","resolvedReason","message","signal","category","sequence","listenerStatus","skippedJobDescription","statusReason"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-execution-switcher.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/job-execution-switcher.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"job-execution-switcher.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/job-execution-switcher.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAkC,KAAK,GAAG,EAAoB,MAAM,uBAAuB,CAAC;AAGnG,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,GAAG,CAAC;IACT,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,4BAA4B,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,oBAAoB,CAAC,EACnC,GAAG,EACH,oBAAoB,EACpB,4BAA4B,EAC5B,OAAmB,EACnB,SAAS,GACV,EAAE,yBAAyB,sCA+F3B"}
|
|
@@ -4,8 +4,10 @@ import { Icon } from '@shipfox/react-ui/icon';
|
|
|
4
4
|
import { Text } from '@shipfox/react-ui/typography';
|
|
5
5
|
import { cn } from '@shipfox/react-ui/utils';
|
|
6
6
|
import { useMemo } from 'react';
|
|
7
|
+
import { getWorkflowStatusVisual } from '#components/workflow-status/status-visuals.js';
|
|
7
8
|
import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-icon.js';
|
|
8
|
-
import {
|
|
9
|
+
import { deriveJobExecutionDisplayStatus } from '#core/workflow-run.js';
|
|
10
|
+
import { formatJobExecutionTime, JobExecutionTimeText } from './job-execution-time-text.js';
|
|
9
11
|
export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobExecutionChange, variant = 'compact', className }) {
|
|
10
12
|
const executions = useMemo(()=>[
|
|
11
13
|
...job.jobExecutions
|
|
@@ -26,10 +28,9 @@ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobE
|
|
|
26
28
|
children: [
|
|
27
29
|
/*#__PURE__*/ _jsxs(DropdownMenuTrigger, {
|
|
28
30
|
className: cn('inline-flex min-w-0 max-w-full items-center rounded-6 text-left transition-colors focus-visible:shadow-border-interactive-with-active focus-visible:outline-none', variant === 'title' ? '-mx-6 -my-4 gap-8 px-6 py-4 hover:bg-background-components-hover' : 'min-h-28 gap-6 px-8 py-4 text-sm leading-20 text-foreground-neutral-subtle hover:bg-background-components-hover', className),
|
|
29
|
-
"aria-label": `Switch job execution, currently execution ${selected.sequence}`,
|
|
31
|
+
"aria-label": `Switch job execution, currently execution ${selected.sequence}: ${selected.displayName}`,
|
|
30
32
|
children: [
|
|
31
33
|
variant === 'title' ? /*#__PURE__*/ _jsx(TitleExecutionSummary, {
|
|
32
|
-
job: job,
|
|
33
34
|
execution: selected
|
|
34
35
|
}) : /*#__PURE__*/ _jsx(ExecutionSummary, {
|
|
35
36
|
execution: selected
|
|
@@ -60,10 +61,11 @@ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobE
|
|
|
60
61
|
children: /*#__PURE__*/ _jsxs("button", {
|
|
61
62
|
type: "button",
|
|
62
63
|
"aria-current": isSelected ? 'true' : undefined,
|
|
64
|
+
"aria-label": executionAccessibleLabel(jobExecution),
|
|
63
65
|
className: "w-full text-left",
|
|
64
66
|
children: [
|
|
65
67
|
/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
66
|
-
status: jobExecution
|
|
68
|
+
status: deriveJobExecutionDisplayStatus(jobExecution),
|
|
67
69
|
size: 14,
|
|
68
70
|
tooltip: false
|
|
69
71
|
}),
|
|
@@ -76,7 +78,7 @@ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobE
|
|
|
76
78
|
}),
|
|
77
79
|
/*#__PURE__*/ _jsx("span", {
|
|
78
80
|
className: "min-w-0 truncate text-xs leading-20 text-foreground-neutral-base",
|
|
79
|
-
children:
|
|
81
|
+
children: jobExecution.displayName
|
|
80
82
|
}),
|
|
81
83
|
!isSelected && jobExecution.statusReason ? /*#__PURE__*/ _jsx("span", {
|
|
82
84
|
className: "min-w-0 flex-1 truncate text-xs leading-20 text-foreground-neutral-muted",
|
|
@@ -101,7 +103,16 @@ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobE
|
|
|
101
103
|
]
|
|
102
104
|
});
|
|
103
105
|
}
|
|
104
|
-
function
|
|
106
|
+
function executionAccessibleLabel(execution) {
|
|
107
|
+
const status = getWorkflowStatusVisual(deriveJobExecutionDisplayStatus(execution));
|
|
108
|
+
return [
|
|
109
|
+
`Execution #${execution.sequence}: ${execution.displayName}`,
|
|
110
|
+
status.label,
|
|
111
|
+
execution.statusReason ?? undefined,
|
|
112
|
+
execution.displayDuration ? `duration ${formatJobExecutionTime(execution.displayDuration)}` : undefined
|
|
113
|
+
].filter((part)=>Boolean(part)).join(', ');
|
|
114
|
+
}
|
|
115
|
+
function TitleExecutionSummary({ execution }) {
|
|
105
116
|
return /*#__PURE__*/ _jsxs("span", {
|
|
106
117
|
className: "flex min-w-0 items-center gap-8",
|
|
107
118
|
children: [
|
|
@@ -120,17 +131,18 @@ function TitleExecutionSummary({ job, execution }) {
|
|
|
120
131
|
size: "sm",
|
|
121
132
|
bold: true,
|
|
122
133
|
className: "min-w-0 truncate text-foreground-neutral-base",
|
|
123
|
-
children:
|
|
134
|
+
children: execution.displayName
|
|
124
135
|
})
|
|
125
136
|
]
|
|
126
137
|
});
|
|
127
138
|
}
|
|
128
139
|
function ExecutionSummary({ execution }) {
|
|
140
|
+
const displayStatus = deriveJobExecutionDisplayStatus(execution);
|
|
129
141
|
return /*#__PURE__*/ _jsxs("span", {
|
|
130
142
|
className: "flex min-w-0 items-center gap-6",
|
|
131
143
|
children: [
|
|
132
144
|
/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
133
|
-
status:
|
|
145
|
+
status: displayStatus,
|
|
134
146
|
size: 14,
|
|
135
147
|
tooltip: false
|
|
136
148
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-view/job-execution-switcher.tsx"],"sourcesContent":["import {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {useMemo} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport type {Job, JobExecution} from '#core/workflow-run.js';\nimport {JobExecutionTimeText} from './job-execution-time-text.js';\n\nexport interface JobExecutionSwitcherProps {\n job: Job;\n selectedJobExecution: string | null;\n onSelectedJobExecutionChange: (jobExecutionId: string) => void;\n variant?: 'compact' | 'title' | undefined;\n className?: string | undefined;\n}\n\nexport function JobExecutionSwitcher({\n job,\n selectedJobExecution,\n onSelectedJobExecutionChange,\n variant = 'compact',\n className,\n}: JobExecutionSwitcherProps) {\n const executions = useMemo(\n () => [...job.jobExecutions].sort((left, right) => right.sequence - left.sequence),\n [job.jobExecutions],\n );\n const selected =\n executions.find((jobExecution) => jobExecution.id === selectedJobExecution) ??\n executions[0] ??\n null;\n\n if (selected === null) return null;\n\n if (!job.executionCountVisible) {\n return (\n <div\n className={cn(\n 'flex min-w-0 items-center gap-6 text-sm leading-20 text-foreground-neutral-subtle',\n className,\n )}\n >\n <ExecutionSummary execution={selected} />\n </div>\n );\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n className={cn(\n 'inline-flex min-w-0 max-w-full items-center rounded-6 text-left transition-colors focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n variant === 'title'\n ? '-mx-6 -my-4 gap-8 px-6 py-4 hover:bg-background-components-hover'\n : 'min-h-28 gap-6 px-8 py-4 text-sm leading-20 text-foreground-neutral-subtle hover:bg-background-components-hover',\n className,\n )}\n aria-label={`Switch job execution, currently execution ${selected.sequence}`}\n >\n {variant === 'title' ? (\n <TitleExecutionSummary job={job} execution={selected} />\n ) : (\n <ExecutionSummary execution={selected} />\n )}\n <Icon name=\"arrowDownSLine\" className=\"size-14 shrink-0 text-foreground-neutral-muted\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" size=\"lg\" className=\"max-h-[320px] overflow-y-auto\">\n <DropdownMenuLabel>\n {executions.length} execution{executions.length === 1 ? '' : 's'}\n </DropdownMenuLabel>\n {executions.map((jobExecution) => {\n const isSelected = jobExecution.id === selected.id;\n\n return (\n <DropdownMenuItem\n key={jobExecution.id}\n asChild\n onSelect={() => onSelectedJobExecutionChange(jobExecution.id)}\n >\n <button\n type=\"button\"\n aria-current={isSelected ? 'true' : undefined}\n className=\"w-full text-left\"\n >\n <WorkflowStatusIcon status={jobExecution.status} size={14} tooltip={false} />\n <span className=\"font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n #{jobExecution.sequence}\n </span>\n <span className=\"min-w-0 truncate text-xs leading-20 text-foreground-neutral-base\">\n {job.displayName}\n </span>\n {!isSelected && jobExecution.statusReason ? (\n <span className=\"min-w-0 flex-1 truncate text-xs leading-20 text-foreground-neutral-muted\">\n {jobExecution.statusReason}\n </span>\n ) : (\n <span className=\"min-w-0 flex-1\" />\n )}\n {isSelected ? (\n <Icon name=\"check\" className=\"size-14 shrink-0 text-foreground-neutral-base\" />\n ) : null}\n <JobExecutionDuration\n execution={jobExecution}\n className=\"ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </button>\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction TitleExecutionSummary({job, execution}: {job: Job; execution: JobExecution}) {\n return (\n <span className=\"flex min-w-0 items-center gap-8\">\n <Text as=\"span\" size=\"sm\" bold className=\"shrink-0 text-foreground-neutral-base\">\n #{execution.sequence}\n </Text>\n <Text as=\"span\" size=\"sm\" bold className=\"min-w-0 truncate text-foreground-neutral-base\">\n {job.displayName}\n </Text>\n </span>\n );\n}\n\nfunction ExecutionSummary({execution}: {execution: JobExecution}) {\n return (\n <span className=\"flex min-w-0 items-center gap-6\">\n <WorkflowStatusIcon status={execution.status} size={14} tooltip={false} />\n <span className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n Execution #{execution.sequence}\n </span>\n {execution.statusReason ? (\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate text-foreground-neutral-muted\">\n · {execution.statusReason}\n </Text>\n ) : null}\n <JobExecutionDuration\n execution={execution}\n className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </span>\n );\n}\n\nfunction JobExecutionDuration({\n execution,\n className,\n}: {\n execution: JobExecution;\n className?: string | undefined;\n}) {\n const duration = execution.displayDuration;\n if (!duration) return null;\n\n return (\n <span className={className}>\n <JobExecutionTimeText time={duration} />\n </span>\n );\n}\n"],"names":["DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuLabel","DropdownMenuTrigger","Icon","Text","cn","useMemo","WorkflowStatusIcon","JobExecutionTimeText","JobExecutionSwitcher","job","selectedJobExecution","onSelectedJobExecutionChange","variant","className","executions","jobExecutions","sort","left","right","sequence","selected","find","jobExecution","id","executionCountVisible","div","ExecutionSummary","execution","aria-label","TitleExecutionSummary","name","align","size","length","map","isSelected","asChild","onSelect","button","type","aria-current","undefined","status","tooltip","span","displayName","statusReason","JobExecutionDuration","as","bold","duration","displayDuration","time"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-view/job-execution-switcher.tsx"],"sourcesContent":["import {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {useMemo} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {deriveJobExecutionDisplayStatus, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {formatJobExecutionTime, JobExecutionTimeText} from './job-execution-time-text.js';\n\nexport interface JobExecutionSwitcherProps {\n job: Job;\n selectedJobExecution: string | null;\n onSelectedJobExecutionChange: (jobExecutionId: string) => void;\n variant?: 'compact' | 'title' | undefined;\n className?: string | undefined;\n}\n\nexport function JobExecutionSwitcher({\n job,\n selectedJobExecution,\n onSelectedJobExecutionChange,\n variant = 'compact',\n className,\n}: JobExecutionSwitcherProps) {\n const executions = useMemo(\n () => [...job.jobExecutions].sort((left, right) => right.sequence - left.sequence),\n [job.jobExecutions],\n );\n const selected =\n executions.find((jobExecution) => jobExecution.id === selectedJobExecution) ??\n executions[0] ??\n null;\n\n if (selected === null) return null;\n\n if (!job.executionCountVisible) {\n return (\n <div\n className={cn(\n 'flex min-w-0 items-center gap-6 text-sm leading-20 text-foreground-neutral-subtle',\n className,\n )}\n >\n <ExecutionSummary execution={selected} />\n </div>\n );\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n className={cn(\n 'inline-flex min-w-0 max-w-full items-center rounded-6 text-left transition-colors focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n variant === 'title'\n ? '-mx-6 -my-4 gap-8 px-6 py-4 hover:bg-background-components-hover'\n : 'min-h-28 gap-6 px-8 py-4 text-sm leading-20 text-foreground-neutral-subtle hover:bg-background-components-hover',\n className,\n )}\n aria-label={`Switch job execution, currently execution ${selected.sequence}: ${selected.displayName}`}\n >\n {variant === 'title' ? (\n <TitleExecutionSummary execution={selected} />\n ) : (\n <ExecutionSummary execution={selected} />\n )}\n <Icon name=\"arrowDownSLine\" className=\"size-14 shrink-0 text-foreground-neutral-muted\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" size=\"lg\" className=\"max-h-[320px] overflow-y-auto\">\n <DropdownMenuLabel>\n {executions.length} execution{executions.length === 1 ? '' : 's'}\n </DropdownMenuLabel>\n {executions.map((jobExecution) => {\n const isSelected = jobExecution.id === selected.id;\n\n return (\n <DropdownMenuItem\n key={jobExecution.id}\n asChild\n onSelect={() => onSelectedJobExecutionChange(jobExecution.id)}\n >\n <button\n type=\"button\"\n aria-current={isSelected ? 'true' : undefined}\n aria-label={executionAccessibleLabel(jobExecution)}\n className=\"w-full text-left\"\n >\n <WorkflowStatusIcon\n status={deriveJobExecutionDisplayStatus(jobExecution)}\n size={14}\n tooltip={false}\n />\n <span className=\"font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n #{jobExecution.sequence}\n </span>\n <span className=\"min-w-0 truncate text-xs leading-20 text-foreground-neutral-base\">\n {jobExecution.displayName}\n </span>\n {!isSelected && jobExecution.statusReason ? (\n <span className=\"min-w-0 flex-1 truncate text-xs leading-20 text-foreground-neutral-muted\">\n {jobExecution.statusReason}\n </span>\n ) : (\n <span className=\"min-w-0 flex-1\" />\n )}\n {isSelected ? (\n <Icon name=\"check\" className=\"size-14 shrink-0 text-foreground-neutral-base\" />\n ) : null}\n <JobExecutionDuration\n execution={jobExecution}\n className=\"ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </button>\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction executionAccessibleLabel(execution: JobExecution): string {\n const status = getWorkflowStatusVisual(deriveJobExecutionDisplayStatus(execution));\n return [\n `Execution #${execution.sequence}: ${execution.displayName}`,\n status.label,\n execution.statusReason ?? undefined,\n execution.displayDuration\n ? `duration ${formatJobExecutionTime(execution.displayDuration)}`\n : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ');\n}\n\nfunction TitleExecutionSummary({execution}: {execution: JobExecution}) {\n return (\n <span className=\"flex min-w-0 items-center gap-8\">\n <Text as=\"span\" size=\"sm\" bold className=\"shrink-0 text-foreground-neutral-base\">\n #{execution.sequence}\n </Text>\n <Text as=\"span\" size=\"sm\" bold className=\"min-w-0 truncate text-foreground-neutral-base\">\n {execution.displayName}\n </Text>\n </span>\n );\n}\n\nfunction ExecutionSummary({execution}: {execution: JobExecution}) {\n const displayStatus = deriveJobExecutionDisplayStatus(execution);\n\n return (\n <span className=\"flex min-w-0 items-center gap-6\">\n <WorkflowStatusIcon status={displayStatus} size={14} tooltip={false} />\n <span className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n Execution #{execution.sequence}\n </span>\n {execution.statusReason ? (\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate text-foreground-neutral-muted\">\n · {execution.statusReason}\n </Text>\n ) : null}\n <JobExecutionDuration\n execution={execution}\n className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </span>\n );\n}\n\nfunction JobExecutionDuration({\n execution,\n className,\n}: {\n execution: JobExecution;\n className?: string | undefined;\n}) {\n const duration = execution.displayDuration;\n if (!duration) return null;\n\n return (\n <span className={className}>\n <JobExecutionTimeText time={duration} />\n </span>\n );\n}\n"],"names":["DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuLabel","DropdownMenuTrigger","Icon","Text","cn","useMemo","getWorkflowStatusVisual","WorkflowStatusIcon","deriveJobExecutionDisplayStatus","formatJobExecutionTime","JobExecutionTimeText","JobExecutionSwitcher","job","selectedJobExecution","onSelectedJobExecutionChange","variant","className","executions","jobExecutions","sort","left","right","sequence","selected","find","jobExecution","id","executionCountVisible","div","ExecutionSummary","execution","aria-label","displayName","TitleExecutionSummary","name","align","size","length","map","isSelected","asChild","onSelect","button","type","aria-current","undefined","executionAccessibleLabel","status","tooltip","span","statusReason","JobExecutionDuration","label","displayDuration","filter","part","Boolean","join","as","bold","displayStatus","duration","time"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type WorkflowRunSelectionInput } from '#core/workflow-run-url-state.js';
|
|
2
2
|
export interface WorkflowRunViewProps {
|
|
3
|
-
workspaceId: string;
|
|
4
3
|
projectId: string;
|
|
4
|
+
workspaceSlug?: string | undefined;
|
|
5
|
+
projectSlug?: string | undefined;
|
|
5
6
|
workflowRunId?: string | undefined;
|
|
6
7
|
selection?: WorkflowRunSelectionInput | undefined;
|
|
7
8
|
onSelectionChange?: ((selection: WorkflowRunSelectionInput) => void) | undefined;
|
|
@@ -11,5 +12,5 @@ export interface WorkflowRunViewProps {
|
|
|
11
12
|
* resolving which run to show) or the run is loading, so the page never branches on the
|
|
12
13
|
* loading state itself.
|
|
13
14
|
*/
|
|
14
|
-
export declare function WorkflowRunView({
|
|
15
|
+
export declare function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, selection, onSelectionChange, }: WorkflowRunViewProps): import("react").JSX.Element;
|
|
15
16
|
//# sourceMappingURL=workflow-run-view.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-run-view.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,yBAAyB,EAE/B,MAAM,iCAAiC,CAAC;AAsBzC,MAAM,WAAW,oBAAoB;IACnC,
|
|
1
|
+
{"version":3,"file":"workflow-run-view.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,yBAAyB,EAE/B,MAAM,iCAAiC,CAAC;AAsBzC,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,SAAS,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAClD,iBAAiB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,yBAAyB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAClF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,aAAa,EACb,WAAW,EACX,aAAa,EACb,SAAS,EACT,iBAAiB,GAClB,EAAE,oBAAoB,+BAkBtB"}
|
|
@@ -18,7 +18,7 @@ import { WorkflowRunNotFound, WorkflowRunSkeleton, WorkflowRunStaleError } from
|
|
|
18
18
|
* Renders the run for `workflowRunId`, or a skeleton while `workflowRunId` is still unknown (the page is
|
|
19
19
|
* resolving which run to show) or the run is loading, so the page never branches on the
|
|
20
20
|
* loading state itself.
|
|
21
|
-
*/ export function WorkflowRunView({
|
|
21
|
+
*/ export function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, selection, onSelectionChange }) {
|
|
22
22
|
const runQuery = useWorkflowRunAttemptQuery({
|
|
23
23
|
workflowRunId,
|
|
24
24
|
runAttempt: selection?.runAttempt
|
|
@@ -28,8 +28,8 @@ import { WorkflowRunNotFound, WorkflowRunSkeleton, WorkflowRunStaleError } from
|
|
|
28
28
|
children: /*#__PURE__*/ _jsx("div", {
|
|
29
29
|
className: "flex min-h-0 min-w-0 flex-1 overflow-hidden",
|
|
30
30
|
children: /*#__PURE__*/ _jsx(RunViewContent, {
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
workspaceSlug: workspaceSlug,
|
|
32
|
+
projectSlug: projectSlug,
|
|
33
33
|
query: runQuery,
|
|
34
34
|
rerunMutation: rerunMutation,
|
|
35
35
|
selection: selection,
|
|
@@ -38,7 +38,7 @@ import { WorkflowRunNotFound, WorkflowRunSkeleton, WorkflowRunStaleError } from
|
|
|
38
38
|
})
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
-
function RunViewContent({
|
|
41
|
+
function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, selection, onSelectionChange }) {
|
|
42
42
|
const navigate = useNavigate();
|
|
43
43
|
const [selectedJobId, setSelectedJobId] = useState();
|
|
44
44
|
const [selectedJobExecutionId, setSelectedJobExecutionId] = useState();
|
|
@@ -101,6 +101,10 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
|
|
|
101
101
|
const highlightedLineRange = sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;
|
|
102
102
|
const sourceSnapshot = runData.sourceSnapshot;
|
|
103
103
|
async function rerun(mode) {
|
|
104
|
+
if (!workspaceSlug || !projectSlug) {
|
|
105
|
+
toast.error('Could not start re-run from this route.');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
104
108
|
try {
|
|
105
109
|
const run = await rerunMutation.mutateAsync({
|
|
106
110
|
workflowRunId: runData.id,
|
|
@@ -108,10 +112,10 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
|
|
|
108
112
|
});
|
|
109
113
|
toast.success('Re-run started');
|
|
110
114
|
await navigate({
|
|
111
|
-
to: '/
|
|
115
|
+
to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
|
|
112
116
|
params: {
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
workspaceSlug,
|
|
118
|
+
projectSlug,
|
|
115
119
|
workflowRunId: run.id
|
|
116
120
|
},
|
|
117
121
|
search: (previous)=>withoutWorkflowRunSelectionSearch(previous)
|
|
@@ -210,8 +214,8 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
|
|
|
210
214
|
className: "flex min-w-0 flex-1 flex-col",
|
|
211
215
|
children: [
|
|
212
216
|
/*#__PURE__*/ _jsx(WorkflowRunSummary, {
|
|
213
|
-
|
|
214
|
-
|
|
217
|
+
workspaceSlug: workspaceSlug,
|
|
218
|
+
projectSlug: projectSlug,
|
|
215
219
|
run: runData,
|
|
216
220
|
sourceAvailable: sourceAvailable,
|
|
217
221
|
sourceOpen: sourcePanelOpen && sourceFocus === null,
|
|
@@ -230,7 +234,7 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
|
|
|
230
234
|
/*#__PURE__*/ _jsx("div", {
|
|
231
235
|
className: "min-h-0 flex-1 overflow-auto bg-background-neutral-base p-16",
|
|
232
236
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
233
|
-
className: "mx-auto flex w-full max-w-[
|
|
237
|
+
className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16",
|
|
234
238
|
children: [
|
|
235
239
|
/*#__PURE__*/ _jsx(JobGraph, {
|
|
236
240
|
run: runData,
|
|
@@ -238,7 +242,7 @@ function RunViewContent({ workspaceId, projectId, query, rerunMutation, selectio
|
|
|
238
242
|
onSelectedJobChange: selectJob
|
|
239
243
|
}),
|
|
240
244
|
selectedJob ? /*#__PURE__*/ _jsx(JobCard, {
|
|
241
|
-
|
|
245
|
+
workspaceSlug: workspaceSlug,
|
|
242
246
|
job: selectedJob,
|
|
243
247
|
selectedJobExecution: selectedJobExecution,
|
|
244
248
|
selectedAttemptId: selectedJob.carriedOver ? undefined : selectedAttemptId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useEffect, useId, useRef, useState} from 'react';\nimport {\n type JobExecution,\n resolveJobExecution,\n type StepSourceLocation,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport {\n type WorkflowRunSelectionInput,\n withoutWorkflowRunSelectionSearch,\n} from '#core/workflow-run-url-state.js';\nimport {\n useCancelWorkflowRunMutation,\n useRerunWorkflowRunMutation,\n useWorkflowRunAttemptQuery,\n} from '#hooks/api/workflow-runs.js';\nimport {JobGraph} from '../job-graph/index.js';\nimport {WorkflowRunSummary} from '../workflow-run-summary/index.js';\nimport {WorkflowSourcePanel} from '../workflow-source-panel/index.js';\nimport {JobCard} from './job-card.js';\nimport {resolveWorkflowRunSelection} from './workflow-run-selection.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunSkeleton,\n WorkflowRunStaleError,\n} from './workflow-run-states.js';\n\ninterface WorkflowSourceFocus {\n stepId: string;\n location: StepSourceLocation;\n}\n\nexport interface WorkflowRunViewProps {\n workspaceId: string;\n projectId: string;\n workflowRunId?: string | undefined;\n selection?: WorkflowRunSelectionInput | undefined;\n onSelectionChange?: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n}\n\n/**\n * Renders the run for `workflowRunId`, or a skeleton while `workflowRunId` is still unknown (the page is\n * resolving which run to show) or the run is loading, so the page never branches on the\n * loading state itself.\n */\nexport function WorkflowRunView({\n workspaceId,\n projectId,\n workflowRunId,\n selection,\n onSelectionChange,\n}: WorkflowRunViewProps) {\n const runQuery = useWorkflowRunAttemptQuery({workflowRunId, runAttempt: selection?.runAttempt});\n const rerunMutation = useRerunWorkflowRunMutation(projectId);\n\n return (\n <RelativeTimeProvider>\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <RunViewContent\n workspaceId={workspaceId}\n projectId={projectId}\n query={runQuery}\n rerunMutation={rerunMutation}\n selection={selection}\n onSelectionChange={onSelectionChange}\n />\n </div>\n </RelativeTimeProvider>\n );\n}\n\nfunction RunViewContent({\n workspaceId,\n projectId,\n query,\n rerunMutation,\n selection,\n onSelectionChange,\n}: {\n workspaceId: string;\n projectId: string;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;\n selection: WorkflowRunSelectionInput | undefined;\n onSelectionChange: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n}) {\n const navigate = useNavigate();\n const [selectedJobId, setSelectedJobId] = useState<string | undefined>();\n const [selectedJobExecutionId, setSelectedJobExecutionId] = useState<string | undefined>();\n const [sourcePanelOpen, setSourcePanelOpen] = useState(false);\n const [sourceFocus, setSourceFocus] = useState<WorkflowSourceFocus | null>(null);\n const sourcePanelId = useId();\n const sourceButtonRef = useRef<HTMLButtonElement>(null);\n // The button that last opened the panel (summary or a step detail), so Escape /\n // Close returns focus to whoever opened it.\n const lastSourceTriggerRef = useRef<HTMLButtonElement | null>(null);\n const selectionControlled = selection !== undefined;\n const sourceAvailable =\n query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;\n const cancelMutation = useCancelWorkflowRunMutation(query.data);\n\n useEffect(() => {\n if (!sourceAvailable) {\n setSourcePanelOpen(false);\n setSourceFocus(null);\n }\n }, [sourceAvailable]);\n\n // If a refetch drops the focused step or its location, degrade to whole-workflow\n // focus so the panel never points at an unmounted Source button.\n useEffect(() => {\n if (!sourceFocus) return;\n const stillLocated = query.data?.jobs.some((job) =>\n job.jobExecutions.some((execution) =>\n execution.steps.some((step) => step.id === sourceFocus.stepId && step.sourceLocation),\n ),\n );\n if (!stillLocated) {\n setSourceFocus(null);\n lastSourceTriggerRef.current = sourceButtonRef.current;\n }\n }, [sourceFocus, query.data]);\n\n if (query.isPending) return <WorkflowRunSkeleton />;\n\n // Only show the full error placeholder when nothing ever loaded. A refetch that fails after\n // a prior success keeps the loaded run on screen (see below) instead of wiping the pane,\n // since active-run polling can hit a transient API error.\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 <WorkflowRunSkeleton />;\n\n const runData = query.data;\n const resolvedSelection = selectionControlled\n ? resolveWorkflowRunSelection({run: runData, selection})\n : undefined;\n const selectedJob = selectionControlled\n ? resolvedSelection?.job\n : (runData.jobs.find((job) => job.id === selectedJobId) ?? runData.jobs.at(0));\n const selectedJobExecution = selectionControlled\n ? resolvedSelection?.jobExecution\n : selectedJob\n ? resolveJobExecution(selectedJob, selectedJobExecutionId)\n : undefined;\n const selectedAttemptId = selectionControlled\n ? (resolvedSelection?.selectedAttemptId ?? null)\n : undefined;\n // Explicit per-step focus wins; fall back to the URL-selected step so deep links\n // still pre-highlight when the summary opens the whole-workflow source.\n const highlightedLineRange =\n sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;\n const sourceSnapshot = runData.sourceSnapshot;\n async function rerun(mode: WorkflowRunRerunMode) {\n try {\n const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});\n toast.success('Re-run started');\n await navigate({\n to: '/workspaces/$wid/projects/$pid/runs/$workflowRunId',\n params: {wid: workspaceId, pid: projectId, workflowRunId: run.id},\n search: ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never,\n });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');\n }\n }\n\n function selectJob(jobId: string | undefined) {\n if (!selectionControlled) {\n setSelectedJobId(jobId);\n setSelectedJobExecutionId(undefined);\n return;\n }\n\n onSelectionChange?.(\n jobId ? {jobId, runAttempt: selection?.runAttempt} : {runAttempt: selection?.runAttempt},\n );\n }\n\n function selectJobExecution(jobExecutionId: string | undefined) {\n if (!selectionControlled) {\n setSelectedJobExecutionId(jobExecutionId);\n return;\n }\n if (!selectedJob) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectionControlled || !selectedJob || !selectedJobExecution) return;\n\n if (!attemptId) {\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n runAttempt: selection?.runAttempt,\n });\n return;\n }\n\n const match = findAttemptSelection(selectedJobExecution, attemptId);\n if (!match) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.stepId,\n stepAttemptId: match.attemptId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function openWholeWorkflowSource() {\n setSourceFocus(null);\n lastSourceTriggerRef.current = sourceButtonRef.current;\n setSourcePanelOpen(true);\n }\n\n function toggleWholeWorkflowSource() {\n if (sourcePanelOpen && sourceFocus === null) {\n closeSourcePanel();\n return;\n }\n openWholeWorkflowSource();\n }\n\n function openStepSource(\n stepId: string,\n location: StepSourceLocation,\n trigger: HTMLButtonElement | null,\n ) {\n setSourceFocus({stepId, location});\n lastSourceTriggerRef.current = trigger;\n setSourcePanelOpen(true);\n }\n\n function closeSourcePanel() {\n const trigger = lastSourceTriggerRef.current;\n const fallbackTrigger = sourceButtonRef.current;\n setSourcePanelOpen(false);\n // Defer so focus lands after the panel unmounts; clear the focus only after\n // focusing so the opener button is still expanded (force-visible) on return.\n window.setTimeout(() => {\n const focusTarget = trigger?.isConnected ? trigger : fallbackTrigger;\n focusTarget?.focus();\n setSourceFocus(null);\n }, 0);\n }\n\n function cancelRun() {\n cancelMutation.mutate(undefined, {\n onError: (error) => {\n toast.error(cancelErrorMessage(error));\n },\n });\n }\n\n return (\n <>\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <WorkflowRunSummary\n workspaceId={workspaceId}\n projectId={projectId}\n run={runData}\n sourceAvailable={sourceAvailable}\n sourceOpen={sourcePanelOpen && sourceFocus === null}\n sourcePanelId={sourcePanelId}\n sourceButtonRef={sourceButtonRef}\n onSourceToggle={toggleWholeWorkflowSource}\n cancelling={cancelMutation.isPending}\n onCancel={cancelRun}\n rerunPending={rerunMutation.isPending}\n onRerun={(mode) => void rerun(mode)}\n latestAttempt={runData.latestAttempt}\n />\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n <div className=\"min-h-0 flex-1 overflow-auto bg-background-neutral-base p-16\">\n <div className=\"mx-auto flex w-full max-w-[1280px] flex-col gap-16\">\n <JobGraph\n run={runData}\n selectedJobId={selectedJob?.id}\n onSelectedJobChange={selectJob}\n />\n {selectedJob ? (\n <JobCard\n workspaceId={workspaceId}\n job={selectedJob}\n selectedJobExecution={selectedJobExecution}\n selectedAttemptId={selectedJob.carriedOver ? undefined : selectedAttemptId}\n onSelectedJobExecutionChange={selectJobExecution}\n onSelectedAttemptChange={selectionControlled ? selectAttempt : undefined}\n sourcePanelId={sourcePanelId}\n sourceAvailable={sourceAvailable}\n focusedSourceStepId={sourceFocus?.stepId ?? null}\n onOpenStepSource={openStepSource}\n />\n ) : null}\n </div>\n </div>\n </div>\n <WorkflowSourcePanel\n id={sourcePanelId}\n source={sourceSnapshot}\n open={sourcePanelOpen && sourceAvailable}\n onClose={closeSourcePanel}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n </>\n );\n}\n\nfunction cancelErrorMessage(error: unknown): string {\n if (error instanceof ApiError && error.code === 'run-already-finished') {\n return 'This workflow run has already finished.';\n }\n return 'Could not cancel workflow run.';\n}\n\nfunction findAttemptSelection(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 {stepId: step.id, attemptId: attempt.id};\n }\n return undefined;\n}\n"],"names":["ApiError","QueryLoadError","RelativeTimeProvider","toast","useNavigate","useEffect","useId","useRef","useState","resolveJobExecution","withoutWorkflowRunSelectionSearch","useCancelWorkflowRunMutation","useRerunWorkflowRunMutation","useWorkflowRunAttemptQuery","JobGraph","WorkflowRunSummary","WorkflowSourcePanel","JobCard","resolveWorkflowRunSelection","WorkflowRunNotFound","WorkflowRunSkeleton","WorkflowRunStaleError","WorkflowRunView","workspaceId","projectId","workflowRunId","selection","onSelectionChange","runQuery","runAttempt","rerunMutation","div","className","RunViewContent","query","navigate","selectedJobId","setSelectedJobId","selectedJobExecutionId","setSelectedJobExecutionId","sourcePanelOpen","setSourcePanelOpen","sourceFocus","setSourceFocus","sourcePanelId","sourceButtonRef","lastSourceTriggerRef","selectionControlled","undefined","sourceAvailable","data","sourceSnapshot","cancelMutation","stillLocated","jobs","some","job","jobExecutions","execution","steps","step","id","stepId","sourceLocation","current","isPending","isError","error","status","subject","icon","runData","resolvedSelection","run","selectedJob","find","at","selectedJobExecution","jobExecution","selectedAttemptId","highlightedLineRange","location","rerun","mode","mutateAsync","success","to","params","wid","pid","search","previous","message","selectJob","jobId","selectJobExecution","jobExecutionId","selectAttempt","attemptId","match","findAttemptSelection","stepAttemptId","openWholeWorkflowSource","toggleWholeWorkflowSource","closeSourcePanel","openStepSource","trigger","fallbackTrigger","window","setTimeout","focusTarget","isConnected","focus","cancelRun","mutate","onError","cancelErrorMessage","sourceOpen","onSourceToggle","cancelling","onCancel","rerunPending","onRerun","latestAttempt","onSelectedJobChange","carriedOver","onSelectedJobExecutionChange","onSelectedAttemptChange","focusedSourceStepId","onOpenStepSource","source","open","onClose","scrollHighlightedIntoView","code","attempt","attempts","candidate"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useEffect, useId, useRef, useState} from 'react';\nimport {\n type JobExecution,\n resolveJobExecution,\n type StepSourceLocation,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport {\n type WorkflowRunSelectionInput,\n withoutWorkflowRunSelectionSearch,\n} from '#core/workflow-run-url-state.js';\nimport {\n useCancelWorkflowRunMutation,\n useRerunWorkflowRunMutation,\n useWorkflowRunAttemptQuery,\n} from '#hooks/api/workflow-runs.js';\nimport {JobGraph} from '../job-graph/index.js';\nimport {WorkflowRunSummary} from '../workflow-run-summary/index.js';\nimport {WorkflowSourcePanel} from '../workflow-source-panel/index.js';\nimport {JobCard} from './job-card.js';\nimport {resolveWorkflowRunSelection} from './workflow-run-selection.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunSkeleton,\n WorkflowRunStaleError,\n} from './workflow-run-states.js';\n\ninterface WorkflowSourceFocus {\n stepId: string;\n location: StepSourceLocation;\n}\n\nexport interface WorkflowRunViewProps {\n projectId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n selection?: WorkflowRunSelectionInput | undefined;\n onSelectionChange?: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n}\n\n/**\n * Renders the run for `workflowRunId`, or a skeleton while `workflowRunId` is still unknown (the page is\n * resolving which run to show) or the run is loading, so the page never branches on the\n * loading state itself.\n */\nexport function WorkflowRunView({\n projectId,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n selection,\n onSelectionChange,\n}: WorkflowRunViewProps) {\n const runQuery = useWorkflowRunAttemptQuery({workflowRunId, runAttempt: selection?.runAttempt});\n const rerunMutation = useRerunWorkflowRunMutation(projectId);\n\n return (\n <RelativeTimeProvider>\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <RunViewContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n query={runQuery}\n rerunMutation={rerunMutation}\n selection={selection}\n onSelectionChange={onSelectionChange}\n />\n </div>\n </RelativeTimeProvider>\n );\n}\n\nfunction RunViewContent({\n workspaceSlug,\n projectSlug,\n query,\n rerunMutation,\n selection,\n onSelectionChange,\n}: {\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;\n selection: WorkflowRunSelectionInput | undefined;\n onSelectionChange: ((selection: WorkflowRunSelectionInput) => void) | undefined;\n}) {\n const navigate = useNavigate();\n const [selectedJobId, setSelectedJobId] = useState<string | undefined>();\n const [selectedJobExecutionId, setSelectedJobExecutionId] = useState<string | undefined>();\n const [sourcePanelOpen, setSourcePanelOpen] = useState(false);\n const [sourceFocus, setSourceFocus] = useState<WorkflowSourceFocus | null>(null);\n const sourcePanelId = useId();\n const sourceButtonRef = useRef<HTMLButtonElement>(null);\n // The button that last opened the panel (summary or a step detail), so Escape /\n // Close returns focus to whoever opened it.\n const lastSourceTriggerRef = useRef<HTMLButtonElement | null>(null);\n const selectionControlled = selection !== undefined;\n const sourceAvailable =\n query.data?.sourceSnapshot !== null && query.data?.sourceSnapshot !== undefined;\n const cancelMutation = useCancelWorkflowRunMutation(query.data);\n\n useEffect(() => {\n if (!sourceAvailable) {\n setSourcePanelOpen(false);\n setSourceFocus(null);\n }\n }, [sourceAvailable]);\n\n // If a refetch drops the focused step or its location, degrade to whole-workflow\n // focus so the panel never points at an unmounted Source button.\n useEffect(() => {\n if (!sourceFocus) return;\n const stillLocated = query.data?.jobs.some((job) =>\n job.jobExecutions.some((execution) =>\n execution.steps.some((step) => step.id === sourceFocus.stepId && step.sourceLocation),\n ),\n );\n if (!stillLocated) {\n setSourceFocus(null);\n lastSourceTriggerRef.current = sourceButtonRef.current;\n }\n }, [sourceFocus, query.data]);\n\n if (query.isPending) return <WorkflowRunSkeleton />;\n\n // Only show the full error placeholder when nothing ever loaded. A refetch that fails after\n // a prior success keeps the loaded run on screen (see below) instead of wiping the pane,\n // since active-run polling can hit a transient API error.\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 <WorkflowRunSkeleton />;\n\n const runData = query.data;\n const resolvedSelection = selectionControlled\n ? resolveWorkflowRunSelection({run: runData, selection})\n : undefined;\n const selectedJob = selectionControlled\n ? resolvedSelection?.job\n : (runData.jobs.find((job) => job.id === selectedJobId) ?? runData.jobs.at(0));\n const selectedJobExecution = selectionControlled\n ? resolvedSelection?.jobExecution\n : selectedJob\n ? resolveJobExecution(selectedJob, selectedJobExecutionId)\n : undefined;\n const selectedAttemptId = selectionControlled\n ? (resolvedSelection?.selectedAttemptId ?? null)\n : undefined;\n // Explicit per-step focus wins; fall back to the URL-selected step so deep links\n // still pre-highlight when the summary opens the whole-workflow source.\n const highlightedLineRange =\n sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;\n const sourceSnapshot = runData.sourceSnapshot;\n async function rerun(mode: WorkflowRunRerunMode) {\n if (!workspaceSlug || !projectSlug) {\n toast.error('Could not start re-run from this route.');\n return;\n }\n try {\n const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});\n toast.success('Re-run started');\n await navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {\n workspaceSlug,\n projectSlug,\n workflowRunId: run.id,\n },\n search: ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never,\n });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');\n }\n }\n\n function selectJob(jobId: string | undefined) {\n if (!selectionControlled) {\n setSelectedJobId(jobId);\n setSelectedJobExecutionId(undefined);\n return;\n }\n\n onSelectionChange?.(\n jobId ? {jobId, runAttempt: selection?.runAttempt} : {runAttempt: selection?.runAttempt},\n );\n }\n\n function selectJobExecution(jobExecutionId: string | undefined) {\n if (!selectionControlled) {\n setSelectedJobExecutionId(jobExecutionId);\n return;\n }\n if (!selectedJob) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectionControlled || !selectedJob || !selectedJobExecution) return;\n\n if (!attemptId) {\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n runAttempt: selection?.runAttempt,\n });\n return;\n }\n\n const match = findAttemptSelection(selectedJobExecution, attemptId);\n if (!match) return;\n\n onSelectionChange?.({\n jobId: selectedJob.id,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.stepId,\n stepAttemptId: match.attemptId,\n runAttempt: selection?.runAttempt,\n });\n }\n\n function openWholeWorkflowSource() {\n setSourceFocus(null);\n lastSourceTriggerRef.current = sourceButtonRef.current;\n setSourcePanelOpen(true);\n }\n\n function toggleWholeWorkflowSource() {\n if (sourcePanelOpen && sourceFocus === null) {\n closeSourcePanel();\n return;\n }\n openWholeWorkflowSource();\n }\n\n function openStepSource(\n stepId: string,\n location: StepSourceLocation,\n trigger: HTMLButtonElement | null,\n ) {\n setSourceFocus({stepId, location});\n lastSourceTriggerRef.current = trigger;\n setSourcePanelOpen(true);\n }\n\n function closeSourcePanel() {\n const trigger = lastSourceTriggerRef.current;\n const fallbackTrigger = sourceButtonRef.current;\n setSourcePanelOpen(false);\n // Defer so focus lands after the panel unmounts; clear the focus only after\n // focusing so the opener button is still expanded (force-visible) on return.\n window.setTimeout(() => {\n const focusTarget = trigger?.isConnected ? trigger : fallbackTrigger;\n focusTarget?.focus();\n setSourceFocus(null);\n }, 0);\n }\n\n function cancelRun() {\n cancelMutation.mutate(undefined, {\n onError: (error) => {\n toast.error(cancelErrorMessage(error));\n },\n });\n }\n\n return (\n <>\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <WorkflowRunSummary\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n sourceAvailable={sourceAvailable}\n sourceOpen={sourcePanelOpen && sourceFocus === null}\n sourcePanelId={sourcePanelId}\n sourceButtonRef={sourceButtonRef}\n onSourceToggle={toggleWholeWorkflowSource}\n cancelling={cancelMutation.isPending}\n onCancel={cancelRun}\n rerunPending={rerunMutation.isPending}\n onRerun={(mode) => void rerun(mode)}\n latestAttempt={runData.latestAttempt}\n />\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n <div className=\"min-h-0 flex-1 overflow-auto bg-background-neutral-base p-16\">\n <div className=\"mx-auto flex w-full max-w-[1120px] flex-col gap-16\">\n <JobGraph\n run={runData}\n selectedJobId={selectedJob?.id}\n onSelectedJobChange={selectJob}\n />\n {selectedJob ? (\n <JobCard\n workspaceSlug={workspaceSlug}\n job={selectedJob}\n selectedJobExecution={selectedJobExecution}\n selectedAttemptId={selectedJob.carriedOver ? undefined : selectedAttemptId}\n onSelectedJobExecutionChange={selectJobExecution}\n onSelectedAttemptChange={selectionControlled ? selectAttempt : undefined}\n sourcePanelId={sourcePanelId}\n sourceAvailable={sourceAvailable}\n focusedSourceStepId={sourceFocus?.stepId ?? null}\n onOpenStepSource={openStepSource}\n />\n ) : null}\n </div>\n </div>\n </div>\n <WorkflowSourcePanel\n id={sourcePanelId}\n source={sourceSnapshot}\n open={sourcePanelOpen && sourceAvailable}\n onClose={closeSourcePanel}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n </>\n );\n}\n\nfunction cancelErrorMessage(error: unknown): string {\n if (error instanceof ApiError && error.code === 'run-already-finished') {\n return 'This workflow run has already finished.';\n }\n return 'Could not cancel workflow run.';\n}\n\nfunction findAttemptSelection(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 {stepId: step.id, attemptId: attempt.id};\n }\n return undefined;\n}\n"],"names":["ApiError","QueryLoadError","RelativeTimeProvider","toast","useNavigate","useEffect","useId","useRef","useState","resolveJobExecution","withoutWorkflowRunSelectionSearch","useCancelWorkflowRunMutation","useRerunWorkflowRunMutation","useWorkflowRunAttemptQuery","JobGraph","WorkflowRunSummary","WorkflowSourcePanel","JobCard","resolveWorkflowRunSelection","WorkflowRunNotFound","WorkflowRunSkeleton","WorkflowRunStaleError","WorkflowRunView","projectId","workspaceSlug","projectSlug","workflowRunId","selection","onSelectionChange","runQuery","runAttempt","rerunMutation","div","className","RunViewContent","query","navigate","selectedJobId","setSelectedJobId","selectedJobExecutionId","setSelectedJobExecutionId","sourcePanelOpen","setSourcePanelOpen","sourceFocus","setSourceFocus","sourcePanelId","sourceButtonRef","lastSourceTriggerRef","selectionControlled","undefined","sourceAvailable","data","sourceSnapshot","cancelMutation","stillLocated","jobs","some","job","jobExecutions","execution","steps","step","id","stepId","sourceLocation","current","isPending","isError","error","status","subject","icon","runData","resolvedSelection","run","selectedJob","find","at","selectedJobExecution","jobExecution","selectedAttemptId","highlightedLineRange","location","rerun","mode","mutateAsync","success","to","params","search","previous","message","selectJob","jobId","selectJobExecution","jobExecutionId","selectAttempt","attemptId","match","findAttemptSelection","stepAttemptId","openWholeWorkflowSource","toggleWholeWorkflowSource","closeSourcePanel","openStepSource","trigger","fallbackTrigger","window","setTimeout","focusTarget","isConnected","focus","cancelRun","mutate","onError","cancelErrorMessage","sourceOpen","onSourceToggle","cancelling","onCancel","rerunPending","onRerun","latestAttempt","onSelectedJobChange","carriedOver","onSelectedJobExecutionChange","onSelectedAttemptChange","focusedSourceStepId","onOpenStepSource","source","open","onClose","scrollHighlightedIntoView","code","attempt","attempts","candidate"],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { BadgeVariant } from '@shipfox/react-ui/badge';
|
|
2
2
|
import type { DotVariant } from '@shipfox/react-ui/dot';
|
|
3
|
-
import type {
|
|
4
|
-
export type {
|
|
3
|
+
import type { WorkflowDisplayStatus } from '#core/workflow-run.js';
|
|
4
|
+
export type { WorkflowDisplayStatus };
|
|
5
5
|
export interface WorkflowStatusVisual {
|
|
6
|
-
kind:
|
|
6
|
+
kind: WorkflowDisplayStatus;
|
|
7
7
|
label: string;
|
|
8
8
|
dot: DotVariant;
|
|
9
9
|
badge: BadgeVariant;
|
|
10
10
|
}
|
|
11
|
-
export declare function getWorkflowStatusVisual(status:
|
|
11
|
+
export declare function getWorkflowStatusVisual(status: WorkflowDisplayStatus): WorkflowStatusVisual;
|
|
12
12
|
//# sourceMappingURL=status-visuals.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status-visuals.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-status/status-visuals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAC,
|
|
1
|
+
{"version":3,"file":"status-visuals.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-status/status-visuals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,uBAAuB,CAAC;AAEjE,YAAY,EAAC,qBAAqB,EAAC,CAAC;AAEpC,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;CACrB;AAMD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,qBAAqB,GAAG,oBAAoB,CAoB3F"}
|
|
@@ -18,6 +18,13 @@ export function getWorkflowStatusVisual(status) {
|
|
|
18
18
|
dot: 'info',
|
|
19
19
|
badge: 'info'
|
|
20
20
|
};
|
|
21
|
+
case 'listening':
|
|
22
|
+
return {
|
|
23
|
+
kind: 'listening',
|
|
24
|
+
label: 'Listening',
|
|
25
|
+
dot: 'info',
|
|
26
|
+
badge: 'info'
|
|
27
|
+
};
|
|
21
28
|
case 'succeeded':
|
|
22
29
|
return {
|
|
23
30
|
kind: 'succeeded',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-status/status-visuals.ts"],"sourcesContent":["import type {BadgeVariant} from '@shipfox/react-ui/badge';\nimport type {DotVariant} from '@shipfox/react-ui/dot';\nimport type {
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-status/status-visuals.ts"],"sourcesContent":["import type {BadgeVariant} from '@shipfox/react-ui/badge';\nimport type {DotVariant} from '@shipfox/react-ui/dot';\nimport type {WorkflowDisplayStatus} from '#core/workflow-run.js';\n\nexport type {WorkflowDisplayStatus};\n\nexport interface WorkflowStatusVisual {\n kind: WorkflowDisplayStatus;\n label: string;\n dot: DotVariant;\n badge: BadgeVariant;\n}\n\n// The status -> visual mapping shared by the run-header pill (color + label) and\n// WorkflowStatusIcon (which renders the glyph per kind). The exhaustive switch turns any new\n// status the API grows into (DESIGN.md section 9) into a compile error; reserve the `warning`\n// tone for the queued/awaiting-* states when they land.\nexport function getWorkflowStatusVisual(status: WorkflowDisplayStatus): WorkflowStatusVisual {\n switch (status) {\n case 'pending':\n return {kind: 'pending', label: 'Pending', dot: 'neutral', badge: 'neutral'};\n case 'running':\n return {kind: 'running', label: 'Running', dot: 'info', badge: 'info'};\n case 'listening':\n return {kind: 'listening', label: 'Listening', dot: 'info', badge: 'info'};\n case 'succeeded':\n return {kind: 'succeeded', label: 'Succeeded', dot: 'success', badge: 'success'};\n case 'failed':\n return {kind: 'failed', label: 'Failed', dot: 'error', badge: 'error'};\n case 'cancelled':\n return {kind: 'cancelled', label: 'Cancelled', dot: 'neutral', badge: 'neutral'};\n case 'skipped':\n return {kind: 'skipped', label: 'Skipped', dot: 'neutral', badge: 'neutral'};\n }\n\n const exhaustive: never = status;\n return exhaustive;\n}\n"],"names":["getWorkflowStatusVisual","status","kind","label","dot","badge","exhaustive"],"mappings":"AAaA,iFAAiF;AACjF,6FAA6F;AAC7F,8FAA8F;AAC9F,wDAAwD;AACxD,OAAO,SAASA,wBAAwBC,MAA6B;IACnE,OAAQA;QACN,KAAK;YACH,OAAO;gBAACC,MAAM;gBAAWC,OAAO;gBAAWC,KAAK;gBAAWC,OAAO;YAAS;QAC7E,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAWC,OAAO;gBAAWC,KAAK;gBAAQC,OAAO;YAAM;QACvE,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAaC,OAAO;gBAAaC,KAAK;gBAAQC,OAAO;YAAM;QAC3E,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAaC,OAAO;gBAAaC,KAAK;gBAAWC,OAAO;YAAS;QACjF,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAUC,OAAO;gBAAUC,KAAK;gBAASC,OAAO;YAAO;QACvE,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAaC,OAAO;gBAAaC,KAAK;gBAAWC,OAAO;YAAS;QACjF,KAAK;YACH,OAAO;gBAACH,MAAM;gBAAWC,OAAO;gBAAWC,KAAK;gBAAWC,OAAO;YAAS;IAC/E;IAEA,MAAMC,aAAoBL;IAC1B,OAAOK;AACT"}
|