@@ -29,24 +29,64 @@ function buildAsyncParams(
2929}
3030
3131function AsyncOptionsInput ( { inputParam, value, disabled, onChange, nodeName, inputValues } : AsyncInputProps ) {
32- const params = buildAsyncParams ( inputParam . loadMethod , nodeName , inputValues )
33- const { options, loading, error, refetch } = useAsyncOptions ( {
34- loadMethod : inputParam . loadMethod ,
35- credentialNames : inputParam . credentialNames ,
36- params
37- } )
38-
3932 const isCredential = ! ! inputParam . credentialNames ?. length
4033 const [ createDialogOpen , setCreateDialogOpen ] = useState ( false )
34+ const [ reloadKey , setReloadKey ] = useState ( 0 )
4135
4236 const handleCreated = useCallback (
4337 ( newCredentialId : string ) => {
4438 setCreateDialogOpen ( false )
4539 onChange ( newCredentialId )
46- refetch ( )
40+ // Changing reloadKey forces AsyncOptionsDropdown to remount, which
41+ // re-runs useAsyncOptions and fetches fresh options including the
42+ // newly created credential — matching original Flowise behaviour.
43+ setReloadKey ( ( k ) => k + 1 )
4744 } ,
48- [ onChange , refetch ]
45+ [ onChange ]
46+ )
47+
48+ return (
49+ < >
50+ < AsyncOptionsDropdown
51+ key = { reloadKey }
52+ inputParam = { inputParam }
53+ value = { value }
54+ disabled = { disabled }
55+ onChange = { onChange }
56+ nodeName = { nodeName }
57+ inputValues = { inputValues }
58+ isCredential = { isCredential }
59+ onCreateNew = { ( ) => setCreateDialogOpen ( true ) }
60+ />
61+ { isCredential && (
62+ < CreateCredentialDialog
63+ open = { createDialogOpen }
64+ credentialNames = { inputParam . credentialNames ! }
65+ onClose = { ( ) => setCreateDialogOpen ( false ) }
66+ onCreated = { handleCreated }
67+ />
68+ ) }
69+ </ >
4970 )
71+ }
72+
73+ /** Inner component that owns the useAsyncOptions hook. Remounted via key to force a fresh fetch. */
74+ function AsyncOptionsDropdown ( {
75+ inputParam,
76+ value,
77+ disabled,
78+ onChange,
79+ nodeName,
80+ inputValues,
81+ isCredential,
82+ onCreateNew
83+ } : AsyncInputProps & { isCredential : boolean ; onCreateNew : ( ) => void } ) {
84+ const params = buildAsyncParams ( inputParam . loadMethod , nodeName , inputValues )
85+ const { options, loading, error, refetch } = useAsyncOptions ( {
86+ loadMethod : inputParam . loadMethod ,
87+ credentialNames : inputParam . credentialNames ,
88+ params
89+ } )
5090
5191 if ( error ) {
5292 return (
@@ -67,85 +107,75 @@ function AsyncOptionsInput({ inputParam, value, disabled, onChange, nodeName, in
67107 const matchedValue = displayOptions . find ( ( o ) => o . name === value ) ?? null
68108
69109 return (
70- < >
71- < Autocomplete < NodeOption >
72- size = 'small'
73- disabled = { disabled }
74- options = { displayOptions }
75- value = { matchedValue }
76- getOptionLabel = { ( o ) => o . label }
77- isOptionEqualToValue = { ( o , v ) => o . name === v . name }
78- onChange = { ( _e , selection ) => {
79- if ( selection ?. name === CREATE_NEW_SENTINEL ) {
80- setCreateDialogOpen ( true )
81- return
82- }
83- onChange ( selection ?. name ?? '' )
84- } }
85- loading = { loading }
86- noOptionsText = { loading ? 'Loading…' : 'No options available' }
87- sx = { { mt : 1 } }
88- renderOption = { ( props , option ) => (
89- < Box component = 'li' { ...props } sx = { { display : 'flex' , alignItems : 'center' , gap : 1 } } >
90- { option . name === CREATE_NEW_SENTINEL ? (
91- < Typography variant = 'h5' color = 'primary' >
92- { option . label }
93- </ Typography >
94- ) : (
110+ < Autocomplete < NodeOption >
111+ size = 'small'
112+ disabled = { disabled }
113+ options = { displayOptions }
114+ value = { matchedValue }
115+ getOptionLabel = { ( o ) => o . label }
116+ isOptionEqualToValue = { ( o , v ) => o . name === v . name }
117+ onChange = { ( _e , selection ) => {
118+ if ( selection ?. name === CREATE_NEW_SENTINEL ) {
119+ onCreateNew ( )
120+ return
121+ }
122+ onChange ( selection ?. name ?? '' )
123+ } }
124+ loading = { loading }
125+ noOptionsText = { loading ? 'Loading…' : 'No options available' }
126+ sx = { { mt : 1 } }
127+ renderOption = { ( props , option ) => (
128+ < Box component = 'li' { ...props } sx = { { display : 'flex' , alignItems : 'center' , gap : 1 } } >
129+ { option . name === CREATE_NEW_SENTINEL ? (
130+ < Typography variant = 'h5' color = 'primary' >
131+ { option . label }
132+ </ Typography >
133+ ) : (
134+ < >
135+ { option . imageSrc && (
136+ < Box
137+ component = 'img'
138+ src = { option . imageSrc }
139+ alt = { option . label }
140+ sx = { { width : 30 , height : 30 , padding : '1px' , borderRadius : '50%' , flexShrink : 0 } }
141+ />
142+ ) }
143+ < Box sx = { { display : 'flex' , flexDirection : 'column' } } >
144+ < Typography variant = 'h5' > { option . label } </ Typography >
145+ { option . description && < Typography variant = 'caption' > { option . description } </ Typography > }
146+ </ Box >
147+ </ >
148+ ) }
149+ </ Box >
150+ ) }
151+ renderInput = { ( params ) => (
152+ < TextField
153+ { ...params }
154+ InputProps = { {
155+ ...params . InputProps ,
156+ startAdornment : (
95157 < >
96- { option . imageSrc && (
158+ { matchedValue ? .imageSrc && (
97159 < Box
98160 component = 'img'
99- src = { option . imageSrc }
100- alt = { option . label }
101- sx = { { width : 30 , height : 30 , padding : '1px ' , borderRadius : '50%' , flexShrink : 0 } }
161+ src = { matchedValue . imageSrc }
162+ alt = { matchedValue . label }
163+ sx = { { width : 32 , height : 32 , borderRadius : '50% ' , mr : 0.5 , flexShrink : 0 } }
102164 />
103165 ) }
104- < Box sx = { { display : 'flex' , flexDirection : 'column' } } >
105- < Typography variant = 'h5' > { option . label } </ Typography >
106- { option . description && < Typography variant = 'caption' > { option . description } </ Typography > }
107- </ Box >
166+ { params . InputProps . startAdornment }
108167 </ >
109- ) }
110- </ Box >
111- ) }
112- renderInput = { ( params ) => (
113- < TextField
114- { ...params }
115- InputProps = { {
116- ...params . InputProps ,
117- startAdornment : (
118- < >
119- { matchedValue ?. imageSrc && (
120- < Box
121- component = 'img'
122- src = { matchedValue . imageSrc }
123- alt = { matchedValue . label }
124- sx = { { width : 32 , height : 32 , borderRadius : '50%' , mr : 0.5 , flexShrink : 0 } }
125- />
126- ) }
127- { params . InputProps . startAdornment }
128- </ >
129- ) ,
130- endAdornment : (
131- < Fragment >
132- { loading ? < CircularProgress color = 'inherit' size = { 20 } /> : null }
133- { params . InputProps . endAdornment }
134- </ Fragment >
135- )
136- } }
137- />
138- ) }
139- />
140- { isCredential && (
141- < CreateCredentialDialog
142- open = { createDialogOpen }
143- credentialNames = { inputParam . credentialNames ! }
144- onClose = { ( ) => setCreateDialogOpen ( false ) }
145- onCreated = { handleCreated }
168+ ) ,
169+ endAdornment : (
170+ < Fragment >
171+ { loading ? < CircularProgress color = 'inherit' size = { 20 } /> : null }
172+ { params . InputProps . endAdornment }
173+ </ Fragment >
174+ )
175+ } }
146176 />
147177 ) }
148- < />
178+ />
149179 )
150180}
151181
0 commit comments