@@ -18,20 +18,36 @@ import {
18
18
Tooltip ,
19
19
StackItem ,
20
20
Stack ,
21
+ Title ,
22
+ Icon ,
21
23
} from "@patternfly/react-core" ;
22
- import { ExclamationCircleIcon } from "@patternfly/react-icons" ;
24
+ import {
25
+ ExclamationCircleIcon ,
26
+ CheckCircleIcon ,
27
+ StarIcon ,
28
+ InfoCircleIcon ,
29
+ } from "@patternfly/react-icons" ;
23
30
import { useExtensionStateContext } from "../../context/ExtensionStateContext" ;
24
31
import { AnalysisProfile , CONFIGURE_CUSTOM_RULES } from "@editor-extensions/shared" ;
25
32
import { ConfirmDialog } from "../ConfirmDialog/ConfirmDialog" ;
26
33
import { CreatableMultiSelectField } from "./CreatableMultiSelectField" ;
27
34
28
35
function useDebouncedCallback ( callback : ( ...args : any [ ] ) => void , delay : number ) {
29
36
const timeoutRef = useRef < NodeJS . Timeout | null > ( null ) ;
30
- return ( ...args : any [ ] ) => {
31
- if ( timeoutRef . current ) {
32
- clearTimeout ( timeoutRef . current ) ;
33
- }
34
- timeoutRef . current = setTimeout ( ( ) => callback ( ...args ) , delay ) ;
37
+ const [ isPending , setIsPending ] = useState ( false ) ;
38
+
39
+ return {
40
+ callback : ( ...args : any [ ] ) => {
41
+ setIsPending ( true ) ;
42
+ if ( timeoutRef . current ) {
43
+ clearTimeout ( timeoutRef . current ) ;
44
+ }
45
+ timeoutRef . current = setTimeout ( ( ) => {
46
+ callback ( ...args ) ;
47
+ setIsPending ( false ) ;
48
+ } , delay ) ;
49
+ } ,
50
+ isPending,
35
51
} ;
36
52
}
37
53
@@ -52,6 +68,8 @@ export const ProfileEditorForm: React.FC<{
52
68
53
69
const { dispatch } = useExtensionStateContext ( ) ;
54
70
71
+ const { callback : debouncedChange , isPending : isSaving } = useDebouncedCallback ( onChange , 300 ) ;
72
+
55
73
useEffect ( ( ) => {
56
74
setLocalProfile ( profile ) ;
57
75
setNameValidation ( "default" ) ;
@@ -76,8 +94,6 @@ export const ProfileEditorForm: React.FC<{
76
94
setSelectedTargets ( parsedTargets ) ;
77
95
} , [ profile ] ) ;
78
96
79
- const debouncedChange = useDebouncedCallback ( onChange , 300 ) ;
80
-
81
97
const handleInputChange = ( value : string , field : keyof AnalysisProfile ) => {
82
98
const updated = { ...localProfile , [ field ] : value } ;
83
99
setLocalProfile ( updated ) ;
@@ -118,6 +134,55 @@ export const ProfileEditorForm: React.FC<{
118
134
119
135
return (
120
136
< Form isWidthLimited >
137
+ { /* Active Profile Header */ }
138
+ { isActive && (
139
+ < Alert
140
+ variant = "info"
141
+ title = {
142
+ < Flex alignItems = { { default : "alignItemsCenter" } } >
143
+ < FlexItem >
144
+ < Icon >
145
+ < StarIcon color = "var(--pf-v5-global--info-color--100)" />
146
+ </ Icon >
147
+ </ FlexItem >
148
+ < FlexItem > Active Profile</ FlexItem >
149
+ </ Flex >
150
+ }
151
+ isInline
152
+ style = { { marginBottom : "1rem" } }
153
+ >
154
+ This is your active analysis profile. It will be used for all new analyses. Changes are
155
+ saved automatically, no action needed.
156
+ </ Alert >
157
+ ) }
158
+
159
+ { /* Auto-save Status */ }
160
+ < Flex
161
+ justifyContent = { { default : "justifyContentSpaceBetween" } }
162
+ alignItems = { { default : "alignItemsCenter" } }
163
+ style = { { marginBottom : "1rem" } }
164
+ >
165
+ < FlexItem >
166
+ < Title headingLevel = "h3" size = "lg" >
167
+ Profile Settings
168
+ </ Title >
169
+ </ FlexItem >
170
+ < FlexItem >
171
+ < Flex alignItems = { { default : "alignItemsCenter" } } >
172
+ < FlexItem >
173
+ < Icon >
174
+ < CheckCircleIcon color = "var(--pf-v5-global--success-color--100)" />
175
+ </ Icon >
176
+ </ FlexItem >
177
+ < FlexItem >
178
+ < span style = { { fontSize : "0.875rem" , color : "var(--pf-v5-global--Color--200)" } } >
179
+ { isSaving ? "Saving..." : "Changes saved automatically" }
180
+ </ span >
181
+ </ FlexItem >
182
+ </ Flex >
183
+ </ FlexItem >
184
+ </ Flex >
185
+
121
186
{ nameValidation === "error" && (
122
187
< FormAlert >
123
188
< Alert
@@ -159,7 +224,15 @@ export const ProfileEditorForm: React.FC<{
159
224
} }
160
225
initialOptions = { targetOptions }
161
226
/>
227
+ < FormHelperText >
228
+ < HelperText >
229
+ < HelperTextItem icon = { < InfoCircleIcon /> } >
230
+ Technologies you want to migrate to (e.g., Spring Boot, Quarkus)
231
+ </ HelperTextItem >
232
+ </ HelperText >
233
+ </ FormHelperText >
162
234
</ FormGroup >
235
+
163
236
< FormGroup label = "Source Technologies" fieldId = "sources" >
164
237
< CreatableMultiSelectField
165
238
fieldId = "sources"
@@ -170,6 +243,13 @@ export const ProfileEditorForm: React.FC<{
170
243
} }
171
244
initialOptions = { sourceOptions }
172
245
/>
246
+ < FormHelperText >
247
+ < HelperText >
248
+ < HelperTextItem icon = { < InfoCircleIcon /> } >
249
+ Technologies you're migrating from (e.g., Java EE, WebLogic)
250
+ </ HelperTextItem >
251
+ </ HelperText >
252
+ </ FormHelperText >
173
253
</ FormGroup >
174
254
175
255
< FormGroup label = "Use Default Rules" fieldId = "use-default-rules" >
@@ -183,7 +263,15 @@ export const ProfileEditorForm: React.FC<{
183
263
debouncedChange ( updated ) ;
184
264
} }
185
265
/>
266
+ < FormHelperText >
267
+ < HelperText >
268
+ < HelperTextItem icon = { < InfoCircleIcon /> } >
269
+ Include Konveyor's built-in migration rules
270
+ </ HelperTextItem >
271
+ </ HelperText >
272
+ </ FormHelperText >
186
273
</ FormGroup >
274
+
187
275
< FormGroup label = "Custom Rules" fieldId = "custom-rules" >
188
276
< Stack hasGutter >
189
277
< StackItem isFilled >
@@ -199,6 +287,13 @@ export const ProfileEditorForm: React.FC<{
199
287
>
200
288
Select Custom Rules…
201
289
</ Button >
290
+ < FormHelperText >
291
+ < HelperText >
292
+ < HelperTextItem icon = { < InfoCircleIcon /> } >
293
+ Add your own custom migration rules
294
+ </ HelperTextItem >
295
+ </ HelperText >
296
+ </ FormHelperText >
202
297
</ StackItem >
203
298
< StackItem >
204
299
< LabelGroup aria-label = "Custom Rules" >
@@ -229,13 +324,19 @@ export const ProfileEditorForm: React.FC<{
229
324
230
325
< Flex spaceItems = { { default : "spaceItemsMd" } } >
231
326
< FlexItem >
232
- < Button
233
- variant = "secondary"
234
- onClick = { ( ) => onMakeActive ( profile . id ) }
235
- isDisabled = { isActive }
236
- >
237
- Make Active
238
- </ Button >
327
+ { isActive ? (
328
+ < Tooltip content = "This profile is already active and will be used for analyses" >
329
+ < Button variant = "secondary" isDisabled = { true } icon = { < StarIcon /> } >
330
+ Active Profile
331
+ </ Button >
332
+ </ Tooltip >
333
+ ) : (
334
+ < Tooltip content = "Set this profile as active to use it for new analyses" >
335
+ < Button variant = "secondary" onClick = { ( ) => onMakeActive ( profile . id ) } >
336
+ Make Active
337
+ </ Button >
338
+ </ Tooltip >
339
+ ) }
239
340
</ FlexItem >
240
341
< FlexItem >
241
342
< Button
@@ -247,6 +348,7 @@ export const ProfileEditorForm: React.FC<{
247
348
</ Button >
248
349
</ FlexItem >
249
350
</ Flex >
351
+
250
352
< ConfirmDialog
251
353
isOpen = { isDeleteDialogOpen }
252
354
title = "Delete profile?"
0 commit comments