11import { DialogClose } from "@radix-ui/react-dialog" ;
22import { Form , useNavigation , useSubmit } from "@remix-run/react" ;
3- import { useCallback , useEffect , useRef } from "react" ;
3+ import { useCallback , useEffect , useRef , useState } from "react" ;
44import { type UseDataFunctionReturn , useTypedFetcher } from "remix-typedjson" ;
55import { JSONEditor } from "~/components/code/JSONEditor" ;
66import { EnvironmentCombo } from "~/components/environments/EnvironmentLabel" ;
@@ -12,6 +12,7 @@ import { Label } from "~/components/primitives/Label";
1212import { Paragraph } from "~/components/primitives/Paragraph" ;
1313import { Select , SelectItem } from "~/components/primitives/Select" ;
1414import { Spinner , SpinnerWhite } from "~/components/primitives/Spinner" ;
15+ import { TabButton , TabContainer } from "~/components/primitives/Tabs" ;
1516import { type loader } from "~/routes/resources.taskruns.$runParam.replay" ;
1617
1718type ReplayRunDialogProps = {
@@ -21,7 +22,7 @@ type ReplayRunDialogProps = {
2122
2223export function ReplayRunDialog ( { runFriendlyId, failedRedirect } : ReplayRunDialogProps ) {
2324 return (
24- < DialogContent key = { `replay` } className = "md:max-w-xl " >
25+ < DialogContent key = { `replay` } className = "h-full md:max-h-[85vh] md:max-w-3xl lg:max-w-5xl " >
2526 < ReplayContent runFriendlyId = { runFriendlyId } failedRedirect = { failedRedirect } />
2627 </ DialogContent >
2728 ) ;
@@ -36,7 +37,7 @@ function ReplayContent({ runFriendlyId, failedRedirect }: ReplayRunDialogProps)
3637 } , [ runFriendlyId ] ) ;
3738
3839 return (
39- < >
40+ < div className = "flex h-full flex-col" >
4041 < DialogHeader > Replay this run</ DialogHeader >
4142 { isLoading ? (
4243 < div className = "grid place-items-center p-6" >
@@ -51,7 +52,7 @@ function ReplayContent({ runFriendlyId, failedRedirect }: ReplayRunDialogProps)
5152 ) : (
5253 < > Failed to get run data</ >
5354 ) }
54- </ >
55+ </ div >
5556 ) ;
5657}
5758
@@ -93,82 +94,112 @@ function ReplayForm({
9394 [ currentJson ]
9495 ) ;
9596
97+ const [ tab , setTab ] = useState < "payload" | "metadata" > ( "payload" ) ;
98+
9699 return (
97- < Form action = { formAction } method = "post" onSubmit = { ( e ) => submitForm ( e ) } className = "pt-2" >
98- { editablePayload ? (
99- < >
100- < Paragraph className = "mb-3" >
101- Replaying will create a new run using the same or modified payload, executing against
102- the latest version in your selected environment.
103- </ Paragraph >
104- < Header3 spacing > Payload</ Header3 >
105- < div className = "mb-3 max-h-[70vh] min-h-40 overflow-y-auto rounded-sm border border-grid-dimmed bg-charcoal-900 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600" >
106- < JSONEditor
107- autoFocus
108- defaultValue = { currentJson . current }
109- readOnly = { false }
110- basicSetup
111- onChange = { ( v ) => {
112- currentJson . current = v ;
113- } }
114- showClearButton = { false }
115- showCopyButton = { false }
116- height = "100%"
117- min-height = "100%"
118- max-height = "100%"
119- />
120- </ div >
121- </ >
122- ) : null }
123- < InputGroup >
124- < Label > Environment</ Label >
125- < Select
126- id = "environment"
127- name = "environment"
128- placeholder = "Select an environment"
129- defaultValue = { environment . id }
130- items = { environments }
131- dropdownIcon
132- variant = "tertiary/medium"
133- className = "w-fit pl-1"
134- filter = { {
135- keys : [
136- ( item ) => item . type . replace ( / \/ / g, " " ) . replace ( / _ / g, " " ) ,
137- ( item ) => item . branchName ?. replace ( / \/ / g, " " ) . replace ( / _ / g, " " ) ?? "" ,
138- ] ,
139- } }
140- text = { ( value ) => {
141- const env = environments . find ( ( env ) => env . id === value ) ! ;
142- return (
143- < div className = "flex items-center pl-1 pr-2" >
144- < EnvironmentCombo environment = { env } />
145- </ div >
146- ) ;
147- } }
148- >
149- { ( matches ) =>
150- matches . map ( ( env ) => (
151- < SelectItem key = { env . id } value = { env . id } >
152- < EnvironmentCombo environment = { env } />
153- </ SelectItem >
154- ) )
155- }
156- </ Select >
157- </ InputGroup >
100+ < Form
101+ action = { formAction }
102+ method = "post"
103+ onSubmit = { ( e ) => submitForm ( e ) }
104+ className = "flex grow flex-col gap-3"
105+ >
158106 < input type = "hidden" name = "failedRedirect" value = { failedRedirect } />
159- < div className = "mt-3 flex items-center justify-between gap-2 border-t border-grid-dimmed pt-3.5" >
107+
108+ < Paragraph className = "pt-6" >
109+ Replaying will create a new run using the same or modified payload, executing against the
110+ latest version in your selected environment.
111+ </ Paragraph >
112+ < div className = "grow" >
113+ < div className = "mb-3 h-full min-h-40 overflow-y-auto rounded-sm border border-grid-dimmed bg-charcoal-900 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600" >
114+ < JSONEditor
115+ autoFocus
116+ defaultValue = { currentJson . current }
117+ readOnly = { false }
118+ basicSetup
119+ onChange = { ( v ) => {
120+ currentJson . current = v ;
121+ } }
122+ height = "100%"
123+ min-height = "100%"
124+ max-height = "100%"
125+ additionalActions = {
126+ < TabContainer className = "flex grow items-baseline justify-between self-end border-none" >
127+ < div className = "flex gap-5" >
128+ < TabButton
129+ isActive = { ! tab || tab === "payload" }
130+ layoutId = "replay-editor"
131+ onClick = { ( ) => {
132+ setTab ( "payload" ) ;
133+ } }
134+ >
135+ Payload
136+ </ TabButton >
137+ < TabButton
138+ isActive = { tab === "metadata" }
139+ layoutId = "replay-editor"
140+ onClick = { ( ) => {
141+ setTab ( "metadata" ) ;
142+ } }
143+ >
144+ Metadata
145+ </ TabButton >
146+ </ div >
147+ </ TabContainer >
148+ }
149+ />
150+ </ div >
151+ </ div >
152+
153+ < div className = "flex items-center justify-between gap-2 border-t border-grid-dimmed pt-3.5" >
160154 < DialogClose asChild >
161155 < Button variant = "tertiary/medium" > Cancel</ Button >
162156 </ DialogClose >
163- < Button
164- type = "submit"
165- variant = "primary/medium"
166- LeadingIcon = { isSubmitting ? SpinnerWhite : undefined }
167- disabled = { isSubmitting }
168- shortcut = { { modifiers : [ "mod" ] , key : "enter" , enabledOnInputElements : true } }
169- >
170- { isSubmitting ? "Replaying..." : "Replay run" }
171- </ Button >
157+ < div className = "flex items-center gap-3" >
158+ < InputGroup className = "flex flex-row items-center" >
159+ < Label > Replay this run in</ Label >
160+ < Select
161+ id = "environment"
162+ name = "environment"
163+ placeholder = "Select an environment"
164+ defaultValue = { environment . id }
165+ items = { environments }
166+ dropdownIcon
167+ variant = "tertiary/medium"
168+ className = "w-fit pl-1"
169+ filter = { {
170+ keys : [
171+ ( item ) => item . type . replace ( / \/ / g, " " ) . replace ( / _ / g, " " ) ,
172+ ( item ) => item . branchName ?. replace ( / \/ / g, " " ) . replace ( / _ / g, " " ) ?? "" ,
173+ ] ,
174+ } }
175+ text = { ( value ) => {
176+ const env = environments . find ( ( env ) => env . id === value ) ! ;
177+ return (
178+ < div className = "flex items-center pl-1 pr-2" >
179+ < EnvironmentCombo environment = { env } />
180+ </ div >
181+ ) ;
182+ } }
183+ >
184+ { ( matches ) =>
185+ matches . map ( ( env ) => (
186+ < SelectItem key = { env . id } value = { env . id } >
187+ < EnvironmentCombo environment = { env } />
188+ </ SelectItem >
189+ ) )
190+ }
191+ </ Select >
192+ </ InputGroup >
193+ < Button
194+ type = "submit"
195+ variant = "primary/medium"
196+ LeadingIcon = { isSubmitting ? SpinnerWhite : undefined }
197+ disabled = { isSubmitting }
198+ shortcut = { { modifiers : [ "mod" ] , key : "enter" , enabledOnInputElements : true } }
199+ >
200+ { isSubmitting ? "Replaying..." : "Replay run" }
201+ </ Button >
202+ </ div >
172203 </ div >
173204 </ Form >
174205 ) ;
0 commit comments