@shipfox/client-workflows 21.0.0 → 22.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +33 -0
- package/dist/components/job-detail/job-detail-view.js +1 -2
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/job-empty-states.js +2 -2
- package/dist/components/job-detail/job-empty-states.js.map +1 -1
- package/dist/components/job-detail/job-execution-time-text.d.ts.map +1 -1
- package/dist/components/job-detail/job-execution-time-text.js +2 -15
- package/dist/components/job-detail/job-execution-time-text.js.map +1 -1
- package/dist/components/job-graph/job-duration-format.js +1 -1
- package/dist/components/job-graph/job-duration-format.js.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +2 -2
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/step-list/step-list.js +1 -2
- package/dist/components/step-list/step-list.js.map +1 -1
- 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 +13 -7
- 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.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +42 -37
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +14 -10
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +3 -3
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +20 -7
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +68 -50
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/pages/project-workflows-page.js +181 -172
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/components/job-detail/job-detail-view.tsx +1 -2
- package/src/components/job-detail/job-empty-states.tsx +2 -2
- package/src/components/job-detail/job-execution-time-text.test.ts +46 -0
- package/src/components/job-detail/job-execution-time-text.tsx +2 -23
- package/src/components/job-graph/job-duration-format.ts +1 -1
- package/src/components/job-graph/job-graph-content.tsx +2 -2
- package/src/components/step-list/step-list.tsx +1 -2
- package/src/components/workflow-run-list/workflow-run-list-content.tsx +7 -2
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +41 -40
- package/src/components/workflow-run-list/workflow-run-row.tsx +11 -8
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +9 -1
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +3 -3
- package/src/components/workflow-run-view/run-workspace-nav.test.tsx +40 -2
- package/src/components/workflow-run-view/run-workspace-nav.tsx +28 -9
- package/src/components/workflow-run-view/workflow-run-view.stories.tsx +3 -1
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +11 -8
- package/src/components/workflow-run-view/workflow-run-view.tsx +78 -70
- package/src/pages/project-workflows-page.test.tsx +14 -4
- package/src/pages/project-workflows-page.tsx +138 -132
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/step-list/step-list.tsx"],"sourcesContent":["import {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@shipfox/react-ui/accordion';\nimport {Badge, type BadgeVariant} from '@shipfox/react-ui/badge';\nimport {Dot} from '@shipfox/react-ui/dot';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider, useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useId, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n isWorkflowStatus,\n type Job,\n type JobDisplayStatus,\n type JobExecution,\n type Step,\n type StepSourceLocation,\n} from '#core/workflow-run.js';\nimport {formatJobExecutionTimeLabel} from '../job-graph/job-duration-format.js';\nimport {\n buildStepListModel,\n defaultStepListJobExecution,\n humanizeStatus,\n type StepAttemptModel,\n type StepListEntryModel,\n type StepListModel,\n} from './step-list-model.js';\n\nexport interface StepExpandedContext {\n step: Step;\n stepId: string;\n stepLabel: string;\n sourceLocation: StepSourceLocation | null;\n attempt: number;\n attemptId: string;\n attemptStartedAt: string;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n carriedOver: boolean;\n}\n\nexport interface StepListEmptyState {\n title: string;\n description: string;\n status?: JobDisplayStatus | undefined;\n}\n\nexport interface StepListProps {\n job: Job;\n jobExecution?: JobExecution | undefined;\n selectedAttemptId?: string | null | undefined;\n defaultSelectedAttemptId?: string | undefined;\n onSelectedAttemptChange?: ((attemptId: string | undefined) => void) | undefined;\n onExpandedAttemptIdsChange?: ((attemptIds: readonly string[]) => void) | undefined;\n inspectorOpenAttemptId?: string | null | undefined;\n onInspectorOpenChange?: ((attemptId: string | null) => void) | undefined;\n autoSelectActiveAttempt?: boolean | undefined;\n emptyState?: StepListEmptyState | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n renderInspector?: ((entry: StepListEntryModel) => ReactNode) | undefined;\n showHeader?: boolean | undefined;\n className?: string | undefined;\n}\n\nexport function StepList({\n job,\n jobExecution,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n onExpandedAttemptIdsChange,\n inspectorOpenAttemptId = null,\n onInspectorOpenChange,\n autoSelectActiveAttempt = false,\n emptyState,\n renderExpandedStep,\n renderInspector,\n showHeader = true,\n className,\n}: StepListProps) {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const model = useMemo(\n () => buildStepListModel({job, jobExecution: selectedJobExecution}),\n [job, selectedJobExecution],\n );\n\n return (\n <StepListContent\n key={model.jobExecutionId}\n model={model}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={defaultSelectedAttemptId}\n onSelectedAttemptChange={onSelectedAttemptChange}\n onExpandedAttemptIdsChange={onExpandedAttemptIdsChange}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt={autoSelectActiveAttempt}\n emptyState={emptyState}\n renderExpandedStep={renderExpandedStep}\n renderInspector={renderInspector}\n showHeader={showHeader}\n className={className}\n />\n );\n}\n\nfunction StepListContent({\n model,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n onExpandedAttemptIdsChange,\n inspectorOpenAttemptId = null,\n onInspectorOpenChange,\n autoSelectActiveAttempt,\n emptyState,\n renderExpandedStep,\n renderInspector,\n showHeader,\n className,\n}: Omit<StepListProps, 'job' | 'jobExecution'> & {model: StepListModel}) {\n const titleId = useId();\n const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState<string[]>(() =>\n selectedAttemptId\n ? [selectedAttemptId]\n : defaultSelectedAttemptId\n ? [defaultSelectedAttemptId]\n : [],\n );\n const [userSelectedAttempt, setUserSelectedAttempt] = useState(false);\n const lastNotifiedSelectedAttemptId = useRef<string | null>(null);\n const autoSelectedAttemptIdRef = useRef<string | undefined>(undefined);\n const previousSelectedAttemptIdRef = useRef<string | null | undefined>(selectedAttemptId);\n const lastReportedExpandedAttemptIdsKeyRef = useRef<string | null>(null);\n const shouldUseControlledCollapsedState =\n selectedAttemptId === null && lastNotifiedSelectedAttemptId.current === null;\n const autoSelectedAttemptId =\n selectedAttemptId === undefined && autoSelectActiveAttempt && !userSelectedAttempt\n ? (autoSelectedAttemptIdRef.current ?? model.activeEntryId)\n : undefined;\n const autoSelectedAttemptIds = useMemo(\n () => (autoSelectedAttemptId ? [autoSelectedAttemptId] : []),\n [autoSelectedAttemptId],\n );\n const selectedAttemptIds = useMemo(\n () =>\n shouldUseControlledCollapsedState\n ? []\n : localSelectedAttemptIds.length > 0\n ? localSelectedAttemptIds\n : autoSelectedAttemptIds,\n [autoSelectedAttemptIds, localSelectedAttemptIds, shouldUseControlledCollapsedState],\n );\n const hasExpandedContent = renderExpandedStep !== undefined;\n\n useEffect(() => {\n const key = selectedAttemptIds.join('|');\n if (lastReportedExpandedAttemptIdsKeyRef.current === key) return;\n lastReportedExpandedAttemptIdsKeyRef.current = key;\n onExpandedAttemptIdsChange?.(selectedAttemptIds);\n }, [onExpandedAttemptIdsChange, selectedAttemptIds]);\n\n useEffect(() => {\n if (selectedAttemptId !== undefined) {\n previousSelectedAttemptIdRef.current = selectedAttemptId;\n return;\n }\n\n // A changing landing candidate is normal while a live job polls. Only reset the local\n // accordion when the parent has just cleared a previously controlled URL selection.\n const wasControlled = previousSelectedAttemptIdRef.current !== undefined;\n previousSelectedAttemptIdRef.current = undefined;\n if (!wasControlled) return;\n\n setLocalSelectedAttemptIds(defaultSelectedAttemptId ? [defaultSelectedAttemptId] : []);\n setUserSelectedAttempt(false);\n autoSelectedAttemptIdRef.current = undefined;\n }, [defaultSelectedAttemptId, selectedAttemptId]);\n\n useEffect(() => {\n if (\n selectedAttemptId !== undefined ||\n !autoSelectActiveAttempt ||\n userSelectedAttempt ||\n localSelectedAttemptIds.length > 0 ||\n autoSelectedAttemptIdRef.current !== undefined\n ) {\n return;\n }\n if (model.activeEntryId) autoSelectedAttemptIdRef.current = model.activeEntryId;\n }, [\n autoSelectActiveAttempt,\n localSelectedAttemptIds.length,\n model.activeEntryId,\n selectedAttemptId,\n userSelectedAttempt,\n ]);\n\n useEffect(() => {\n if (selectedAttemptId === undefined) return;\n\n const nextSelectedAttemptId = selectedAttemptId ?? null;\n if (lastNotifiedSelectedAttemptId.current === nextSelectedAttemptId) {\n lastNotifiedSelectedAttemptId.current = null;\n return;\n }\n\n setLocalSelectedAttemptIds(selectedAttemptId ? [selectedAttemptId] : []);\n setUserSelectedAttempt(true);\n }, [selectedAttemptId]);\n\n useEffect(() => {\n if (\n inspectorOpenAttemptId !== null &&\n !model.entries.some((entry) => entry.id === inspectorOpenAttemptId && !entry.carriedOver)\n ) {\n onInspectorOpenChange?.(null);\n }\n }, [inspectorOpenAttemptId, model.entries, onInspectorOpenChange]);\n\n function selectAttempt(nextAttemptIds: string[]) {\n const nextAttemptId = nextSelectedAttemptId(selectedAttemptIds, nextAttemptIds);\n setUserSelectedAttempt(true);\n setLocalSelectedAttemptIds(nextAttemptIds);\n lastNotifiedSelectedAttemptId.current = nextAttemptId ?? null;\n onSelectedAttemptChange?.(nextAttemptId);\n }\n\n const inspectorEntry = model.entries.find(\n (entry) => entry.id === inspectorOpenAttemptId && !entry.carriedOver,\n );\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={showHeader ? titleId : undefined}\n className={cn(\n 'flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-neutral-base',\n className,\n )}\n >\n {showHeader ? (\n <div className=\"flex min-h-40 items-center border-b border-border-neutral-base px-row py-row\">\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"text-foreground-neutral-base\">\n {model.jobName}\n </Text>\n </div>\n ) : null}\n\n {model.entries.length === 0 ? (\n <StepListEmptyStateView emptyState={emptyState} />\n ) : (\n <Accordion\n type=\"multiple\"\n value={selectedAttemptIds}\n onValueChange={selectAttempt}\n asChild\n >\n <ol>\n {model.entries.map((entry, index) => {\n const selected = selectedAttemptIds.includes(entry.id);\n return (\n <StepRow\n key={entry.id}\n entry={entry}\n selected={selected}\n hasExpandedContent={hasExpandedContent}\n isLast={index === model.entries.length - 1}\n onSelect={() => {\n selectAttempt(\n hasExpandedContent\n ? toggleAttemptId(selectedAttemptIds, entry.id)\n : selected\n ? []\n : [entry.id],\n );\n }}\n onInspect={\n onInspectorOpenChange && !entry.carriedOver\n ? () => onInspectorOpenChange(entry.id)\n : undefined\n }\n expandedContent={\n selected\n ? renderExpandedStep?.({\n step: entry.step,\n stepId: entry.step.id,\n stepLabel: entry.step.label,\n sourceLocation: entry.step.sourceLocation,\n attempt: entry.attempt,\n attemptId: entry.id,\n attemptStartedAt: entry.startedAt,\n attemptError: entry.error,\n attemptStatus: entry.statusVisual.kind,\n carriedOver: entry.carriedOver,\n })\n : null\n }\n />\n );\n })}\n </ol>\n </Accordion>\n )}\n </section>\n {inspectorEntry ? renderInspector?.(inspectorEntry) : null}\n </TimeTickerProvider>\n );\n}\n\nfunction StepListEmptyStateView({\n emptyState = {\n title: 'No steps recorded',\n description: 'This job has not recorded any steps.',\n },\n}: {\n emptyState?: StepListEmptyState | undefined;\n}) {\n if (!emptyState.status) {\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n }\n\n return (\n <div className=\"flex min-h-120 flex-col items-center justify-center gap-inline p-panel\">\n <StepListEmptyStateIcon status={emptyState.status} />\n <div className=\"text-center\">\n <Text size=\"sm\" className=\"text-foreground-neutral-subtle\">\n {emptyState.title}\n </Text>\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {emptyState.description}\n </Text>\n </div>\n </div>\n );\n}\n\nfunction StepListEmptyStateIcon({status}: {status: JobDisplayStatus}) {\n if (status !== 'running') {\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-tight\">\n <WorkflowStatusIcon status={status} size={20} tooltip={false} />\n </div>\n );\n }\n\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-tight text-foreground-neutral-muted\">\n <Icon name=\"timerLine\" size={18} aria-hidden=\"true\" />\n </div>\n );\n}\n\nfunction StepRow({\n entry,\n selected,\n hasExpandedContent,\n isLast,\n onSelect,\n onInspect,\n expandedContent,\n}: {\n entry: StepListEntryModel;\n selected: boolean;\n hasExpandedContent: boolean;\n isLast: boolean;\n onSelect: () => void;\n onInspect?: (() => void) | undefined;\n expandedContent: ReactNode;\n}) {\n const shouldShowLabelTooltip = entry.step.label.length > 32;\n const rowContent = (\n <>\n <Icon\n name=\"chevronRight\"\n aria-hidden=\"true\"\n className={cn(\n 'size-14 shrink-0 text-foreground-neutral-muted transition-transform',\n selected && 'rotate-90',\n )}\n />\n <StepStatusIcon entry={entry} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Text size=\"sm\" bold className=\"truncate text-foreground-neutral-base\">\n {entry.step.label}\n </Text>\n {entry.step.attempts.length > 1 ? <StepAttemptChip attempt={entry} /> : null}\n {entry.carriedOver ? <CarriedOverBadge /> : null}\n </div>\n </div>\n <StepAttemptDurationLabel attempt={entry} />\n </>\n );\n const rowClasses = cn(\n 'group flex min-h-44 min-w-0 flex-1 items-center gap-x-inline bg-transparent px-row py-row text-left transition-colors hover:bg-transparent active:bg-transparent focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n entry.carriedOver && 'opacity-[0.55]',\n );\n const button = hasExpandedContent ? (\n <AccordionTrigger\n showIcon={false}\n aria-label={entryAccessibleLabel(entry)}\n className={rowClasses}\n >\n {rowContent}\n </AccordionTrigger>\n ) : (\n <button\n type=\"button\"\n aria-expanded={false}\n aria-label={entryAccessibleLabel(entry)}\n onClick={onSelect}\n className={rowClasses}\n >\n {rowContent}\n </button>\n );\n const triggerNode = shouldShowLabelTooltip ? (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent>\n <span className=\"block max-w-320 break-words\">{entry.step.label}</span>\n </TooltipContent>\n </Tooltip>\n ) : (\n button\n );\n const row = (\n <>\n <div\n className={cn(\n 'group flex min-w-0 items-center gap-inline bg-background-neutral-base pr-[8px] transition-colors hover:bg-background-neutral-hover active:bg-background-neutral-pressed',\n selected && 'bg-background-neutral-hover',\n !hasExpandedContent && ['border-b border-border-neutral-base', isLast && 'border-b-0'],\n )}\n >\n <div className=\"min-w-0 flex-1\">{triggerNode}</div>\n {onInspect ? (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={`Inspect ${entry.step.label}, attempt ${entry.attempt}`}\n onClick={onInspect}\n className=\"flex size-28 shrink-0 items-center justify-center rounded-4 bg-transparent text-foreground-neutral-muted outline-none transition-colors hover:bg-transparent hover:text-foreground-neutral-base active:bg-transparent focus-visible:shadow-button-neutral-focus\"\n >\n <Icon name=\"informationLine\" size={14} aria-hidden=\"true\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>Inspect step details</TooltipContent>\n </Tooltip>\n ) : null}\n </div>\n {selected && expandedContent ? (\n <AccordionContent className=\"border-t border-border-neutral-base bg-transparent px-0 py-0\">\n <div className=\"min-w-0\">{expandedContent}</div>\n </AccordionContent>\n ) : null}\n </>\n );\n\n if (hasExpandedContent) {\n return (\n <AccordionItem value={entry.id} asChild>\n <li>{row}</li>\n </AccordionItem>\n );\n }\n\n return <li>{row}</li>;\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt.\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction toggleAttemptId(selectedAttemptIds: readonly string[], attemptId: string): string[] {\n if (selectedAttemptIds.includes(attemptId)) {\n return selectedAttemptIds.filter((selectedAttemptId) => selectedAttemptId !== attemptId);\n }\n return [...selectedAttemptIds, attemptId];\n}\n\nfunction nextSelectedAttemptId(\n selectedAttemptIds: readonly string[],\n nextAttemptIds: readonly string[],\n): string | undefined {\n if (nextAttemptIds.length === 0) return undefined;\n\n const openedAttemptId = nextAttemptIds.find(\n (attemptId) => !selectedAttemptIds.includes(attemptId),\n );\n return openedAttemptId ?? nextAttemptIds.at(-1);\n}\n\nfunction StepStatusIcon({entry}: {entry: StepListEntryModel}) {\n if (isWorkflowStatus(entry.statusVisual.kind)) {\n return (\n <WorkflowStatusIcon\n status={entry.statusVisual.kind}\n ripple={entry.statusVisual.ripple}\n size={14}\n />\n );\n }\n\n return (\n <span role=\"img\" aria-label={entry.statusVisual.label} className=\"inline-flex shrink-0\">\n <Dot\n variant={entry.statusVisual.dot}\n ripple={entry.statusVisual.ripple}\n className=\"size-12 shrink-0\"\n />\n </span>\n );\n}\n\nconst attemptChipClasses: Record<NonNullable<BadgeVariant>, string> = {\n neutral: 'bg-tag-neutral-bg border-tag-neutral-border',\n info: 'bg-tag-blue-bg border-tag-blue-border',\n feature: 'bg-tag-purple-bg border-tag-purple-border',\n success: 'bg-tag-success-bg border-tag-success-border',\n warning: 'bg-tag-warning-bg border-tag-warning-border',\n error: 'bg-tag-error-bg border-tag-error-border',\n};\n\nfunction StepAttemptChip({attempt}: {attempt: StepAttemptModel}) {\n return (\n <div className=\"flex shrink-0 items-center gap-tight\" aria-hidden=\"true\">\n <span\n className={cn(\n 'inline-flex h-18 min-w-24 items-center justify-center rounded-4 border px-tight font-code text-xs leading-16 text-foreground-neutral-base',\n attemptChipClasses[attempt.statusVisual.badge ?? 'neutral'],\n )}\n >\n #{attempt.attempt}\n </span>\n </div>\n );\n}\n\nfunction entryAccessibleLabel(entry: StepListEntryModel): string {\n const parts = [entry.step.label, entry.statusVisual.label, `attempt ${entry.attempt}`];\n if (entry.step.error?.category) parts.push(humanizeStatus(entry.step.error.category));\n return parts.join(', ');\n}\n\nfunction StepAttemptDurationLabel({attempt}: {attempt: StepAttemptModel}) {\n const duration = attempt.displayDuration;\n if (duration === null) return null;\n\n if (duration.state === 'live') {\n return <LiveDurationText fromIso={duration.fromIso} />;\n }\n\n return <DurationText>{formatJobExecutionTimeLabel(duration)}</DurationText>;\n}\n\nfunction LiveDurationText({fromIso}: {fromIso: string}) {\n useTimeTick();\n return <DurationText>{humanDuration(fromIso)}</DurationText>;\n}\n\nfunction DurationText({children}: {children: string}) {\n return (\n <Code as=\"span\" variant=\"label\" className=\"shrink-0 tabular-nums text-foreground-neutral-muted\">\n {children}\n </Code>\n );\n}\n"],"names":["Accordion","AccordionContent","AccordionItem","AccordionTrigger","Badge","Dot","EmptyState","Icon","TimeTickerProvider","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","humanDuration","useEffect","useId","useMemo","useRef","useState","WorkflowStatusIcon","isWorkflowStatus","formatJobExecutionTimeLabel","buildStepListModel","defaultStepListJobExecution","humanizeStatus","StepList","job","jobExecution","selectedAttemptId","defaultSelectedAttemptId","onSelectedAttemptChange","onExpandedAttemptIdsChange","inspectorOpenAttemptId","onInspectorOpenChange","autoSelectActiveAttempt","emptyState","renderExpandedStep","renderInspector","showHeader","className","selectedJobExecution","model","StepListContent","jobExecutionId","titleId","localSelectedAttemptIds","setLocalSelectedAttemptIds","userSelectedAttempt","setUserSelectedAttempt","lastNotifiedSelectedAttemptId","autoSelectedAttemptIdRef","undefined","previousSelectedAttemptIdRef","lastReportedExpandedAttemptIdsKeyRef","shouldUseControlledCollapsedState","current","autoSelectedAttemptId","activeEntryId","autoSelectedAttemptIds","selectedAttemptIds","length","hasExpandedContent","key","join","wasControlled","nextSelectedAttemptId","entries","some","entry","id","carriedOver","selectAttempt","nextAttemptIds","nextAttemptId","inspectorEntry","find","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","div","as","size","bold","jobName","StepListEmptyStateView","type","value","onValueChange","asChild","ol","map","index","selected","includes","StepRow","isLast","onSelect","toggleAttemptId","onInspect","expandedContent","step","stepId","stepLabel","label","sourceLocation","attempt","attemptId","attemptStartedAt","startedAt","attemptError","error","attemptStatus","statusVisual","kind","title","description","status","icon","variant","StepListEmptyStateIcon","tooltip","name","aria-hidden","shouldShowLabelTooltip","rowContent","StepStatusIcon","attempts","StepAttemptChip","CarriedOverBadge","StepAttemptDurationLabel","rowClasses","button","showIcon","aria-label","entryAccessibleLabel","aria-expanded","onClick","triggerNode","span","row","li","filter","openedAttemptId","at","ripple","role","dot","attemptChipClasses","neutral","info","feature","success","warning","badge","parts","category","push","duration","displayDuration","state","LiveDurationText","fromIso","DurationText","children"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/step-list/step-list.tsx"],"sourcesContent":["import {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@shipfox/react-ui/accordion';\nimport {Badge, type BadgeVariant} from '@shipfox/react-ui/badge';\nimport {Dot} from '@shipfox/react-ui/dot';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider, useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useId, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n isWorkflowStatus,\n type Job,\n type JobDisplayStatus,\n type JobExecution,\n type Step,\n type StepSourceLocation,\n} from '#core/workflow-run.js';\nimport {formatJobExecutionTimeLabel} from '../job-graph/job-duration-format.js';\nimport {\n buildStepListModel,\n defaultStepListJobExecution,\n humanizeStatus,\n type StepAttemptModel,\n type StepListEntryModel,\n type StepListModel,\n} from './step-list-model.js';\n\nexport interface StepExpandedContext {\n step: Step;\n stepId: string;\n stepLabel: string;\n sourceLocation: StepSourceLocation | null;\n attempt: number;\n attemptId: string;\n attemptStartedAt: string;\n attemptError: Record<string, unknown> | null;\n attemptStatus: string;\n carriedOver: boolean;\n}\n\nexport interface StepListEmptyState {\n title: string;\n description: string;\n status?: JobDisplayStatus | undefined;\n}\n\nexport interface StepListProps {\n job: Job;\n jobExecution?: JobExecution | undefined;\n selectedAttemptId?: string | null | undefined;\n defaultSelectedAttemptId?: string | undefined;\n onSelectedAttemptChange?: ((attemptId: string | undefined) => void) | undefined;\n onExpandedAttemptIdsChange?: ((attemptIds: readonly string[]) => void) | undefined;\n inspectorOpenAttemptId?: string | null | undefined;\n onInspectorOpenChange?: ((attemptId: string | null) => void) | undefined;\n autoSelectActiveAttempt?: boolean | undefined;\n emptyState?: StepListEmptyState | undefined;\n renderExpandedStep?: ((context: StepExpandedContext) => ReactNode) | undefined;\n renderInspector?: ((entry: StepListEntryModel) => ReactNode) | undefined;\n showHeader?: boolean | undefined;\n className?: string | undefined;\n}\n\nexport function StepList({\n job,\n jobExecution,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n onExpandedAttemptIdsChange,\n inspectorOpenAttemptId = null,\n onInspectorOpenChange,\n autoSelectActiveAttempt = false,\n emptyState,\n renderExpandedStep,\n renderInspector,\n showHeader = true,\n className,\n}: StepListProps) {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const model = useMemo(\n () => buildStepListModel({job, jobExecution: selectedJobExecution}),\n [job, selectedJobExecution],\n );\n\n return (\n <StepListContent\n key={model.jobExecutionId}\n model={model}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={defaultSelectedAttemptId}\n onSelectedAttemptChange={onSelectedAttemptChange}\n onExpandedAttemptIdsChange={onExpandedAttemptIdsChange}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt={autoSelectActiveAttempt}\n emptyState={emptyState}\n renderExpandedStep={renderExpandedStep}\n renderInspector={renderInspector}\n showHeader={showHeader}\n className={className}\n />\n );\n}\n\nfunction StepListContent({\n model,\n selectedAttemptId,\n defaultSelectedAttemptId,\n onSelectedAttemptChange,\n onExpandedAttemptIdsChange,\n inspectorOpenAttemptId = null,\n onInspectorOpenChange,\n autoSelectActiveAttempt,\n emptyState,\n renderExpandedStep,\n renderInspector,\n showHeader,\n className,\n}: Omit<StepListProps, 'job' | 'jobExecution'> & {model: StepListModel}) {\n const titleId = useId();\n const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState<string[]>(() =>\n selectedAttemptId\n ? [selectedAttemptId]\n : defaultSelectedAttemptId\n ? [defaultSelectedAttemptId]\n : [],\n );\n const [userSelectedAttempt, setUserSelectedAttempt] = useState(false);\n const lastNotifiedSelectedAttemptId = useRef<string | null>(null);\n const autoSelectedAttemptIdRef = useRef<string | undefined>(undefined);\n const previousSelectedAttemptIdRef = useRef<string | null | undefined>(selectedAttemptId);\n const lastReportedExpandedAttemptIdsKeyRef = useRef<string | null>(null);\n const shouldUseControlledCollapsedState =\n selectedAttemptId === null && lastNotifiedSelectedAttemptId.current === null;\n const autoSelectedAttemptId =\n selectedAttemptId === undefined && autoSelectActiveAttempt && !userSelectedAttempt\n ? (autoSelectedAttemptIdRef.current ?? model.activeEntryId)\n : undefined;\n const autoSelectedAttemptIds = useMemo(\n () => (autoSelectedAttemptId ? [autoSelectedAttemptId] : []),\n [autoSelectedAttemptId],\n );\n const selectedAttemptIds = useMemo(\n () =>\n shouldUseControlledCollapsedState\n ? []\n : localSelectedAttemptIds.length > 0\n ? localSelectedAttemptIds\n : autoSelectedAttemptIds,\n [autoSelectedAttemptIds, localSelectedAttemptIds, shouldUseControlledCollapsedState],\n );\n const hasExpandedContent = renderExpandedStep !== undefined;\n\n useEffect(() => {\n const key = selectedAttemptIds.join('|');\n if (lastReportedExpandedAttemptIdsKeyRef.current === key) return;\n lastReportedExpandedAttemptIdsKeyRef.current = key;\n onExpandedAttemptIdsChange?.(selectedAttemptIds);\n }, [onExpandedAttemptIdsChange, selectedAttemptIds]);\n\n useEffect(() => {\n if (selectedAttemptId !== undefined) {\n previousSelectedAttemptIdRef.current = selectedAttemptId;\n return;\n }\n\n // A changing landing candidate is normal while a live job polls. Only reset the local\n // accordion when the parent has just cleared a previously controlled URL selection.\n const wasControlled = previousSelectedAttemptIdRef.current !== undefined;\n previousSelectedAttemptIdRef.current = undefined;\n if (!wasControlled) return;\n\n setLocalSelectedAttemptIds(defaultSelectedAttemptId ? [defaultSelectedAttemptId] : []);\n setUserSelectedAttempt(false);\n autoSelectedAttemptIdRef.current = undefined;\n }, [defaultSelectedAttemptId, selectedAttemptId]);\n\n useEffect(() => {\n if (\n selectedAttemptId !== undefined ||\n !autoSelectActiveAttempt ||\n userSelectedAttempt ||\n localSelectedAttemptIds.length > 0 ||\n autoSelectedAttemptIdRef.current !== undefined\n ) {\n return;\n }\n if (model.activeEntryId) autoSelectedAttemptIdRef.current = model.activeEntryId;\n }, [\n autoSelectActiveAttempt,\n localSelectedAttemptIds.length,\n model.activeEntryId,\n selectedAttemptId,\n userSelectedAttempt,\n ]);\n\n useEffect(() => {\n if (selectedAttemptId === undefined) return;\n\n const nextSelectedAttemptId = selectedAttemptId ?? null;\n if (lastNotifiedSelectedAttemptId.current === nextSelectedAttemptId) {\n lastNotifiedSelectedAttemptId.current = null;\n return;\n }\n\n setLocalSelectedAttemptIds(selectedAttemptId ? [selectedAttemptId] : []);\n setUserSelectedAttempt(true);\n }, [selectedAttemptId]);\n\n useEffect(() => {\n if (\n inspectorOpenAttemptId !== null &&\n !model.entries.some((entry) => entry.id === inspectorOpenAttemptId && !entry.carriedOver)\n ) {\n onInspectorOpenChange?.(null);\n }\n }, [inspectorOpenAttemptId, model.entries, onInspectorOpenChange]);\n\n function selectAttempt(nextAttemptIds: string[]) {\n const nextAttemptId = nextSelectedAttemptId(selectedAttemptIds, nextAttemptIds);\n setUserSelectedAttempt(true);\n setLocalSelectedAttemptIds(nextAttemptIds);\n lastNotifiedSelectedAttemptId.current = nextAttemptId ?? null;\n onSelectedAttemptChange?.(nextAttemptId);\n }\n\n const inspectorEntry = model.entries.find(\n (entry) => entry.id === inspectorOpenAttemptId && !entry.carriedOver,\n );\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <section\n aria-labelledby={showHeader ? titleId : undefined}\n className={cn(\n 'flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-neutral-base',\n className,\n )}\n >\n {showHeader ? (\n <div className=\"flex min-h-40 items-center border-b border-border-neutral-base px-row py-row\">\n <Text as=\"h2\" id={titleId} size=\"sm\" bold className=\"text-foreground-neutral-base\">\n {model.jobName}\n </Text>\n </div>\n ) : null}\n\n {model.entries.length === 0 ? (\n <StepListEmptyStateView emptyState={emptyState} />\n ) : (\n <Accordion\n type=\"multiple\"\n value={selectedAttemptIds}\n onValueChange={selectAttempt}\n asChild\n >\n <ol>\n {model.entries.map((entry, index) => {\n const selected = selectedAttemptIds.includes(entry.id);\n return (\n <StepRow\n key={entry.id}\n entry={entry}\n selected={selected}\n hasExpandedContent={hasExpandedContent}\n isLast={index === model.entries.length - 1}\n onSelect={() => {\n selectAttempt(\n hasExpandedContent\n ? toggleAttemptId(selectedAttemptIds, entry.id)\n : selected\n ? []\n : [entry.id],\n );\n }}\n onInspect={\n onInspectorOpenChange && !entry.carriedOver\n ? () => onInspectorOpenChange(entry.id)\n : undefined\n }\n expandedContent={\n selected\n ? renderExpandedStep?.({\n step: entry.step,\n stepId: entry.step.id,\n stepLabel: entry.step.label,\n sourceLocation: entry.step.sourceLocation,\n attempt: entry.attempt,\n attemptId: entry.id,\n attemptStartedAt: entry.startedAt,\n attemptError: entry.error,\n attemptStatus: entry.statusVisual.kind,\n carriedOver: entry.carriedOver,\n })\n : null\n }\n />\n );\n })}\n </ol>\n </Accordion>\n )}\n </section>\n {inspectorEntry ? renderInspector?.(inspectorEntry) : null}\n </TimeTickerProvider>\n );\n}\n\nfunction StepListEmptyStateView({\n emptyState = {\n title: 'No steps recorded',\n description: 'This job has not recorded any steps.',\n },\n}: {\n emptyState?: StepListEmptyState | undefined;\n}) {\n if (!emptyState.status) {\n return (\n <EmptyState\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"panel\"\n />\n );\n }\n\n return (\n <div className=\"flex min-h-120 flex-col items-center justify-center gap-inline p-panel\">\n <StepListEmptyStateIcon status={emptyState.status} />\n <div className=\"text-center\">\n <Text size=\"sm\" className=\"text-foreground-neutral-subtle\">\n {emptyState.title}\n </Text>\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {emptyState.description}\n </Text>\n </div>\n </div>\n );\n}\n\nfunction StepListEmptyStateIcon({status}: {status: JobDisplayStatus}) {\n if (status !== 'running') {\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-tight\">\n <WorkflowStatusIcon status={status} size={20} tooltip={false} />\n </div>\n );\n }\n\n return (\n <div className=\"flex size-32 items-center justify-center rounded-6 border border-border-neutral-strong bg-background-neutral-base p-tight text-foreground-neutral-muted\">\n <Icon name=\"timerLine\" size={18} aria-hidden=\"true\" />\n </div>\n );\n}\n\nfunction StepRow({\n entry,\n selected,\n hasExpandedContent,\n isLast,\n onSelect,\n onInspect,\n expandedContent,\n}: {\n entry: StepListEntryModel;\n selected: boolean;\n hasExpandedContent: boolean;\n isLast: boolean;\n onSelect: () => void;\n onInspect?: (() => void) | undefined;\n expandedContent: ReactNode;\n}) {\n const shouldShowLabelTooltip = entry.step.label.length > 32;\n const rowContent = (\n <>\n <Icon\n name=\"chevronRight\"\n aria-hidden=\"true\"\n className={cn(\n 'size-14 shrink-0 text-foreground-neutral-muted transition-transform',\n selected && 'rotate-90',\n )}\n />\n <StepStatusIcon entry={entry} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Text size=\"sm\" bold className=\"truncate text-foreground-neutral-base\">\n {entry.step.label}\n </Text>\n {entry.step.attempts.length > 1 ? <StepAttemptChip attempt={entry} /> : null}\n {entry.carriedOver ? <CarriedOverBadge /> : null}\n </div>\n </div>\n <StepAttemptDurationLabel attempt={entry} />\n </>\n );\n const rowClasses = cn(\n 'group flex min-h-44 min-w-0 flex-1 items-center gap-x-inline bg-transparent px-row py-row text-left transition-colors hover:bg-transparent active:bg-transparent focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n entry.carriedOver && 'opacity-[0.55]',\n );\n const button = hasExpandedContent ? (\n <AccordionTrigger\n showIcon={false}\n aria-label={entryAccessibleLabel(entry)}\n className={rowClasses}\n >\n {rowContent}\n </AccordionTrigger>\n ) : (\n <button\n type=\"button\"\n aria-expanded={false}\n aria-label={entryAccessibleLabel(entry)}\n onClick={onSelect}\n className={rowClasses}\n >\n {rowContent}\n </button>\n );\n const triggerNode = shouldShowLabelTooltip ? (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent>\n <span className=\"block max-w-320 break-words\">{entry.step.label}</span>\n </TooltipContent>\n </Tooltip>\n ) : (\n button\n );\n const row = (\n <>\n <div\n className={cn(\n 'group flex min-w-0 items-center gap-inline bg-background-neutral-base pr-[8px] transition-colors hover:bg-background-neutral-hover active:bg-background-neutral-pressed',\n selected && 'bg-background-neutral-hover',\n !hasExpandedContent && ['border-b border-border-neutral-base', isLast && 'border-b-0'],\n )}\n >\n <div className=\"min-w-0 flex-1\">{triggerNode}</div>\n {onInspect ? (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={`Inspect ${entry.step.label}, attempt ${entry.attempt}`}\n onClick={onInspect}\n className=\"flex size-28 shrink-0 items-center justify-center rounded-4 bg-transparent text-foreground-neutral-muted outline-none transition-colors hover:bg-transparent hover:text-foreground-neutral-base active:bg-transparent focus-visible:shadow-button-neutral-focus\"\n >\n <Icon name=\"informationLine\" size={14} aria-hidden=\"true\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>Inspect step details</TooltipContent>\n </Tooltip>\n ) : null}\n </div>\n {selected && expandedContent ? (\n <AccordionContent className=\"border-t border-border-neutral-base bg-transparent px-0 py-0\">\n <div className=\"min-w-0\">{expandedContent}</div>\n </AccordionContent>\n ) : null}\n </>\n );\n\n if (hasExpandedContent) {\n return (\n <AccordionItem value={entry.id} asChild>\n <li>{row}</li>\n </AccordionItem>\n );\n }\n\n return <li>{row}</li>;\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt.\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction toggleAttemptId(selectedAttemptIds: readonly string[], attemptId: string): string[] {\n if (selectedAttemptIds.includes(attemptId)) {\n return selectedAttemptIds.filter((selectedAttemptId) => selectedAttemptId !== attemptId);\n }\n return [...selectedAttemptIds, attemptId];\n}\n\nfunction nextSelectedAttemptId(\n selectedAttemptIds: readonly string[],\n nextAttemptIds: readonly string[],\n): string | undefined {\n if (nextAttemptIds.length === 0) return undefined;\n\n const openedAttemptId = nextAttemptIds.find(\n (attemptId) => !selectedAttemptIds.includes(attemptId),\n );\n return openedAttemptId ?? nextAttemptIds.at(-1);\n}\n\nfunction StepStatusIcon({entry}: {entry: StepListEntryModel}) {\n if (isWorkflowStatus(entry.statusVisual.kind)) {\n return (\n <WorkflowStatusIcon\n status={entry.statusVisual.kind}\n ripple={entry.statusVisual.ripple}\n size={14}\n />\n );\n }\n\n return (\n <span role=\"img\" aria-label={entry.statusVisual.label} className=\"inline-flex shrink-0\">\n <Dot\n variant={entry.statusVisual.dot}\n ripple={entry.statusVisual.ripple}\n className=\"size-12 shrink-0\"\n />\n </span>\n );\n}\n\nconst attemptChipClasses: Record<NonNullable<BadgeVariant>, string> = {\n neutral: 'bg-tag-neutral-bg border-tag-neutral-border',\n info: 'bg-tag-blue-bg border-tag-blue-border',\n feature: 'bg-tag-purple-bg border-tag-purple-border',\n success: 'bg-tag-success-bg border-tag-success-border',\n warning: 'bg-tag-warning-bg border-tag-warning-border',\n error: 'bg-tag-error-bg border-tag-error-border',\n};\n\nfunction StepAttemptChip({attempt}: {attempt: StepAttemptModel}) {\n return (\n <div className=\"flex shrink-0 items-center gap-tight\" aria-hidden=\"true\">\n <span\n className={cn(\n 'inline-flex h-18 min-w-24 items-center justify-center rounded-4 border px-tight font-code text-xs leading-16 text-foreground-neutral-base',\n attemptChipClasses[attempt.statusVisual.badge ?? 'neutral'],\n )}\n >\n #{attempt.attempt}\n </span>\n </div>\n );\n}\n\nfunction entryAccessibleLabel(entry: StepListEntryModel): string {\n const parts = [entry.step.label, entry.statusVisual.label, `attempt ${entry.attempt}`];\n if (entry.step.error?.category) parts.push(humanizeStatus(entry.step.error.category));\n return parts.join(', ');\n}\n\nfunction StepAttemptDurationLabel({attempt}: {attempt: StepAttemptModel}) {\n const duration = attempt.displayDuration;\n if (duration === null) return null;\n\n if (duration.state === 'live') {\n return <LiveDurationText fromIso={duration.fromIso} />;\n }\n\n return <DurationText>{formatJobExecutionTimeLabel(duration)}</DurationText>;\n}\n\nfunction LiveDurationText({fromIso}: {fromIso: string}) {\n useTimeTick();\n return <DurationText>{humanDuration(fromIso)}</DurationText>;\n}\n\nfunction DurationText({children}: {children: string}) {\n return (\n <Code as=\"span\" variant=\"label\" className=\"shrink-0 tabular-nums text-foreground-neutral-muted\">\n {children}\n </Code>\n );\n}\n"],"names":["Accordion","AccordionContent","AccordionItem","AccordionTrigger","Badge","Dot","EmptyState","Icon","TimeTickerProvider","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","humanDuration","useEffect","useId","useMemo","useRef","useState","WorkflowStatusIcon","isWorkflowStatus","formatJobExecutionTimeLabel","buildStepListModel","defaultStepListJobExecution","humanizeStatus","StepList","job","jobExecution","selectedAttemptId","defaultSelectedAttemptId","onSelectedAttemptChange","onExpandedAttemptIdsChange","inspectorOpenAttemptId","onInspectorOpenChange","autoSelectActiveAttempt","emptyState","renderExpandedStep","renderInspector","showHeader","className","selectedJobExecution","model","StepListContent","jobExecutionId","titleId","localSelectedAttemptIds","setLocalSelectedAttemptIds","userSelectedAttempt","setUserSelectedAttempt","lastNotifiedSelectedAttemptId","autoSelectedAttemptIdRef","undefined","previousSelectedAttemptIdRef","lastReportedExpandedAttemptIdsKeyRef","shouldUseControlledCollapsedState","current","autoSelectedAttemptId","activeEntryId","autoSelectedAttemptIds","selectedAttemptIds","length","hasExpandedContent","key","join","wasControlled","nextSelectedAttemptId","entries","some","entry","id","carriedOver","selectAttempt","nextAttemptIds","nextAttemptId","inspectorEntry","find","intervalMs","reducedMotionIntervalMs","section","aria-labelledby","div","as","size","bold","jobName","StepListEmptyStateView","type","value","onValueChange","asChild","ol","map","index","selected","includes","StepRow","isLast","onSelect","toggleAttemptId","onInspect","expandedContent","step","stepId","stepLabel","label","sourceLocation","attempt","attemptId","attemptStartedAt","startedAt","attemptError","error","attemptStatus","statusVisual","kind","title","description","status","icon","variant","StepListEmptyStateIcon","tooltip","name","aria-hidden","shouldShowLabelTooltip","rowContent","StepStatusIcon","attempts","StepAttemptChip","CarriedOverBadge","StepAttemptDurationLabel","rowClasses","button","showIcon","aria-label","entryAccessibleLabel","aria-expanded","onClick","triggerNode","span","row","li","filter","openedAttemptId","at","ripple","role","dot","attemptChipClasses","neutral","info","feature","success","warning","badge","parts","category","push","duration","displayDuration","state","LiveDurationText","fromIso","DurationText","children"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-run-list-content.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/workflow-run-list-content.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workflow-run-list-content.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/workflow-run-list-content.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,YAAY,CAAC;AAUrD,UAAU,2BAA2B;IACnC,KAAK,EAAE,oBAAoB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,mBAAmB,EAAE,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,SAAS,EACT,IAAI,EACJ,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,oBAAoB,EACpB,UAAU,GACX,EAAE,2BAA2B,+BAmD7B"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { QueryLoadError } from '@shipfox/client-ui';
|
|
3
|
+
import { Panel } from '@shipfox/react-ui/panel';
|
|
3
4
|
import { WorkflowRunListEmpty, WorkflowRunListLoadMore, WorkflowRunListNoMatches, WorkflowRunListSkeleton, WorkflowRunListStaleError } from './workflow-run-list-states.js';
|
|
4
5
|
import { WorkflowRunRowList } from './workflow-run-row.js';
|
|
5
6
|
export function WorkflowRunListContent({ query, totalRuns, runs, workspaceSlug, projectSlug, hasActiveFilters, onClearFilters, hasNextPage, isFetchingNextPage, isFetchNextPageError, onLoadMore }) {
|
|
@@ -20,17 +21,22 @@ export function WorkflowRunListContent({ query, totalRuns, runs, workspaceSlug,
|
|
|
20
21
|
className: "min-h-0 flex-1 overflow-y-auto",
|
|
21
22
|
children: [
|
|
22
23
|
isPending ? /*#__PURE__*/ _jsx(WorkflowRunListSkeleton, {}) : null,
|
|
23
|
-
!isPending ? /*#__PURE__*/ _jsx(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
!isPending ? /*#__PURE__*/ _jsx(Panel, {
|
|
25
|
+
children: /*#__PURE__*/ _jsx(QueryLoadError, {
|
|
26
|
+
query: query,
|
|
27
|
+
subject: "workflow runs",
|
|
28
|
+
icon: "pulseLine",
|
|
29
|
+
variant: "panel"
|
|
30
|
+
})
|
|
27
31
|
}) : null,
|
|
28
32
|
!isPending && refreshFailed ? /*#__PURE__*/ _jsx(WorkflowRunListStaleError, {
|
|
29
33
|
query: query
|
|
30
34
|
}) : null,
|
|
31
|
-
showEmptyState ? /*#__PURE__*/ _jsx(
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
showEmptyState ? /*#__PURE__*/ _jsx(Panel, {
|
|
36
|
+
children: /*#__PURE__*/ _jsx(WorkflowRunListEmpty, {
|
|
37
|
+
workspaceSlug: workspaceSlug,
|
|
38
|
+
projectSlug: projectSlug
|
|
39
|
+
})
|
|
34
40
|
}) : null,
|
|
35
41
|
showNoMatches ? /*#__PURE__*/ _jsx(WorkflowRunListNoMatches, {
|
|
36
42
|
onClear: onClearFilters,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-list/workflow-run-list-content.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport type {WorkflowRunListItem} from '#core/workflow-run.js';\nimport type {WorkflowRunListQuery} from './types.js';\nimport {\n WorkflowRunListEmpty,\n WorkflowRunListLoadMore,\n WorkflowRunListNoMatches,\n WorkflowRunListSkeleton,\n WorkflowRunListStaleError,\n} from './workflow-run-list-states.js';\nimport {WorkflowRunRowList} from './workflow-run-row.js';\n\ninterface WorkflowRunListContentProps {\n query: WorkflowRunListQuery;\n totalRuns: number;\n runs: WorkflowRunListItem[];\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n hasActiveFilters: boolean;\n onClearFilters: () => void;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n isFetchNextPageError: boolean;\n onLoadMore?: () => void;\n}\n\nexport function WorkflowRunListContent({\n query,\n totalRuns,\n runs,\n workspaceSlug,\n projectSlug,\n hasActiveFilters,\n onClearFilters,\n hasNextPage,\n isFetchingNextPage,\n isFetchNextPageError,\n onLoadMore,\n}: WorkflowRunListContentProps) {\n const {isPending, isError} = query;\n // A refetch that fails after a prior success keeps the rows on screen behind a slim\n // banner. QueryLoadError owns the inverse case (errored before anything loaded) and\n // self-gates to nothing here once data exists. A failed fetch never renders as an empty\n // list: \"nothing here\" and \"we could not find out\" are different answers.\n const refreshFailed = isError && query.data !== undefined;\n // Keyed off \"we have an answer\" rather than \"no error\", so a stale refresh still reports\n // whether the filters match anything instead of leaving the list blank under the banner.\n const hasLoaded = !isPending && query.data !== undefined;\n // An empty result is only an empty *list* when no filter is on. With filters active it is a\n // no-matches state even at zero loaded runs, so the way out is clearing them.\n const showEmptyState = hasLoaded && totalRuns === 0 && !hasActiveFilters;\n const showNoMatches = hasLoaded && runs.length === 0 && !showEmptyState;\n\n return (\n <div className=\"min-h-0 flex-1 overflow-y-auto\">\n {isPending ? <WorkflowRunListSkeleton /> : null}\n {!isPending ? (\n <QueryLoadError query={query} subject=\"workflow runs\" icon=\"pulseLine\" />\n ) : null}\n {!isPending && refreshFailed ? <WorkflowRunListStaleError query={query} /> : null}\n {showEmptyState ? (\n <WorkflowRunListEmpty workspaceSlug={workspaceSlug} projectSlug={projectSlug} />\n ) : null}\n {showNoMatches ? (\n <WorkflowRunListNoMatches\n onClear={onClearFilters}\n hasNextPage={hasNextPage}\n isFetchingNextPage={isFetchingNextPage}\n isFetchNextPageError={isFetchNextPageError}\n {...(onLoadMore ? {onLoadMore} : {})}\n />\n ) : null}\n {!isPending && runs.length > 0 ? (\n <>\n <WorkflowRunRowList runs={runs} workspaceSlug={workspaceSlug} projectSlug={projectSlug} />\n <WorkflowRunListLoadMore\n hasNextPage={hasNextPage}\n isFetchingNextPage={isFetchingNextPage}\n isFetchNextPageError={isFetchNextPageError}\n {...(onLoadMore ? {onLoadMore} : {})}\n />\n </>\n ) : null}\n </div>\n );\n}\n"],"names":["QueryLoadError","WorkflowRunListEmpty","WorkflowRunListLoadMore","WorkflowRunListNoMatches","WorkflowRunListSkeleton","WorkflowRunListStaleError","WorkflowRunRowList","WorkflowRunListContent","query","totalRuns","runs","workspaceSlug","projectSlug","hasActiveFilters","onClearFilters","hasNextPage","isFetchingNextPage","isFetchNextPageError","onLoadMore","isPending","isError","refreshFailed","data","undefined","hasLoaded","showEmptyState","showNoMatches","length","div","className","subject","icon","onClear"],"mappings":";AAAA,SAAQA,cAAc,QAAO,qBAAqB;
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-list/workflow-run-list-content.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {Panel} from '@shipfox/react-ui/panel';\nimport type {WorkflowRunListItem} from '#core/workflow-run.js';\nimport type {WorkflowRunListQuery} from './types.js';\nimport {\n WorkflowRunListEmpty,\n WorkflowRunListLoadMore,\n WorkflowRunListNoMatches,\n WorkflowRunListSkeleton,\n WorkflowRunListStaleError,\n} from './workflow-run-list-states.js';\nimport {WorkflowRunRowList} from './workflow-run-row.js';\n\ninterface WorkflowRunListContentProps {\n query: WorkflowRunListQuery;\n totalRuns: number;\n runs: WorkflowRunListItem[];\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n hasActiveFilters: boolean;\n onClearFilters: () => void;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n isFetchNextPageError: boolean;\n onLoadMore?: () => void;\n}\n\nexport function WorkflowRunListContent({\n query,\n totalRuns,\n runs,\n workspaceSlug,\n projectSlug,\n hasActiveFilters,\n onClearFilters,\n hasNextPage,\n isFetchingNextPage,\n isFetchNextPageError,\n onLoadMore,\n}: WorkflowRunListContentProps) {\n const {isPending, isError} = query;\n // A refetch that fails after a prior success keeps the rows on screen behind a slim\n // banner. QueryLoadError owns the inverse case (errored before anything loaded) and\n // self-gates to nothing here once data exists. A failed fetch never renders as an empty\n // list: \"nothing here\" and \"we could not find out\" are different answers.\n const refreshFailed = isError && query.data !== undefined;\n // Keyed off \"we have an answer\" rather than \"no error\", so a stale refresh still reports\n // whether the filters match anything instead of leaving the list blank under the banner.\n const hasLoaded = !isPending && query.data !== undefined;\n // An empty result is only an empty *list* when no filter is on. With filters active it is a\n // no-matches state even at zero loaded runs, so the way out is clearing them.\n const showEmptyState = hasLoaded && totalRuns === 0 && !hasActiveFilters;\n const showNoMatches = hasLoaded && runs.length === 0 && !showEmptyState;\n\n return (\n <div className=\"min-h-0 flex-1 overflow-y-auto\">\n {isPending ? <WorkflowRunListSkeleton /> : null}\n {!isPending ? (\n <Panel>\n <QueryLoadError query={query} subject=\"workflow runs\" icon=\"pulseLine\" variant=\"panel\" />\n </Panel>\n ) : null}\n {!isPending && refreshFailed ? <WorkflowRunListStaleError query={query} /> : null}\n {showEmptyState ? (\n <Panel>\n <WorkflowRunListEmpty workspaceSlug={workspaceSlug} projectSlug={projectSlug} />\n </Panel>\n ) : null}\n {showNoMatches ? (\n <WorkflowRunListNoMatches\n onClear={onClearFilters}\n hasNextPage={hasNextPage}\n isFetchingNextPage={isFetchingNextPage}\n isFetchNextPageError={isFetchNextPageError}\n {...(onLoadMore ? {onLoadMore} : {})}\n />\n ) : null}\n {!isPending && runs.length > 0 ? (\n <>\n <WorkflowRunRowList runs={runs} workspaceSlug={workspaceSlug} projectSlug={projectSlug} />\n <WorkflowRunListLoadMore\n hasNextPage={hasNextPage}\n isFetchingNextPage={isFetchingNextPage}\n isFetchNextPageError={isFetchNextPageError}\n {...(onLoadMore ? {onLoadMore} : {})}\n />\n </>\n ) : null}\n </div>\n );\n}\n"],"names":["QueryLoadError","Panel","WorkflowRunListEmpty","WorkflowRunListLoadMore","WorkflowRunListNoMatches","WorkflowRunListSkeleton","WorkflowRunListStaleError","WorkflowRunRowList","WorkflowRunListContent","query","totalRuns","runs","workspaceSlug","projectSlug","hasActiveFilters","onClearFilters","hasNextPage","isFetchingNextPage","isFetchNextPageError","onLoadMore","isPending","isError","refreshFailed","data","undefined","hasLoaded","showEmptyState","showNoMatches","length","div","className","subject","icon","variant","onClear"],"mappings":";AAAA,SAAQA,cAAc,QAAO,qBAAqB;AAClD,SAAQC,KAAK,QAAO,0BAA0B;AAG9C,SACEC,oBAAoB,EACpBC,uBAAuB,EACvBC,wBAAwB,EACxBC,uBAAuB,EACvBC,yBAAyB,QACpB,gCAAgC;AACvC,SAAQC,kBAAkB,QAAO,wBAAwB;AAgBzD,OAAO,SAASC,uBAAuB,EACrCC,KAAK,EACLC,SAAS,EACTC,IAAI,EACJC,aAAa,EACbC,WAAW,EACXC,gBAAgB,EAChBC,cAAc,EACdC,WAAW,EACXC,kBAAkB,EAClBC,oBAAoB,EACpBC,UAAU,EACkB;IAC5B,MAAM,EAACC,SAAS,EAAEC,OAAO,EAAC,GAAGZ;IAC7B,oFAAoF;IACpF,oFAAoF;IACpF,wFAAwF;IACxF,0EAA0E;IAC1E,MAAMa,gBAAgBD,WAAWZ,MAAMc,IAAI,KAAKC;IAChD,yFAAyF;IACzF,yFAAyF;IACzF,MAAMC,YAAY,CAACL,aAAaX,MAAMc,IAAI,KAAKC;IAC/C,4FAA4F;IAC5F,8EAA8E;IAC9E,MAAME,iBAAiBD,aAAaf,cAAc,KAAK,CAACI;IACxD,MAAMa,gBAAgBF,aAAad,KAAKiB,MAAM,KAAK,KAAK,CAACF;IAEzD,qBACE,MAACG;QAAIC,WAAU;;YACZV,0BAAY,KAACf,+BAA6B;YAC1C,CAACe,0BACA,KAACnB;0BACC,cAAA,KAACD;oBAAeS,OAAOA;oBAAOsB,SAAQ;oBAAgBC,MAAK;oBAAYC,SAAQ;;iBAE/E;YACH,CAACb,aAAaE,8BAAgB,KAAChB;gBAA0BG,OAAOA;iBAAY;YAC5EiB,+BACC,KAACzB;0BACC,cAAA,KAACC;oBAAqBU,eAAeA;oBAAeC,aAAaA;;iBAEjE;YACHc,8BACC,KAACvB;gBACC8B,SAASnB;gBACTC,aAAaA;gBACbC,oBAAoBA;gBACpBC,sBAAsBA;gBACrB,GAAIC,aAAa;oBAACA;gBAAU,IAAI,CAAC,CAAC;iBAEnC;YACH,CAACC,aAAaT,KAAKiB,MAAM,GAAG,kBAC3B;;kCACE,KAACrB;wBAAmBI,MAAMA;wBAAMC,eAAeA;wBAAeC,aAAaA;;kCAC3E,KAACV;wBACCa,aAAaA;wBACbC,oBAAoBA;wBACpBC,sBAAsBA;wBACrB,GAAIC,aAAa;4BAACA;wBAAU,IAAI,CAAC,CAAC;;;iBAGrC;;;AAGV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-run-list-states.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/workflow-run-list-states.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-run-list-states.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/workflow-run-list-states.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,oBAAoB,CAAC;AAW5D;;;;GAIG;AACH,wBAAgB,uBAAuB,gCAkBtC;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EAAC,KAAK,EAAC,EAAE;IAAC,KAAK,EAAE,mBAAmB,CAAA;CAAC,+BAqB9E;AAED,wBAAgB,oBAAoB,CAAC,EACnC,aAAa,EACb,WAAW,GACZ,EAAE;IACD,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,+BAqBA;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,WAAW,EACX,kBAAkB,EAClB,oBAAoB,EACpB,UAAU,GACX,EAAE;IACD,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,+BAwCA;AAED,wBAAgB,uBAAuB,CAAC,EACtC,WAAW,EACX,kBAAkB,EAClB,oBAAoB,EACpB,UAAU,GACX,EAAE;IACD,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,sCAqBA"}
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Button } from '@shipfox/react-ui/button';
|
|
3
3
|
import { Callout } from '@shipfox/react-ui/callout';
|
|
4
4
|
import { EmptyState } from '@shipfox/react-ui/empty-state';
|
|
5
|
+
import { Panel } from '@shipfox/react-ui/panel';
|
|
5
6
|
import { Skeleton } from '@shipfox/react-ui/skeleton';
|
|
6
7
|
import { Text } from '@shipfox/react-ui/typography';
|
|
7
8
|
import { Link } from '@tanstack/react-router';
|
|
@@ -11,14 +12,14 @@ const SKELETON_ROW_COUNT = 8;
|
|
|
11
12
|
* the first page lands. Only the initial load renders these: a refetch keeps the rows it has,
|
|
12
13
|
* per the design system's rule against animating frequently-updating data.
|
|
13
14
|
*/ export function WorkflowRunListSkeleton() {
|
|
14
|
-
return /*#__PURE__*/ _jsx(
|
|
15
|
-
className: "@container divide-y divide-border-neutral-base",
|
|
15
|
+
return /*#__PURE__*/ _jsx(Panel, {
|
|
16
16
|
role: "status",
|
|
17
17
|
"aria-label": "Loading runs",
|
|
18
|
+
className: "@container divide-y",
|
|
18
19
|
children: Array.from({
|
|
19
20
|
length: SKELETON_ROW_COUNT
|
|
20
21
|
}).map((_, index)=>/*#__PURE__*/ _jsxs("div", {
|
|
21
|
-
className: "flex items-center gap-inline px-row py-row @min-[976px]:h-44 @min-[976px]:py-0",
|
|
22
|
+
className: "flex min-h-44 items-center gap-inline px-row py-row @min-[976px]:h-44 @min-[976px]:py-0",
|
|
22
23
|
children: [
|
|
23
24
|
/*#__PURE__*/ _jsx(Skeleton, {
|
|
24
25
|
className: "size-14 shrink-0 rounded-full"
|
|
@@ -89,7 +90,8 @@ export function WorkflowRunListEmpty({ workspaceSlug, projectSlug }) {
|
|
|
89
90
|
},
|
|
90
91
|
children: "View workflows"
|
|
91
92
|
})
|
|
92
|
-
}) : null
|
|
93
|
+
}) : null,
|
|
94
|
+
variant: "panel"
|
|
93
95
|
});
|
|
94
96
|
}
|
|
95
97
|
/**
|
|
@@ -101,40 +103,43 @@ export function WorkflowRunListEmpty({ workspaceSlug, projectSlug }) {
|
|
|
101
103
|
* different ones. It is also the only reset within reach below `md`, where the toolbar's
|
|
102
104
|
* lives inside the filter sheet.
|
|
103
105
|
*/ export function WorkflowRunListNoMatches({ onClear, hasNextPage, isFetchingNextPage, isFetchNextPageError, onLoadMore }) {
|
|
104
|
-
return /*#__PURE__*/
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
106
|
+
return /*#__PURE__*/ _jsx(Panel, {
|
|
107
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
108
|
+
className: "flex flex-col gap-inline",
|
|
109
|
+
children: [
|
|
110
|
+
isFetchNextPageError ? /*#__PURE__*/ _jsx(Callout, {
|
|
111
|
+
role: "alert",
|
|
112
|
+
type: "error",
|
|
113
|
+
children: "Could not load more workflow runs. Try again to continue searching older runs."
|
|
114
|
+
}) : null,
|
|
115
|
+
/*#__PURE__*/ _jsx(EmptyState, {
|
|
116
|
+
icon: "filterOffLine",
|
|
117
|
+
title: hasNextPage ? 'No matches in loaded history' : 'No matching runs',
|
|
118
|
+
description: hasNextPage ? 'No loaded run matches these filters. Load more to search further back.' : 'No run matches these filters.',
|
|
119
|
+
action: /*#__PURE__*/ _jsxs("div", {
|
|
120
|
+
className: "flex flex-wrap justify-center gap-inline",
|
|
121
|
+
children: [
|
|
122
|
+
hasNextPage && onLoadMore ? /*#__PURE__*/ _jsx(Button, {
|
|
123
|
+
type: "button",
|
|
124
|
+
size: "sm",
|
|
125
|
+
variant: "primary",
|
|
126
|
+
isLoading: isFetchingNextPage,
|
|
127
|
+
onClick: onLoadMore,
|
|
128
|
+
children: "Load more runs"
|
|
129
|
+
}) : null,
|
|
130
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
131
|
+
type: "button",
|
|
132
|
+
size: "sm",
|
|
133
|
+
variant: "secondary",
|
|
134
|
+
onClick: onClear,
|
|
135
|
+
children: "Show all runs"
|
|
136
|
+
})
|
|
137
|
+
]
|
|
138
|
+
}),
|
|
139
|
+
variant: "panel"
|
|
135
140
|
})
|
|
136
|
-
|
|
137
|
-
|
|
141
|
+
]
|
|
142
|
+
})
|
|
138
143
|
});
|
|
139
144
|
}
|
|
140
145
|
export function WorkflowRunListLoadMore({ hasNextPage, isFetchingNextPage, isFetchNextPageError, onLoadMore }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-list/workflow-run-list-states.tsx"],"sourcesContent":["import type {QueryLoadErrorQuery} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\n\nconst SKELETON_ROW_COUNT = 8;\n\n/**\n * Placeholder rows at the real row height, so the list does not resize under the cursor when\n * the first page lands. Only the initial load renders these: a refetch keeps the rows it has,\n * per the design system's rule against animating frequently-updating data.\n */\nexport function WorkflowRunListSkeleton() {\n return (\n <
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-list/workflow-run-list-states.tsx"],"sourcesContent":["import type {QueryLoadErrorQuery} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Panel} from '@shipfox/react-ui/panel';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\n\nconst SKELETON_ROW_COUNT = 8;\n\n/**\n * Placeholder rows at the real row height, so the list does not resize under the cursor when\n * the first page lands. Only the initial load renders these: a refetch keeps the rows it has,\n * per the design system's rule against animating frequently-updating data.\n */\nexport function WorkflowRunListSkeleton() {\n return (\n <Panel role=\"status\" aria-label=\"Loading runs\" className=\"@container divide-y\">\n {Array.from({length: SKELETON_ROW_COUNT}).map((_, index) => (\n <div\n // biome-ignore lint/suspicious/noArrayIndexKey: skeleton row, stable position\n key={index}\n className=\"flex min-h-44 items-center gap-inline px-row py-row @min-[976px]:h-44 @min-[976px]:py-0\"\n >\n <Skeleton className=\"size-14 shrink-0 rounded-full\" />\n <Skeleton className=\"h-12 w-[220px] max-w-[40%]\" />\n <Skeleton className=\"ml-auto hidden h-12 w-96 @min-[1200px]:block\" />\n <Skeleton className=\"h-12 w-48\" />\n <Skeleton className=\"h-12 w-48\" />\n </div>\n ))}\n </Panel>\n );\n}\n\n/**\n * Slim non-blocking banner shown when a background refetch fails after a prior success,\n * so the already-loaded rows stay on screen with the retry inline rather than being\n * replaced by a full-placeholder error.\n */\nexport function WorkflowRunListStaleError({query}: {query: QueryLoadErrorQuery}) {\n return (\n <div className=\"pb-[8px]\">\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not refresh workflow runs.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => {\n void query.refetch();\n }}\n >\n Retry\n </Button>\n </div>\n </Callout>\n </div>\n );\n}\n\nexport function WorkflowRunListEmpty({\n workspaceSlug,\n projectSlug,\n}: {\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n}) {\n return (\n <EmptyState\n icon=\"pulseLine\"\n title=\"No runs yet\"\n description=\"Runs from this project's workflows appear here the moment one is launched.\"\n action={\n workspaceSlug && projectSlug ? (\n <Button asChild type=\"button\" size=\"sm\" variant=\"primary\">\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/workflows\"\n params={{workspaceSlug, projectSlug}}\n >\n View workflows\n </Link>\n </Button>\n ) : null\n }\n variant=\"panel\"\n />\n );\n}\n\n/**\n * The filtered-empty state, deliberately distinct from the unfiltered one: someone who\n * filtered their way to nothing needs a way back out, not an onboarding call to action.\n *\n * Its reset is worded as its outcome rather than repeating the toolbar's \"Clear filters\"\n * verbatim, so the two controls on screen read as one action offered twice rather than two\n * different ones. It is also the only reset within reach below `md`, where the toolbar's\n * lives inside the filter sheet.\n */\nexport function WorkflowRunListNoMatches({\n onClear,\n hasNextPage,\n isFetchingNextPage,\n isFetchNextPageError,\n onLoadMore,\n}: {\n onClear: () => void;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n isFetchNextPageError: boolean;\n onLoadMore?: () => void;\n}) {\n return (\n <Panel>\n <div className=\"flex flex-col gap-inline\">\n {isFetchNextPageError ? (\n <Callout role=\"alert\" type=\"error\">\n Could not load more workflow runs. Try again to continue searching older runs.\n </Callout>\n ) : null}\n <EmptyState\n icon=\"filterOffLine\"\n title={hasNextPage ? 'No matches in loaded history' : 'No matching runs'}\n description={\n hasNextPage\n ? 'No loaded run matches these filters. Load more to search further back.'\n : 'No run matches these filters.'\n }\n action={\n <div className=\"flex flex-wrap justify-center gap-inline\">\n {hasNextPage && onLoadMore ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"primary\"\n isLoading={isFetchingNextPage}\n onClick={onLoadMore}\n >\n Load more runs\n </Button>\n ) : null}\n <Button type=\"button\" size=\"sm\" variant=\"secondary\" onClick={onClear}>\n Show all runs\n </Button>\n </div>\n }\n variant=\"panel\"\n />\n </div>\n </Panel>\n );\n}\n\nexport function WorkflowRunListLoadMore({\n hasNextPage,\n isFetchingNextPage,\n isFetchNextPageError,\n onLoadMore,\n}: {\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n isFetchNextPageError: boolean;\n onLoadMore?: () => void;\n}) {\n if (!hasNextPage || !onLoadMore) return null;\n\n return (\n <div className=\"flex flex-col items-center gap-inline border-t border-border-neutral-base py-row\">\n {isFetchNextPageError ? (\n <Callout role=\"alert\" type=\"error\">\n Could not load more workflow runs. Try again.\n </Callout>\n ) : null}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n isLoading={isFetchingNextPage}\n onClick={onLoadMore}\n >\n Load more runs\n </Button>\n </div>\n );\n}\n"],"names":["Button","Callout","EmptyState","Panel","Skeleton","Text","Link","SKELETON_ROW_COUNT","WorkflowRunListSkeleton","role","aria-label","className","Array","from","length","map","_","index","div","WorkflowRunListStaleError","query","type","size","variant","isLoading","isFetching","onClick","refetch","WorkflowRunListEmpty","workspaceSlug","projectSlug","icon","title","description","action","asChild","to","params","WorkflowRunListNoMatches","onClear","hasNextPage","isFetchingNextPage","isFetchNextPageError","onLoadMore","WorkflowRunListLoadMore"],"mappings":";AACA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,UAAU,QAAO,gCAAgC;AACzD,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAE5C,MAAMC,qBAAqB;AAE3B;;;;CAIC,GACD,OAAO,SAASC;IACd,qBACE,KAACL;QAAMM,MAAK;QAASC,cAAW;QAAeC,WAAU;kBACtDC,MAAMC,IAAI,CAAC;YAACC,QAAQP;QAAkB,GAAGQ,GAAG,CAAC,CAACC,GAAGC,sBAChD,MAACC;gBAGCP,WAAU;;kCAEV,KAACP;wBAASO,WAAU;;kCACpB,KAACP;wBAASO,WAAU;;kCACpB,KAACP;wBAASO,WAAU;;kCACpB,KAACP;wBAASO,WAAU;;kCACpB,KAACP;wBAASO,WAAU;;;eAPfM;;AAYf;AAEA;;;;CAIC,GACD,OAAO,SAASE,0BAA0B,EAACC,KAAK,EAA+B;IAC7E,qBACE,KAACF;QAAIP,WAAU;kBACb,cAAA,KAACV;YAAQQ,MAAK;YAAQY,MAAK;sBACzB,cAAA,MAACH;gBAAIP,WAAU;;kCACb,KAACN;wBAAKiB,MAAK;kCAAK;;kCAChB,KAACtB;wBACCqB,MAAK;wBACLC,MAAK;wBACLC,SAAQ;wBACRC,WAAWJ,MAAMK,UAAU;wBAC3BC,SAAS;4BACP,KAAKN,MAAMO,OAAO;wBACpB;kCACD;;;;;;AAOX;AAEA,OAAO,SAASC,qBAAqB,EACnCC,aAAa,EACbC,WAAW,EAIZ;IACC,qBACE,KAAC5B;QACC6B,MAAK;QACLC,OAAM;QACNC,aAAY;QACZC,QACEL,iBAAiBC,4BACf,KAAC9B;YAAOmC,OAAO;YAACd,MAAK;YAASC,MAAK;YAAKC,SAAQ;sBAC9C,cAAA,KAACjB;gBACC8B,IAAG;gBACHC,QAAQ;oBAACR;oBAAeC;gBAAW;0BACpC;;aAID;QAENP,SAAQ;;AAGd;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASe,yBAAyB,EACvCC,OAAO,EACPC,WAAW,EACXC,kBAAkB,EAClBC,oBAAoB,EACpBC,UAAU,EAOX;IACC,qBACE,KAACxC;kBACC,cAAA,MAACe;YAAIP,WAAU;;gBACZ+B,qCACC,KAACzC;oBAAQQ,MAAK;oBAAQY,MAAK;8BAAQ;qBAGjC;8BACJ,KAACnB;oBACC6B,MAAK;oBACLC,OAAOQ,cAAc,iCAAiC;oBACtDP,aACEO,cACI,2EACA;oBAENN,sBACE,MAAChB;wBAAIP,WAAU;;4BACZ6B,eAAeG,2BACd,KAAC3C;gCACCqB,MAAK;gCACLC,MAAK;gCACLC,SAAQ;gCACRC,WAAWiB;gCACXf,SAASiB;0CACV;iCAGC;0CACJ,KAAC3C;gCAAOqB,MAAK;gCAASC,MAAK;gCAAKC,SAAQ;gCAAYG,SAASa;0CAAS;;;;oBAK1EhB,SAAQ;;;;;AAKlB;AAEA,OAAO,SAASqB,wBAAwB,EACtCJ,WAAW,EACXC,kBAAkB,EAClBC,oBAAoB,EACpBC,UAAU,EAMX;IACC,IAAI,CAACH,eAAe,CAACG,YAAY,OAAO;IAExC,qBACE,MAACzB;QAAIP,WAAU;;YACZ+B,qCACC,KAACzC;gBAAQQ,MAAK;gBAAQY,MAAK;0BAAQ;iBAGjC;0BACJ,KAACrB;gBACCqB,MAAK;gBACLC,MAAK;gBACLC,SAAQ;gBACRC,WAAWiB;gBACXf,SAASiB;0BACV;;;;AAKP"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-run-row.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/workflow-run-row.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workflow-run-row.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/workflow-run-row.tsx"],"names":[],"mappings":"AAkBA,OAAO,EACL,KAAK,mBAAmB,EAIzB,MAAM,uBAAuB,CAAC;AAI/B,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,aAAa,EACb,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,mBAAmB,EAAE,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,+BAeA;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,aAAa,EACb,WAAW,GACZ,EAAE;IACD,GAAG,EAAE,mBAAmB,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,+BAgGA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { TriggerSourceIcon } from '@shipfox/client-triggers';
|
|
3
3
|
import { Icon } from '@shipfox/react-ui/icon';
|
|
4
|
+
import { Panel } from '@shipfox/react-ui/panel';
|
|
4
5
|
import { RelativeTime } from '@shipfox/react-ui/relative-time';
|
|
5
6
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@shipfox/react-ui/tooltip';
|
|
6
7
|
import { Code, Text } from '@shipfox/react-ui/typography';
|
|
@@ -17,15 +18,18 @@ export function WorkflowRunRowList({ runs, workspaceSlug, projectSlug }) {
|
|
|
17
18
|
return(// A container, not the viewport, drives the row's breakpoints. What a row can afford is its
|
|
18
19
|
// own width; keying off the viewport would keep the job strip hidden on a wide screen or
|
|
19
20
|
// crush it on a narrow one.
|
|
20
|
-
/*#__PURE__*/ _jsx(
|
|
21
|
-
className: "@container
|
|
22
|
-
children:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
/*#__PURE__*/ _jsx(Panel, {
|
|
22
|
+
className: "@container",
|
|
23
|
+
children: /*#__PURE__*/ _jsx("ul", {
|
|
24
|
+
className: "divide-y divide-border-neutral-base",
|
|
25
|
+
children: runs.map((run)=>/*#__PURE__*/ _jsx("li", {
|
|
26
|
+
children: /*#__PURE__*/ _jsx(WorkflowRunRow, {
|
|
27
|
+
run: run,
|
|
28
|
+
workspaceSlug: workspaceSlug,
|
|
29
|
+
projectSlug: projectSlug
|
|
30
|
+
})
|
|
31
|
+
}, run.id))
|
|
32
|
+
})
|
|
29
33
|
}));
|
|
30
34
|
}
|
|
31
35
|
/**
|
|
@@ -125,7 +129,7 @@ export function WorkflowRunRowList({ runs, workspaceSlug, projectSlug }) {
|
|
|
125
129
|
});
|
|
126
130
|
// Rows run edge to edge inside the list's scroll container, which would clip the standard
|
|
127
131
|
// outset focus ring, so this one is inset per the design system's focus-ring rule.
|
|
128
|
-
const rowClassName = 'flex w-full min-w-0 items-center gap-inline px-row py-row text-left transition-colors hover:bg-background-neutral-hover focus-visible:shadow-
|
|
132
|
+
const rowClassName = 'flex w-full min-w-0 items-center gap-inline px-row py-row text-left transition-colors hover:bg-background-neutral-hover focus-visible:shadow-focus-inset focus-visible:outline-none @min-[976px]:h-44 @min-[976px]:py-0';
|
|
129
133
|
// Optimistic manual runs (temp-<uuid>) have no detail page until the canonical row
|
|
130
134
|
// replaces them on the next poll, so they render non-interactively instead of as a link
|
|
131
135
|
// that would navigate to a workflow run id the detail route rejects.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-list/workflow-run-row.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {\n useWorkflowRunDurationAccessibleLabel,\n WorkflowRunDurationLabel,\n} from '#components/workflow-run-duration-label.js';\nimport {\n formatWorkflowRunNumberLabel,\n WorkflowRunNumberLabel,\n} from '#components/workflow-run-number-label.js';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n type WorkflowRunListItem,\n workflowRunActor,\n workflowRunBranchLabel,\n workflowRunCommitLabel,\n} from '#core/workflow-run.js';\nimport {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';\nimport {JOB_STATUS_STRIP_WIDTH, JobStatusStrip, jobStatusSummary} from './job-status-strip.js';\n\nexport function WorkflowRunRowList({\n runs,\n workspaceSlug,\n projectSlug,\n}: {\n runs: WorkflowRunListItem[];\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n}) {\n return (\n // A container, not the viewport, drives the row's breakpoints. What a row can afford is its\n // own width; keying off the viewport would keep the job strip hidden on a wide screen or\n // crush it on a narrow one.\n <ul className=\"@container divide-y divide-border-neutral-base\">\n {runs.map((run) => (\n <li key={run.id}>\n <WorkflowRunRow run={run} workspaceSlug={workspaceSlug} projectSlug={projectSlug} />\n </li>\n ))}\n </ul>\n );\n}\n\n/**\n * One run at full width.\n *\n * A single 44px line once the list is at least 976px wide, reflowing to two lines below that:\n * the identity and the numerics keep their places on line one, and the provenance metadata\n * drops beneath. The numeric columns are fixed-width so duration and time form real columns\n * down the list rather than tracking each row's name length.\n *\n * The 976px threshold keeps identity and provenance on one line; below it, provenance drops\n * beneath the identity. The 1200px threshold keeps the full job preview and numeric columns\n * readable together; below it, the strip is hidden so the numeric columns remain aligned.\n */\nexport function WorkflowRunRow({\n run,\n workspaceSlug,\n projectSlug,\n}: {\n run: WorkflowRunListItem;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n}) {\n const duration = run.runAttempt.displayDuration;\n const hasStarted = run.jobs.hasStartedJobExecution;\n const durationLabel = useWorkflowRunDurationAccessibleLabel(duration, hasStarted);\n const status = run.runAttempt.status;\n const statusLabel = getWorkflowStatusVisual(status).label;\n const runNumberLabel = formatWorkflowRunNumberLabel(run);\n const branch = workflowRunBranchLabel(run);\n const commit = workflowRunCommitLabel(run);\n const actor = workflowRunActor(run);\n\n const body = (\n <>\n <WorkflowStatusIcon status={status} size={14} className=\"shrink-0\" />\n\n <div className=\"flex min-w-0 flex-1 flex-col gap-tight @min-[976px]:flex-row @min-[976px]:items-center @min-[976px]:gap-cluster\">\n <span className=\"flex min-w-0 items-center gap-inline @min-[976px]:flex-1\">\n <Code variant=\"label\" bold className=\"truncate text-foreground-neutral-base\">\n {run.name}\n </Code>\n {run.number !== null ? <WorkflowRunNumberLabel run={run} /> : null}\n </span>\n\n <span className=\"flex min-w-0 flex-wrap items-center gap-inline text-foreground-neutral-subtle @min-[976px]:flex-nowrap @min-[976px]:shrink-0\">\n {run.triggerDisplayLabel ? <TriggerLabel run={run} /> : null}\n {branch ? <BranchLabel branch={branch} isPullRequest={branch.startsWith('#')} /> : null}\n {commit ? <CommitLabel commit={commit} /> : null}\n {actor ? <ActorLabel actor={actor} /> : null}\n </span>\n </div>\n\n <span className=\"flex shrink-0 items-center gap-cluster\">\n {/* Reserve the API preview's full width so the overflow count cannot paint over the\n duration, and keep it even when a run has no jobs planned yet so the numerics stay in\n line down the list instead of stepping in and out. The max-content width expands if\n the payload ever exceeds the current API preview bound. */}\n <span\n className=\"hidden w-max shrink-0 @min-[1200px]:flex\"\n style={{minWidth: JOB_STATUS_STRIP_WIDTH}}\n >\n <JobStatusStrip jobs={run.jobs} />\n </span>\n <span className=\"flex w-64 justify-end\">\n <WorkflowRunDurationLabel duration={duration} hasStarted={hasStarted} />\n </span>\n <Code\n variant=\"label\"\n className=\"w-64 shrink-0 text-right tabular-nums text-foreground-neutral-muted\"\n >\n <RelativeTime value={run.createdAt} />\n </Code>\n </span>\n </>\n );\n\n // Rows run edge to edge inside the list's scroll container, which would clip the standard\n // outset focus ring, so this one is inset per the design system's focus-ring rule.\n const rowClassName =\n 'flex w-full min-w-0 items-center gap-inline px-row py-row text-left transition-colors hover:bg-background-neutral-hover focus-visible:shadow-[inset_0_0_0_2px_var(--color-primary-500)] focus-visible:outline-none @min-[976px]:h-44 @min-[976px]:py-0';\n\n // Optimistic manual runs (temp-<uuid>) have no detail page until the canonical row\n // replaces them on the next poll, so they render non-interactively instead of as a link\n // that would navigate to a workflow run id the detail route rejects.\n if (run.isTemporary || !workspaceSlug || !projectSlug) {\n return <div className={rowClassName}>{body}</div>;\n }\n\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId: run.id}}\n search={\n ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never\n }\n // An aria-label is the link's whole accessible name, so anything inside the row that\n // carries its own label (the status glyph, the job strip) goes unspoken unless it is\n // curated in here. The job breakdown is the reason this list is worth its width, so it\n // belongs in what a tabbing user hears, not only in browse mode.\n aria-label={[\n run.name,\n runNumberLabel,\n statusLabel,\n durationLabel,\n run.triggerLabel,\n branch ? `branch ${branch}` : undefined,\n actor ? `by ${actor}` : undefined,\n run.jobs.total > 0 ? jobStatusSummary(run.jobs) : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ')}\n className={rowClassName}\n >\n {body}\n </Link>\n );\n}\n\nfunction TriggerLabel({run}: {run: WorkflowRunListItem}) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"flex min-w-0 max-w-[140px] items-center gap-tight outline-none\">\n <TriggerSourceIcon\n provider={run.triggerProvider}\n source={run.triggerSource}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0 text-foreground-neutral-muted\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {run.triggerDisplayLabel}\n </Text>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {run.triggerLabel}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction BranchLabel({branch, isPullRequest}: {branch: string; isPullRequest: boolean}) {\n return (\n <MetadataChip\n icon={isPullRequest ? 'gitPullRequestLine' : 'gitBranchLine'}\n title={isPullRequest ? `Pull request ${branch}` : `Branch ${branch}`}\n className=\"max-w-[180px]\"\n >\n {branch}\n </MetadataChip>\n );\n}\n\nfunction CommitLabel({commit}: {commit: string}) {\n return (\n <MetadataChip icon=\"gitCommitLine\" title={`Commit ${commit}`}>\n {commit}\n </MetadataChip>\n );\n}\n\nfunction ActorLabel({actor}: {actor: string}) {\n return (\n <MetadataChip\n icon=\"userLine\"\n title={`Triggered by ${actor}`}\n // Dropped once the row is two lines, where the provenance line has to stay readable on\n // a phone.\n className=\"hidden max-w-[120px] @min-[976px]:flex\"\n >\n {actor}\n </MetadataChip>\n );\n}\n\n/**\n * A ref, SHA, or handle: monospace because it is content a user copies or pattern-matches,\n * with the icon carrying the kind so the value itself never needs a written label.\n */\nfunction MetadataChip({\n icon,\n title,\n className,\n children,\n}: {\n icon: IconName;\n title: string;\n className?: string | undefined;\n children: string;\n}) {\n return (\n <span className={cn('flex min-w-0 shrink-0 items-center gap-tight', className)} title={title}>\n <Icon name={icon} className=\"size-12 shrink-0 text-foreground-neutral-muted\" aria-hidden />\n <Code as=\"span\" variant=\"label\" className=\"min-w-0 truncate\">\n {children}\n </Code>\n </span>\n );\n}\n"],"names":["TriggerSourceIcon","Icon","RelativeTime","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","Link","useWorkflowRunDurationAccessibleLabel","WorkflowRunDurationLabel","formatWorkflowRunNumberLabel","WorkflowRunNumberLabel","getWorkflowStatusVisual","WorkflowStatusIcon","workflowRunActor","workflowRunBranchLabel","workflowRunCommitLabel","withoutWorkflowRunSelectionSearch","JOB_STATUS_STRIP_WIDTH","JobStatusStrip","jobStatusSummary","WorkflowRunRowList","runs","workspaceSlug","projectSlug","ul","className","map","run","li","WorkflowRunRow","id","duration","runAttempt","displayDuration","hasStarted","jobs","hasStartedJobExecution","durationLabel","status","statusLabel","label","runNumberLabel","branch","commit","actor","body","size","div","span","variant","bold","name","number","triggerDisplayLabel","TriggerLabel","BranchLabel","isPullRequest","startsWith","CommitLabel","ActorLabel","style","minWidth","value","createdAt","rowClassName","isTemporary","to","params","workflowRunId","search","previous","aria-label","triggerLabel","undefined","total","filter","part","Boolean","join","asChild","provider","triggerProvider","source","triggerSource","aria-hidden","as","MetadataChip","icon","title","children"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-list/workflow-run-row.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {Panel} from '@shipfox/react-ui/panel';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {\n useWorkflowRunDurationAccessibleLabel,\n WorkflowRunDurationLabel,\n} from '#components/workflow-run-duration-label.js';\nimport {\n formatWorkflowRunNumberLabel,\n WorkflowRunNumberLabel,\n} from '#components/workflow-run-number-label.js';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n type WorkflowRunListItem,\n workflowRunActor,\n workflowRunBranchLabel,\n workflowRunCommitLabel,\n} from '#core/workflow-run.js';\nimport {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';\nimport {JOB_STATUS_STRIP_WIDTH, JobStatusStrip, jobStatusSummary} from './job-status-strip.js';\n\nexport function WorkflowRunRowList({\n runs,\n workspaceSlug,\n projectSlug,\n}: {\n runs: WorkflowRunListItem[];\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n}) {\n return (\n // A container, not the viewport, drives the row's breakpoints. What a row can afford is its\n // own width; keying off the viewport would keep the job strip hidden on a wide screen or\n // crush it on a narrow one.\n <Panel className=\"@container\">\n <ul className=\"divide-y divide-border-neutral-base\">\n {runs.map((run) => (\n <li key={run.id}>\n <WorkflowRunRow run={run} workspaceSlug={workspaceSlug} projectSlug={projectSlug} />\n </li>\n ))}\n </ul>\n </Panel>\n );\n}\n\n/**\n * One run at full width.\n *\n * A single 44px line once the list is at least 976px wide, reflowing to two lines below that:\n * the identity and the numerics keep their places on line one, and the provenance metadata\n * drops beneath. The numeric columns are fixed-width so duration and time form real columns\n * down the list rather than tracking each row's name length.\n *\n * The 976px threshold keeps identity and provenance on one line; below it, provenance drops\n * beneath the identity. The 1200px threshold keeps the full job preview and numeric columns\n * readable together; below it, the strip is hidden so the numeric columns remain aligned.\n */\nexport function WorkflowRunRow({\n run,\n workspaceSlug,\n projectSlug,\n}: {\n run: WorkflowRunListItem;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n}) {\n const duration = run.runAttempt.displayDuration;\n const hasStarted = run.jobs.hasStartedJobExecution;\n const durationLabel = useWorkflowRunDurationAccessibleLabel(duration, hasStarted);\n const status = run.runAttempt.status;\n const statusLabel = getWorkflowStatusVisual(status).label;\n const runNumberLabel = formatWorkflowRunNumberLabel(run);\n const branch = workflowRunBranchLabel(run);\n const commit = workflowRunCommitLabel(run);\n const actor = workflowRunActor(run);\n\n const body = (\n <>\n <WorkflowStatusIcon status={status} size={14} className=\"shrink-0\" />\n\n <div className=\"flex min-w-0 flex-1 flex-col gap-tight @min-[976px]:flex-row @min-[976px]:items-center @min-[976px]:gap-cluster\">\n <span className=\"flex min-w-0 items-center gap-inline @min-[976px]:flex-1\">\n <Code variant=\"label\" bold className=\"truncate text-foreground-neutral-base\">\n {run.name}\n </Code>\n {run.number !== null ? <WorkflowRunNumberLabel run={run} /> : null}\n </span>\n\n <span className=\"flex min-w-0 flex-wrap items-center gap-inline text-foreground-neutral-subtle @min-[976px]:flex-nowrap @min-[976px]:shrink-0\">\n {run.triggerDisplayLabel ? <TriggerLabel run={run} /> : null}\n {branch ? <BranchLabel branch={branch} isPullRequest={branch.startsWith('#')} /> : null}\n {commit ? <CommitLabel commit={commit} /> : null}\n {actor ? <ActorLabel actor={actor} /> : null}\n </span>\n </div>\n\n <span className=\"flex shrink-0 items-center gap-cluster\">\n {/* Reserve the API preview's full width so the overflow count cannot paint over the\n duration, and keep it even when a run has no jobs planned yet so the numerics stay in\n line down the list instead of stepping in and out. The max-content width expands if\n the payload ever exceeds the current API preview bound. */}\n <span\n className=\"hidden w-max shrink-0 @min-[1200px]:flex\"\n style={{minWidth: JOB_STATUS_STRIP_WIDTH}}\n >\n <JobStatusStrip jobs={run.jobs} />\n </span>\n <span className=\"flex w-64 justify-end\">\n <WorkflowRunDurationLabel duration={duration} hasStarted={hasStarted} />\n </span>\n <Code\n variant=\"label\"\n className=\"w-64 shrink-0 text-right tabular-nums text-foreground-neutral-muted\"\n >\n <RelativeTime value={run.createdAt} />\n </Code>\n </span>\n </>\n );\n\n // Rows run edge to edge inside the list's scroll container, which would clip the standard\n // outset focus ring, so this one is inset per the design system's focus-ring rule.\n const rowClassName =\n 'flex w-full min-w-0 items-center gap-inline px-row py-row text-left transition-colors hover:bg-background-neutral-hover focus-visible:shadow-focus-inset focus-visible:outline-none @min-[976px]:h-44 @min-[976px]:py-0';\n\n // Optimistic manual runs (temp-<uuid>) have no detail page until the canonical row\n // replaces them on the next poll, so they render non-interactively instead of as a link\n // that would navigate to a workflow run id the detail route rejects.\n if (run.isTemporary || !workspaceSlug || !projectSlug) {\n return <div className={rowClassName}>{body}</div>;\n }\n\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId: run.id}}\n search={\n ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never\n }\n // An aria-label is the link's whole accessible name, so anything inside the row that\n // carries its own label (the status glyph, the job strip) goes unspoken unless it is\n // curated in here. The job breakdown is the reason this list is worth its width, so it\n // belongs in what a tabbing user hears, not only in browse mode.\n aria-label={[\n run.name,\n runNumberLabel,\n statusLabel,\n durationLabel,\n run.triggerLabel,\n branch ? `branch ${branch}` : undefined,\n actor ? `by ${actor}` : undefined,\n run.jobs.total > 0 ? jobStatusSummary(run.jobs) : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ')}\n className={rowClassName}\n >\n {body}\n </Link>\n );\n}\n\nfunction TriggerLabel({run}: {run: WorkflowRunListItem}) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"flex min-w-0 max-w-[140px] items-center gap-tight outline-none\">\n <TriggerSourceIcon\n provider={run.triggerProvider}\n source={run.triggerSource}\n aria-hidden=\"true\"\n className=\"size-12 shrink-0 text-foreground-neutral-muted\"\n />\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate\">\n {run.triggerDisplayLabel}\n </Text>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n <Text as=\"span\" size=\"xs\" className=\"block max-w-[360px] break-words\">\n {run.triggerLabel}\n </Text>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction BranchLabel({branch, isPullRequest}: {branch: string; isPullRequest: boolean}) {\n return (\n <MetadataChip\n icon={isPullRequest ? 'gitPullRequestLine' : 'gitBranchLine'}\n title={isPullRequest ? `Pull request ${branch}` : `Branch ${branch}`}\n className=\"max-w-[180px]\"\n >\n {branch}\n </MetadataChip>\n );\n}\n\nfunction CommitLabel({commit}: {commit: string}) {\n return (\n <MetadataChip icon=\"gitCommitLine\" title={`Commit ${commit}`}>\n {commit}\n </MetadataChip>\n );\n}\n\nfunction ActorLabel({actor}: {actor: string}) {\n return (\n <MetadataChip\n icon=\"userLine\"\n title={`Triggered by ${actor}`}\n // Dropped once the row is two lines, where the provenance line has to stay readable on\n // a phone.\n className=\"hidden max-w-[120px] @min-[976px]:flex\"\n >\n {actor}\n </MetadataChip>\n );\n}\n\n/**\n * A ref, SHA, or handle: monospace because it is content a user copies or pattern-matches,\n * with the icon carrying the kind so the value itself never needs a written label.\n */\nfunction MetadataChip({\n icon,\n title,\n className,\n children,\n}: {\n icon: IconName;\n title: string;\n className?: string | undefined;\n children: string;\n}) {\n return (\n <span className={cn('flex min-w-0 shrink-0 items-center gap-tight', className)} title={title}>\n <Icon name={icon} className=\"size-12 shrink-0 text-foreground-neutral-muted\" aria-hidden />\n <Code as=\"span\" variant=\"label\" className=\"min-w-0 truncate\">\n {children}\n </Code>\n </span>\n );\n}\n"],"names":["TriggerSourceIcon","Icon","Panel","RelativeTime","Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","Link","useWorkflowRunDurationAccessibleLabel","WorkflowRunDurationLabel","formatWorkflowRunNumberLabel","WorkflowRunNumberLabel","getWorkflowStatusVisual","WorkflowStatusIcon","workflowRunActor","workflowRunBranchLabel","workflowRunCommitLabel","withoutWorkflowRunSelectionSearch","JOB_STATUS_STRIP_WIDTH","JobStatusStrip","jobStatusSummary","WorkflowRunRowList","runs","workspaceSlug","projectSlug","className","ul","map","run","li","WorkflowRunRow","id","duration","runAttempt","displayDuration","hasStarted","jobs","hasStartedJobExecution","durationLabel","status","statusLabel","label","runNumberLabel","branch","commit","actor","body","size","div","span","variant","bold","name","number","triggerDisplayLabel","TriggerLabel","BranchLabel","isPullRequest","startsWith","CommitLabel","ActorLabel","style","minWidth","value","createdAt","rowClassName","isTemporary","to","params","workflowRunId","search","previous","aria-label","triggerLabel","undefined","total","filter","part","Boolean","join","asChild","provider","triggerProvider","source","triggerSource","aria-hidden","as","MetadataChip","icon","title","children"],"mappings":"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"}
|
|
@@ -16,7 +16,7 @@ import { WorkflowRunDurationLabel } from '../workflow-run-duration-label.js';
|
|
|
16
16
|
import { getWorkflowStatusVisual } from '../workflow-status/status-visuals.js';
|
|
17
17
|
import { WorkflowRunAttemptSwitcher } from './workflow-run-attempt-switcher.js';
|
|
18
18
|
const STATUS_BADGE_LABEL_WIDTH_CH = Math.max(...WORKFLOW_RUN_STATUSES.map((status)=>getWorkflowStatusVisual(status).label.length));
|
|
19
|
-
const
|
|
19
|
+
const NEUTRAL_ACTION_SURFACE_CLASS_NAME = 'bg-background-neutral-base hover:bg-background-neutral-hover active:bg-background-neutral-pressed';
|
|
20
20
|
export function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling = false, onCancel, rerunPending = false, onRerun, latestAttempt }) {
|
|
21
21
|
const headingId = useId();
|
|
22
22
|
const status = getWorkflowStatusVisual(run.runAttempt.status);
|
|
@@ -195,7 +195,7 @@ function WorkflowRunActionSlot({ action, cancelling, onCancel, rerunPending, onR
|
|
|
195
195
|
type: "button",
|
|
196
196
|
variant: "secondary",
|
|
197
197
|
size: "xs",
|
|
198
|
-
className:
|
|
198
|
+
className: NEUTRAL_ACTION_SURFACE_CLASS_NAME,
|
|
199
199
|
isLoading: rerunPending,
|
|
200
200
|
disabled: rerunPending,
|
|
201
201
|
onClick: ()=>onRerun('all'),
|
|
@@ -211,7 +211,7 @@ function WorkflowRunActionSlot({ action, cancelling, onCancel, rerunPending, onR
|
|
|
211
211
|
type: "button",
|
|
212
212
|
variant: "secondary",
|
|
213
213
|
size: "xs",
|
|
214
|
-
className:
|
|
214
|
+
className: NEUTRAL_ACTION_SURFACE_CLASS_NAME,
|
|
215
215
|
iconRight: "arrowDownSLine",
|
|
216
216
|
isLoading: rerunPending,
|
|
217
217
|
disabled: rerunPending,
|