11import { For , Index , Match , Show , Switch } from 'solid-js' ;
2- import type { RequestBodySummary , RequestDetailsRow } from '../../utils/request-details' ;
2+ import type {
3+ RequestBodyField ,
4+ RequestBodySummary ,
5+ RequestDetailsRow
6+ } from '../../utils/request-details' ;
37
48interface RequestWorkspaceParamsPanelProps {
59 requestMethod : string ;
@@ -23,13 +27,22 @@ interface RequestWorkspaceBodyPanelProps {
2327 hasRequest : boolean ;
2428 requestBodySummary : RequestBodySummary ;
2529 requestBodyDraft : string ;
30+ requestBodyFormDataDraft : RequestBodyField [ ] ;
31+ requestBodyFilePathDraft : string ;
2632 bodyDraftDirty : boolean ;
2733 bodyDraftSaving : boolean ;
2834 bodyDraftSaveError ?: string ;
2935 bodyDraftValidationError ?: string ;
3036 bodyDraftIsJsonEditable : boolean ;
3137 bodyDraftTemplateWarnings : string [ ] ;
3238 onBodyChange : ( value : string ) => void ;
39+ onBodyFilePathChange : ( value : string ) => void ;
40+ onBodyFormDataNameChange : ( index : number , value : string ) => void ;
41+ onBodyFormDataTypeChange : ( index : number , isFile : boolean ) => void ;
42+ onBodyFormDataValueChange : ( index : number , value : string ) => void ;
43+ onBodyFormDataFilenameChange : ( index : number , value : string ) => void ;
44+ onBodyFormDataAddField : ( ) => void ;
45+ onBodyFormDataRemoveField : ( index : number ) => void ;
3346 onBodyPrettify : ( ) => void ;
3447 onBodyMinify : ( ) => void ;
3548 onBodyCopy : ( ) => void ;
@@ -301,52 +314,196 @@ export function RequestWorkspaceBodyPanel(props: RequestWorkspaceBodyPanelProps)
301314 </ Match >
302315
303316 < Match when = { props . requestBodySummary . kind === 'form-data' } >
304- < Show
305- when = { ( props . requestBodySummary . fields ?. length ?? 0 ) > 0 }
306- fallback = { < p > No form-data fields were parsed.</ p > }
307- >
317+ < div class = "space-y-2" >
318+ < div class = "flex flex-wrap items-center justify-between gap-2" >
319+ < button
320+ type = "button"
321+ class = "btn btn-ghost btn-xs font-mono"
322+ onClick = { props . onBodyFormDataAddField }
323+ disabled = { ! props . hasRequest || props . bodyDraftSaving }
324+ >
325+ Add Field
326+ </ button >
327+
328+ < div class = "flex items-center gap-2" >
329+ < Show when = { props . bodyDraftDirty && ! props . bodyDraftSaving } >
330+ < span class = "badge badge-sm badge-warning font-mono" > Unsaved</ span >
331+ </ Show >
332+ < button
333+ type = "button"
334+ class = "btn btn-ghost btn-xs font-mono"
335+ onClick = { props . onDiscardBody }
336+ disabled = { ! props . hasRequest || ! props . bodyDraftDirty || props . bodyDraftSaving }
337+ >
338+ Discard
339+ </ button >
340+ < button
341+ type = "button"
342+ class = "btn btn-primary btn-xs font-mono"
343+ onClick = { props . onSaveBody }
344+ disabled = { ! props . hasRequest || ! props . bodyDraftDirty || props . bodyDraftSaving }
345+ >
346+ { props . bodyDraftSaving ? 'Saving…' : 'Save' }
347+ </ button >
348+ </ div >
349+ </ div >
350+
308351 < div class = "overflow-auto rounded-box border border-base-300 bg-base-100/80" >
309352 < table class = "table table-xs" >
310353 < thead >
311354 < tr >
312355 < th class = "font-mono uppercase tracking-[0.06em] text-[11px]" > Name</ th >
313356 < th class = "font-mono uppercase tracking-[0.06em] text-[11px]" > Type</ th >
314357 < th class = "font-mono uppercase tracking-[0.06em] text-[11px]" > Value</ th >
358+ < th class = "font-mono uppercase tracking-[0.06em] text-[11px]" > Filename</ th >
359+ < th class = "font-mono uppercase tracking-[0.06em] text-[11px] text-right" >
360+ Actions
361+ </ th >
315362 </ tr >
316363 </ thead >
317364 < tbody >
318- < For each = { props . requestBodySummary . fields } >
319- { ( field ) => (
365+ < Show
366+ when = { props . requestBodyFormDataDraft . length > 0 }
367+ fallback = {
320368 < tr >
321- < td class = "font-mono text-xs text-base-content" > { field . name } </ td >
322- < td class = "font-mono text-xs text-base-content/80" >
323- { field . isFile ? 'file' : 'text' }
324- </ td >
325- < td class = "font-mono text-xs text-base-content/80" >
326- { field . isFile
327- ? ( field . path ?? field . filename ?? field . value )
328- : field . value }
369+ < td
370+ colSpan = { 5 }
371+ class = "font-mono text-xs text-base-content/70 text-center py-3"
372+ >
373+ No form-data fields configured.
329374 </ td >
330375 </ tr >
331- ) }
332- </ For >
376+ }
377+ >
378+ < Index each = { props . requestBodyFormDataDraft } >
379+ { ( field , index ) => (
380+ < tr >
381+ < td >
382+ < input
383+ type = "text"
384+ class = "input input-xs w-full border-base-300 bg-base-100 font-mono text-xs"
385+ value = { field ( ) . name }
386+ onInput = { ( event ) =>
387+ props . onBodyFormDataNameChange ( index , event . currentTarget . value )
388+ }
389+ disabled = { ! props . hasRequest || props . bodyDraftSaving }
390+ />
391+ </ td >
392+ < td >
393+ < select
394+ class = "select select-xs w-full border-base-300 bg-base-100 font-mono text-xs"
395+ value = { field ( ) . isFile ? 'file' : 'text' }
396+ onChange = { ( event ) =>
397+ props . onBodyFormDataTypeChange (
398+ index ,
399+ event . currentTarget . value === 'file'
400+ )
401+ }
402+ disabled = { ! props . hasRequest || props . bodyDraftSaving }
403+ >
404+ < option value = "text" > text</ option >
405+ < option value = "file" > file</ option >
406+ </ select >
407+ </ td >
408+ < td >
409+ < input
410+ type = "text"
411+ class = "input input-xs w-full border-base-300 bg-base-100 font-mono text-xs"
412+ value = { field ( ) . isFile ? ( field ( ) . path ?? '' ) : field ( ) . value }
413+ onInput = { ( event ) =>
414+ props . onBodyFormDataValueChange ( index , event . currentTarget . value )
415+ }
416+ placeholder = { field ( ) . isFile ? './path/to/file' : 'value' }
417+ disabled = { ! props . hasRequest || props . bodyDraftSaving }
418+ />
419+ </ td >
420+ < td >
421+ < input
422+ type = "text"
423+ class = "input input-xs w-full border-base-300 bg-base-100 font-mono text-xs"
424+ value = { field ( ) . filename ?? '' }
425+ onInput = { ( event ) =>
426+ props . onBodyFormDataFilenameChange ( index , event . currentTarget . value )
427+ }
428+ placeholder = "optional"
429+ disabled = {
430+ ! props . hasRequest || props . bodyDraftSaving || ! field ( ) . isFile
431+ }
432+ />
433+ </ td >
434+ < td class = "text-right" >
435+ < button
436+ type = "button"
437+ class = "btn btn-ghost btn-xs text-error"
438+ onClick = { ( ) => props . onBodyFormDataRemoveField ( index ) }
439+ disabled = { ! props . hasRequest || props . bodyDraftSaving }
440+ >
441+ Remove
442+ </ button >
443+ </ td >
444+ </ tr >
445+ ) }
446+ </ Index >
447+ </ Show >
333448 </ tbody >
334449 </ table >
335450 </ div >
336- </ Show >
451+ </ div >
337452 </ Match >
338453
339454 < Match when = { props . requestBodySummary . kind === 'file' } >
340- < Show
341- when = { props . requestBodySummary . filePath }
342- fallback = { < p > No request body file path was parsed.</ p > }
343- >
344- { ( filePath ) => (
345- < div class = "rounded-box border border-base-300 bg-base-100/80 p-2" >
346- < p class = "font-mono text-xs text-base-content/80" > { filePath ( ) } </ p >
455+ < div class = "space-y-2" >
456+ < div class = "flex flex-wrap items-center justify-between gap-2" >
457+ < button
458+ type = "button"
459+ class = "btn btn-ghost btn-xs font-mono"
460+ onClick = { props . onBodyCopy }
461+ disabled = { ! props . hasRequest }
462+ >
463+ Copy
464+ </ button >
465+
466+ < div class = "flex items-center gap-2" >
467+ < Show when = { props . bodyDraftDirty && ! props . bodyDraftSaving } >
468+ < span class = "badge badge-sm badge-warning font-mono" > Unsaved</ span >
469+ </ Show >
470+ < button
471+ type = "button"
472+ class = "btn btn-ghost btn-xs font-mono"
473+ onClick = { props . onDiscardBody }
474+ disabled = { ! props . hasRequest || ! props . bodyDraftDirty || props . bodyDraftSaving }
475+ >
476+ Discard
477+ </ button >
478+ < button
479+ type = "button"
480+ class = "btn btn-primary btn-xs font-mono"
481+ onClick = { props . onSaveBody }
482+ disabled = { ! props . hasRequest || ! props . bodyDraftDirty || props . bodyDraftSaving }
483+ >
484+ { props . bodyDraftSaving ? 'Saving…' : 'Save' }
485+ </ button >
347486 </ div >
348- ) }
349- </ Show >
487+ </ div >
488+
489+ < div class = "rounded-box border border-base-300 bg-base-100/80 p-2" >
490+ < label
491+ for = "request-workspace-body-file-path"
492+ class = "mb-1 block font-mono text-[11px] uppercase tracking-[0.06em] text-base-content/70"
493+ >
494+ File Path
495+ </ label >
496+ < input
497+ id = "request-workspace-body-file-path"
498+ type = "text"
499+ class = "input input-sm w-full border-base-300 bg-base-100 font-mono text-xs"
500+ value = { props . requestBodyFilePathDraft }
501+ onInput = { ( event ) => props . onBodyFilePathChange ( event . currentTarget . value ) }
502+ disabled = { ! props . hasRequest || props . bodyDraftSaving }
503+ placeholder = "./payload.json"
504+ />
505+ </ div >
506+ </ div >
350507 </ Match >
351508 </ Switch >
352509 </ div >
0 commit comments