11import React , { Fragment , useState , useRef } from 'react' ;
22import { PropTypes } from 'prop-types' ;
3- import { EllipsisVerticalIcon } from '@heroicons/react/24/solid' ;
4- import { DocumentArrowUpIcon , ClipboardDocumentCheckIcon , ClipboardDocumentIcon } from '@heroicons/react/24/outline' ;
3+ import { EllipsisVerticalIcon , PlusIcon } from '@heroicons/react/24/solid' ;
4+ import {
5+ DocumentArrowUpIcon ,
6+ ClipboardDocumentCheckIcon ,
7+ ClipboardDocumentIcon ,
8+ TrashIcon ,
9+ } from '@heroicons/react/24/outline' ;
510import { Menu , Transition } from '@headlessui/react' ;
611import useCanvasStore from 'stores/CanvasStore' ;
712import { toast } from 'react-toastify' ;
@@ -15,6 +20,7 @@ import { useTabStore } from 'stores/TabStore';
1520import { cloneDeep } from 'lodash' ;
1621import { CopyToClipboard } from 'react-copy-to-clipboard' ;
1722import Tippy from '@tippyjs/react' ;
23+ import FormDataSelector from './FormDataSelector' ;
1824
1925const requestBodyTypeOptions = [ 'None' , 'form-data' , 'raw-json' ] ;
2026
@@ -24,7 +30,8 @@ const RequestBody = ({ nodeId, nodeData }) => {
2430
2531 const [ copyStatus , setCopyStatus ] = useState ( false ) ; // To indicate if the text was copied
2632
27- const uploadFileForRequestNode = useRef ( null ) ;
33+ // Refs to hold the input elements
34+ const inputRefs = useRef ( [ ] ) ;
2835
2936 const updateCachedValues = ( ) => {
3037 if ( nodeData . requestBody ) {
@@ -37,12 +44,12 @@ const RequestBody = ({ nodeId, nodeData }) => {
3744 }
3845 } ;
3946
40- const handleFileUpload = async ( e ) => {
47+ const handleFileUpload = async ( e , index ) => {
4148 if ( ! e . target . files ) return ;
4249
4350 if ( e . target . files . length === 1 ) {
4451 const file = e . target . files [ 0 ] ;
45- const { name } = file ;
52+ const { name, path } = file ;
4653
4754 const reader = new FileReader ( ) ;
4855 reader . onload = ( evt ) => {
@@ -53,24 +60,15 @@ const RequestBody = ({ nodeId, nodeData }) => {
5360
5461 const value = result ;
5562
56- setRequestNodeBody ( nodeId , 'form-data' , {
57- key : nodeData . requestBody . body . key ,
58- value : value ,
59- name : name ,
60- } ) ;
63+ const updatedParams = [ ...nodeData . requestBody . body ] ;
64+ updatedParams [ index ] . value = path ;
65+ updatedParams [ index ] . name = name ;
66+ setRequestNodeBody ( nodeId , 'form-data' , updatedParams ) ;
6167 } ;
6268 reader . readAsDataURL ( file ) ;
6369 }
6470 } ;
6571
66- const handleFormDataKey = ( e ) => {
67- setRequestNodeBody ( nodeId , 'form-data' , {
68- key : e . target . value ,
69- value : nodeData . requestBody . body . value ,
70- name : nodeData . requestBody . body . name ,
71- } ) ;
72- } ;
73-
7472 const handleRawJson = ( value ) => {
7573 setRequestNodeBody ( nodeId , 'raw-json' , value ) ;
7674 } ;
@@ -89,11 +87,7 @@ const RequestBody = ({ nodeId, nodeData }) => {
8987 if ( cachedValues [ 'form-data' ] ) {
9088 setRequestNodeBody ( nodeId , option , cachedValues [ 'form-data' ] ) ;
9189 } else {
92- setRequestNodeBody ( nodeId , option , {
93- key : '' ,
94- value : '' ,
95- name : '' ,
96- } ) ;
90+ setRequestNodeBody ( nodeId , option , [ ] ) ;
9791 }
9892 }
9993 } ;
@@ -112,13 +106,113 @@ const RequestBody = ({ nodeId, nodeData }) => {
112106 return [ ] ;
113107 } ;
114108
109+ console . log ( nodeData . requestBody ) ;
110+
111+ const renderFormData = ( params ) => {
112+ return (
113+ < div >
114+ { params && params . length > 0 ? (
115+ < table className = 'leading-normal border-2 border-collapse border-background-dark' >
116+ < thead >
117+ < tr className = 'text-xs font-bold tracking-wider text-left bg-ghost-50 text-ghost-600' >
118+ < th className = 'p-2 border-2 border-background-dark' > Key</ th >
119+ < th className = 'p-2 border-2 border-background-dark' > Value</ th >
120+ < th className = 'p-2 border-2 border-background-dark' > </ th >
121+ </ tr >
122+ </ thead >
123+ < tbody >
124+ { params . map ( ( param , index ) => (
125+ < tr key = { index } className = 'text-sm border-b border-gray-200 text-ghost-700 hover:bg-ghost-50' >
126+ < td className = 'whitespace-no-wrap border-2 border-background-dark' >
127+ < input
128+ type = 'text'
129+ className = 'nodrag nowheel block h-9 w-full bg-background-light p-2.5 outline-none'
130+ name = 'variable-name'
131+ value = { param . key }
132+ onChange = { ( event ) => {
133+ const updatedParams = [ ...nodeData . requestBody . body ] ;
134+ updatedParams [ index ] . key = event . target . value ;
135+ setRequestNodeBody ( nodeId , 'form-data' , updatedParams ) ;
136+ } }
137+ />
138+ </ td >
139+ < td className = 'whitespace-no-wrap border-2 border-background-dark' >
140+ { param . type === 'text' ? (
141+ < input
142+ type = 'text'
143+ className = 'nodrag nowheel block h-9 w-full bg-background-light p-2.5 outline-none'
144+ name = 'variable-name'
145+ value = { param . value }
146+ onChange = { ( event ) => {
147+ const updatedParams = [ ...nodeData . requestBody . body ] ;
148+ updatedParams [ index ] . value = event . target . value ;
149+ setRequestNodeBody ( nodeId , 'form-data' , updatedParams ) ;
150+ } }
151+ />
152+ ) : (
153+ < div className = 'w-full nodrag nowheel' >
154+ < Button
155+ btnType = { BUTTON_TYPES . secondary }
156+ isDisabled = { false }
157+ onClickHandle = { ( ) => {
158+ //uploadFileForRequestNode.current.click();
159+ if ( inputRefs . current [ index ] ) {
160+ inputRefs . current [ index ] . click ( ) ; // Trigger the file input click
161+ }
162+ } }
163+ fullWidth = { true }
164+ >
165+ < DocumentArrowUpIcon className = 'w-4 h-4 text-center' />
166+ < div
167+ className = 'max-w-xs overflow-hidden whitespace-nowrap'
168+ style = { { textOverflow : 'ellipsis' } }
169+ >
170+ { param . name && param . name . trim ( ) !== '' ? param . name : 'Upload File' }
171+ </ div >
172+ { /* Ref: https://stackoverflow.com/questions/37457128/react-open-file-browser-on-click-a-div */ }
173+ < div className = 'hidden' >
174+ < input
175+ type = 'file'
176+ id = 'file'
177+ ref = { ( el ) => ( inputRefs . current [ index ] = el ) }
178+ onChange = { ( e ) => handleFileUpload ( e , index ) }
179+ />
180+ </ div >
181+ </ Button >
182+ </ div >
183+ ) }
184+ </ td >
185+ < td className = 'p-2 border-2 border-background-dark' >
186+ < div className = 'flex items-center gap-4' >
187+ < div
188+ onClick = { ( ) => {
189+ const updatedParams = nodeData . requestBody . body . filter ( ( _ , i ) => i !== index ) ;
190+ setRequestNodeBody ( nodeId , 'form-data' , updatedParams ) ;
191+ } }
192+ className = 'cursor-pointer'
193+ >
194+ < TrashIcon className = 'w-4 h-4' />
195+ </ div >
196+ </ div >
197+ </ td >
198+ </ tr >
199+ ) ) }
200+ </ tbody >
201+ </ table >
202+ ) : (
203+ ''
204+ ) }
205+ </ div >
206+ ) ;
207+ } ;
208+
115209 return (
116210 < >
117- < div className = 'flex items-center justify-between bg-background p-4' >
211+ < div className = 'flex items-center justify-between p-4 bg-background ' >
118212 < h3 > Body</ h3 >
119213 < Menu as = 'div' className = 'relative inline-block text-left' >
120214 < Menu . Button data-click-from = 'body-type-menu' >
121- < EllipsisVerticalIcon className = 'h -4 w -4' aria-hidden = 'true' data-click-from = 'body-type-menu' />
215+ < EllipsisVerticalIcon className = 'w -4 h -4' aria-hidden = 'true' data-click-from = 'body-type-menu' />
122216 </ Menu . Button >
123217 < Transition
124218 as = { Fragment }
@@ -130,13 +224,13 @@ const RequestBody = ({ nodeId, nodeData }) => {
130224 leaveTo = 'transform opacity-0 scale-95'
131225 >
132226 < Menu . Items
133- className = 'absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white px-1 py-1 shadow-lg ring-1 ring-black/5 focus:outline-none'
227+ className = 'absolute right-0 z-10 w-56 px-1 py-1 mt-2 origin-top-right bg-white divide-y divide-gray-100 rounded-md shadow-lg ring-1 ring-black/5 focus:outline-none'
134228 data-click-from = 'body-type-menu'
135229 >
136230 { requestBodyTypeOptions . map ( ( bodyTypeOption , index ) => (
137231 < Menu . Item key = { index } data-click-from = 'body-type-menu' onClick = { ( ) => handleClose ( bodyTypeOption ) } >
138232 < button
139- className = 'group flex w-full items-center rounded-md px-2 py-2 text-sm text-gray-900 hover:bg-background-light'
233+ className = 'flex items-center w-full px-2 py-2 text-sm text-gray-900 rounded-md group hover:bg-background-light'
140234 data-click-from = 'body-type-menu'
141235 >
142236 { bodyTypeOption }
@@ -150,8 +244,8 @@ const RequestBody = ({ nodeId, nodeData }) => {
150244 { nodeData . requestBody && nodeData . requestBody . type === 'raw-json' && (
151245 < >
152246 < NodeHorizontalDivider />
153- < div className = 'bg-background p-4' >
154- < div className = 'nodrag nowheel w-full min-w-72' >
247+ < div className = 'p-4 bg-background ' >
248+ < div className = 'w-full nodrag nowheel min-w-72' >
155249 < div className = 'relative bg-background-lighter' >
156250 < Editor
157251 name = 'request-body-json'
@@ -161,7 +255,7 @@ const RequestBody = ({ nodeId, nodeData }) => {
161255 completionOptions = { getActiveVariables ( ) }
162256 />
163257
164- < div className = 'absolute right-5 top-0 cursor-pointer text-slate-400 hover:text-cyan-900' >
258+ < div className = 'absolute top-0 cursor-pointer right-5 text-slate-400 hover:text-cyan-900' >
165259 < CopyToClipboard
166260 text = { nodeData . requestBody . body }
167261 onCopy = { ( ) => {
@@ -172,11 +266,11 @@ const RequestBody = ({ nodeId, nodeData }) => {
172266 < button >
173267 { copyStatus ? (
174268 < Tippy content = 'Copied to Clipboard' placement = 'top' >
175- < ClipboardDocumentCheckIcon className = 'h -6 w -6' />
269+ < ClipboardDocumentCheckIcon className = 'w -6 h -6' />
176270 </ Tippy >
177271 ) : (
178272 < Tippy content = 'Copy to Clipboard' placement = 'top' >
179- < ClipboardDocumentIcon className = 'h -6 w -6' />
273+ < ClipboardDocumentIcon className = 'w -6 h -6' />
180274 </ Tippy >
181275 ) }
182276 </ button >
@@ -207,33 +301,19 @@ const RequestBody = ({ nodeId, nodeData }) => {
207301 { nodeData . requestBody && nodeData . requestBody . type === 'form-data' && (
208302 < >
209303 < NodeHorizontalDivider />
210- < div className = 'bg-background p-4' >
211- < TextInputWithLabel
212- placeHolder = 'key'
213- onChangeHandler = { ( e ) => handleFormDataKey ( e ) }
214- name = { 'variable-value' }
215- value = { nodeData . requestBody . body . key }
216- label = { 'File' }
217- />
218- < div className = 'pt-4' >
219- < Button
220- btnType = { BUTTON_TYPES . secondary }
221- isDisabled = { false }
222- onClickHandle = { ( ) => {
223- uploadFileForRequestNode . current . click ( ) ;
224- } }
225- fullWidth = { true }
226- >
227- < DocumentArrowUpIcon className = 'h-4 w-4 text-center' />
228- Upload File
229- { /* Ref: https://stackoverflow.com/questions/37457128/react-open-file-browser-on-click-a-div */ }
230- < div className = 'hidden' >
231- < input type = 'file' id = 'file' ref = { uploadFileForRequestNode } onChange = { handleFileUpload } />
232- </ div >
233- </ Button >
234- < div className = 'pt-1 text-center' >
235- { nodeData . requestBody . body . name != '' ? nodeData . requestBody . body . name : 'Choose a file to upload' }
304+ < div className = 'pb-2 bg-background' >
305+ < div >
306+ < div className = 'flex items-center justify-between' >
307+ < div className = 'p-2' > Add Param</ div >
308+ < FormDataSelector
309+ onSelectHandler = { ( type ) => {
310+ const currentParams = nodeData . requestBody . body ;
311+ const updatedParams = currentParams . concat ( [ { key : '' , value : '' , type } ] ) ;
312+ setRequestNodeBody ( nodeId , 'form-data' , updatedParams ) ;
313+ } }
314+ />
236315 </ div >
316+ { renderFormData ( nodeData . requestBody . body ) }
237317 </ div >
238318 </ div >
239319 </ >
0 commit comments