1+ import { useMutation } from "@tanstack/react-query" ;
12import { useCallback , useMemo , useState } from "react" ;
23
4+ import { PipelineRunsList } from "@/components/shared/PipelineRunDisplay/PipelineRunsList" ;
35import { typeSpecToString } from "@/components/shared/ReactFlow/FlowCanvas/TaskNode/ArgumentsEditor/utils" ;
46import { getArgumentsFromInputs } from "@/components/shared/ReactFlow/FlowCanvas/utils/getArgumentsFromInputs" ;
57import { Button } from "@/components/ui/button" ;
@@ -11,12 +13,22 @@ import {
1113 DialogHeader ,
1214 DialogTitle ,
1315} from "@/components/ui/dialog" ;
16+ import { Icon } from "@/components/ui/icon" ;
1417import { Input } from "@/components/ui/input" ;
1518import { BlockStack , InlineStack } from "@/components/ui/layout" ;
19+ import {
20+ Popover ,
21+ PopoverContent ,
22+ PopoverTrigger ,
23+ } from "@/components/ui/popover" ;
1624import { ScrollArea } from "@/components/ui/scroll-area" ;
1725import { Paragraph } from "@/components/ui/typography" ;
1826import { cn } from "@/lib/utils" ;
27+ import { useBackend } from "@/providers/BackendProvider" ;
28+ import { fetchExecutionDetails } from "@/services/executionService" ;
29+ import type { PipelineRun } from "@/types/pipelineRun" ;
1930import type { ComponentSpec , InputSpec } from "@/utils/componentSpec" ;
31+ import { getArgumentValue } from "@/utils/nodes/taskArguments" ;
2032
2133interface SubmitTaskArgumentsDialogProps {
2234 open : boolean ;
@@ -64,14 +76,28 @@ export const SubmitTaskArgumentsDialog = ({
6476 < DialogHeader >
6577 < DialogTitle > Submit Run with Arguments</ DialogTitle >
6678 < DialogDescription >
67- { hasInputs
68- ? "Customize the pipeline input values before submitting."
69- : "This pipeline has no configurable inputs." }
79+ { hasInputs ? (
80+ < BlockStack gap = "2" >
81+ < Paragraph tone = "subdued" >
82+ Customize the pipeline input values before submitting.
83+ </ Paragraph >
84+ < InlineStack align = "end" className = "w-full" >
85+ < CopyFromRunPopover
86+ componentSpec = { componentSpec }
87+ onCopy = { setTaskArguments }
88+ />
89+ </ InlineStack >
90+ </ BlockStack >
91+ ) : (
92+ < Paragraph tone = "subdued" >
93+ This pipeline has no configurable inputs.
94+ </ Paragraph >
95+ ) }
7096 </ DialogDescription >
7197 </ DialogHeader >
7298
7399 { hasInputs && (
74- < ScrollArea className = "max-h-[60vh] pr-4" >
100+ < ScrollArea className = "max-h-[60vh] pr-4 w-full " >
75101 < BlockStack gap = "4" >
76102 { inputs . map ( ( input ) => (
77103 < ArgumentField
@@ -96,6 +122,71 @@ export const SubmitTaskArgumentsDialog = ({
96122 ) ;
97123} ;
98124
125+ const CopyFromRunPopover = ( {
126+ componentSpec,
127+ onCopy,
128+ } : {
129+ componentSpec : ComponentSpec ;
130+ onCopy : ( args : Record < string , string > ) => void ;
131+ } ) => {
132+ const { backendUrl } = useBackend ( ) ;
133+ const pipelineName = componentSpec . name ;
134+
135+ const [ popoverOpen , setPopoverOpen ] = useState ( false ) ;
136+
137+ const { mutate : copyFromRunMutation , isPending : isCopyingFromRun } =
138+ useMutation ( {
139+ mutationFn : async ( run : PipelineRun ) => {
140+ const executionDetails = await fetchExecutionDetails (
141+ String ( run . root_execution_id ) ,
142+ backendUrl ,
143+ ) ;
144+ return executionDetails . task_spec . arguments ;
145+ } ,
146+ onSuccess : ( runArguments ) => {
147+ if ( runArguments ) {
148+ const newArgs = Object . fromEntries (
149+ Object . entries ( runArguments )
150+ . map ( ( [ name , _ ] ) => [ name , getArgumentValue ( runArguments , name ) ] )
151+ . filter (
152+ ( entry ) : entry is [ string , string ] => entry [ 1 ] !== undefined ,
153+ ) ,
154+ ) ;
155+ onCopy ( newArgs ) ;
156+ }
157+ setPopoverOpen ( false ) ;
158+ } ,
159+ onError : ( error ) => {
160+ console . error ( "Failed to fetch run arguments:" , error ) ;
161+ setPopoverOpen ( false ) ;
162+ } ,
163+ } ) ;
164+
165+ return (
166+ < Popover open = { popoverOpen } onOpenChange = { setPopoverOpen } >
167+ < PopoverTrigger asChild >
168+ < Button variant = "link" size = "sm" >
169+ < Icon name = "Copy" />
170+ Copy from recent run
171+ </ Button >
172+ </ PopoverTrigger >
173+ < PopoverContent className = "w-100" align = "end" >
174+ < PipelineRunsList
175+ pipelineName = { pipelineName }
176+ onRunClick = { copyFromRunMutation }
177+ showTitle = { false }
178+ showMoreButton = { true }
179+ overviewConfig = { {
180+ showName : false ,
181+ showTaskStatusBar : false ,
182+ } }
183+ disabled = { isCopyingFromRun }
184+ />
185+ </ PopoverContent >
186+ </ Popover >
187+ ) ;
188+ } ;
189+
99190interface ArgumentFieldProps {
100191 input : InputSpec ;
101192 value : string ;
0 commit comments