@@ -10,13 +10,15 @@ import {
1010 ExpandableSectionToggle ,
1111 Flex ,
1212 FlexItem ,
13+ Icon ,
1314 NumberInput ,
1415 Radio ,
1516 TextArea ,
1617 TextInput ,
18+ Title ,
1719 Tooltip ,
1820} from '@patternfly/react-core' ;
19- import { CubesIcon , ExclamationCircleIcon } from '@patternfly/react-icons' ;
21+ import { CogIcon , CubesIcon , ExclamationCircleIcon , SyncIcon } from '@patternfly/react-icons' ;
2022import * as React from 'react' ;
2123import { TFunction , useTranslation } from 'react-i18next' ;
2224import { useDispatch , useSelector } from 'react-redux' ;
@@ -60,7 +62,8 @@ export default function Korrel8rPanel() {
6062 const [ result , setResult ] = React . useState < Result | null > ( null ) ;
6163 const [ showQuery , setShowQuery ] = React . useState ( false ) ;
6264
63- const cannotFocus = t ( 'The current console page is not supported for correlation.' ) ;
65+ const focusTip = t ( 'Generate a correlation graph starting from resources in the current view.' ) ;
66+ const cannotFocus = t ( 'The current view does not support correlation.' ) ;
6467
6568 React . useEffect ( ( ) => {
6669 // Set result = null to trigger a reload, don't run the query till then.
@@ -127,12 +130,10 @@ export default function Korrel8rPanel() {
127130 setSearch ( updatedSearch ) ; // Update the search state with the new object
128131 } ;
129132
130- const focusTip = locationQuery
131- ? t ( 'Re-calculate the correlation graph starting from resources on the current console page.' )
132- : cannotFocus ;
133133 const minDepth = 1 ;
134134 const maxDepth = 10 ;
135135 const depthBounds = applyBounds ( 1 , 10 ) ;
136+
136137 const runSearch = React . useCallback (
137138 ( newSearch : Search ) => {
138139 newSearch . depth = depthBounds ( newSearch . depth ) ;
@@ -146,7 +147,7 @@ export default function Korrel8rPanel() {
146147 return (
147148 < >
148149 < Flex className = "tp-plugin__panel-query-container" >
149- < Tooltip content = { focusTip } >
150+ < Tooltip content = { locationQuery ? focusTip : cannotFocus } >
150151 < Button
151152 isAriaDisabled = { ! locationQuery }
152153 onClick = { ( ) =>
@@ -160,16 +161,27 @@ export default function Korrel8rPanel() {
160161 { t ( 'Focus' ) }
161162 </ Button >
162163 </ Tooltip >
163- < FlexItem align = { { default : 'alignRight' } } >
164+ < Flex align = { { default : 'alignRight' } } >
164165 < ExpandableSectionToggle
165166 contentId = { queryContentID }
166167 toggleId = { queryToggleID }
167168 isExpanded = { showQuery }
168169 onToggle = { ( on : boolean ) => setShowQuery ( on ) }
169170 >
170- { showQuery ? t ( 'Hide Query' ) : t ( 'Show Query' ) }
171+ < Icon >
172+ < CogIcon />
173+ </ Icon >
171174 </ ExpandableSectionToggle >
172- </ FlexItem >
175+ < Tooltip content = { t ( 'Refresh the graph using the current search settings' ) } >
176+ < Button
177+ isAriaDisabled = { ! search ?. queryStr }
178+ onClick = { ( ) => runSearch ( search ) }
179+ variant = "secondary"
180+ >
181+ < SyncIcon />
182+ </ Button >
183+ </ Tooltip >
184+ </ Flex >
173185 </ Flex >
174186 < ExpandableSection
175187 contentId = { queryContentID }
@@ -178,88 +190,96 @@ export default function Korrel8rPanel() {
178190 isDetached
179191 isIndented
180192 >
181- { /* DateTimeRangePicker section with both date and time */ }
182- < Flex >
183- < FlexItem >
184- < b > { t ( 'Date and Time Range' ) } </ b >
185- < DateTimeRangePicker
186- // Pass the start date/time
187- from = { search . constraint ?. start ? new Date ( search . constraint . start ) : null }
188- // Pass the end date/time
189- to = { search . constraint ?. end ? new Date ( search . constraint . end ) : null }
190- onDateChange = { handleDateChange } // Unified handler for both date and time changes
191- />
192- </ FlexItem >
193- < FlexItem >
194- < b > { t ( 'Korrel8 query selecting the starting points for correlation.' ) } </ b >
195- < TextArea
196- className = "tp-plugin__panel-query-input"
197- placeholder = "domain:class:querydata"
198- id = { queryInputID }
199- value = { search . queryStr }
200- onChange = { ( _event , value ) => setSearch ( { ...search , queryStr : value } ) }
201- resizeOrientation = "vertical"
202- />
203- </ FlexItem >
193+ < Flex className = "tp-plugin__panel-query-container" direction = { { default : 'column' } } >
194+ < Title headingLevel = "h3" > Time Range</ Title >
195+ < DateTimeRangePicker
196+ // Pass the start date/time
197+ from = { search . constraint ?. start ? new Date ( search . constraint . start ) : null }
198+ // Pass the end date/time
199+ to = { search . constraint ?. end ? new Date ( search . constraint . end ) : null }
200+ onDateChange = { handleDateChange } // Unified handler for both date and time changes
201+ />
202+
203+ < Title headingLevel = "h3" > Search type</ Title >
204204 < Flex >
205- < Tooltip content = { t ( 'Show graph of connected classes up to the specified depth.' ) } >
206- < Radio
207- label = { t ( 'Neighbourhood depth: ' ) }
208- name = { searchTypeOptions }
209- id = "neighbourhood-option"
210- isChecked = { search . type === SearchType . Neighbour }
211- onChange = { ( _ : React . FormEvent , on : boolean ) => {
212- on && setSearch ( { ...search , type : SearchType . Neighbour } ) ;
205+ < FlexItem >
206+ < Tooltip content = { t ( 'Search for correlated resources up to the specified depth.' ) } >
207+ < Radio
208+ label = { t ( 'Neighbourhood search' ) }
209+ name = { searchTypeOptions }
210+ id = "neighbourhood-option"
211+ isChecked = { search . type === SearchType . Neighbour }
212+ onChange = { ( _ : React . FormEvent , on : boolean ) => {
213+ on && setSearch ( { ...search , type : SearchType . Neighbour } ) ;
214+ } }
215+ />
216+ </ Tooltip >
217+ </ FlexItem >
218+ < FlexItem hidden = { search . type !== SearchType . Neighbour } > { t ( 'Depth' ) } </ FlexItem >
219+ < FlexItem hidden = { search . type !== SearchType . Neighbour } >
220+ < NumberInput
221+ value = { search . depth }
222+ min = { minDepth }
223+ max = { maxDepth }
224+ onPlus = { ( ) => setSearch ( { ...search , depth : ( search . depth || 0 ) + 1 } ) }
225+ onMinus = { ( ) =>
226+ search . depth > minDepth && setSearch ( { ...search , depth : search . depth - 1 } )
227+ }
228+ onChange = { ( event : React . FormEvent < HTMLInputElement > ) => {
229+ const n = Number ( ( event . target as HTMLInputElement ) . value ) ;
230+ setSearch ( { ...search , depth : isNaN ( n ) ? 1 : n } ) ;
213231 } }
214232 />
215- </ Tooltip >
216- < NumberInput
217- value = { search . depth }
218- min = { minDepth }
219- max = { maxDepth }
220- isDisabled = { search . type !== SearchType . Neighbour }
221- onPlus = { ( ) => setSearch ( { ...search , depth : ( search . depth || 0 ) + 1 } ) }
222- onMinus = { ( ) =>
223- ( search . depth || 0 ) > minDepth && setSearch ( { ...search , depth : search . depth - 1 } )
224- }
225- onChange = { ( event : React . FormEvent < HTMLInputElement > ) => {
226- const n = Number ( ( event . target as HTMLInputElement ) . value ) ;
227- setSearch ( { ...search , depth : isNaN ( n ) ? 1 : n } ) ;
228- } }
229- />
233+ </ FlexItem >
230234 </ Flex >
231235 < Flex >
232- < Tooltip content = { t ( 'Show graph of paths to signals of the specified class.' ) } >
233- < Radio
234- label = { t ( 'Goal class: ' ) }
235- name = { searchTypeOptions }
236- id = "goal-option"
237- isChecked = { search . type === SearchType . Goal }
238- onChange = { ( _ : React . FormEvent , on : boolean ) =>
239- on && setSearch ( { ...search , type : SearchType . Goal } )
240- }
241- />
242- </ Tooltip >
243236 < FlexItem >
237+ < Tooltip content = { t ( 'Search for paths to resources of the specified class.' ) } >
238+ < Radio
239+ label = { t ( 'Goal directed search' ) }
240+ name = { searchTypeOptions }
241+ id = "goal-option"
242+ isChecked = { search . type === SearchType . Goal }
243+ onChange = { ( _ : React . FormEvent , on : boolean ) =>
244+ on && setSearch ( { ...search , type : SearchType . Goal } )
245+ }
246+ />
247+ </ Tooltip >
248+ </ FlexItem >
249+ < FlexItem hidden = { search . type !== SearchType . Goal } > Class</ FlexItem >
250+ < FlexItem hidden = { search . type !== SearchType . Goal } >
244251 < TextInput
252+ label = { 'Class' }
245253 value = { search . goal }
246- isDisabled = { search . type !== SearchType . Goal }
247254 placeholder = "domain:class"
248255 onChange = { ( event : React . FormEvent < HTMLInputElement > ) => {
249256 setSearch ( { ...search , goal : ( event . target as HTMLInputElement ) . value } ) ;
250257 } }
251- aria-label = "Korrel8r Query"
252258 />
253259 </ FlexItem >
254260 </ Flex >
261+ < Title headingLevel = "h3" > Query</ Title >
262+ < Tooltip content = { t ( 'Query to select the starting resources for correlation.' ) } >
263+ < TextArea
264+ className = "tp-plugin__panel-query-input"
265+ placeholder = "domain:class:selector"
266+ id = { queryInputID }
267+ value = { search . queryStr }
268+ onChange = { ( _event , value ) => setSearch ( { ...search , queryStr : value } ) }
269+ />
270+ </ Tooltip >
271+ < FlexItem align = { { default : 'alignLeft' } } >
272+ < Tooltip content = { t ( 'Refresh the graph using the current search settings' ) } >
273+ < Button
274+ isAriaDisabled = { ! search ?. queryStr }
275+ onClick = { ( ) => runSearch ( search ) }
276+ variant = "secondary"
277+ >
278+ { t ( 'Update' ) }
279+ </ Button >
280+ </ Tooltip >
281+ </ FlexItem >
255282 </ Flex >
256- < Button
257- isAriaDisabled = { ! search ?. queryStr }
258- onClick = { ( ) => runSearch ( search ) }
259- variant = "secondary"
260- >
261- { t ( 'Query' ) }
262- </ Button >
263283 </ ExpandableSection >
264284 < Divider />
265285 < FlexItem className = "tp-plugin__panel-topology-container" grow = { { default : 'grow' } } >
@@ -275,7 +295,7 @@ interface TopologyProps {
275295 setSearch : ( search : Search ) => void ;
276296}
277297
278- const Topology : React . FC < TopologyProps > = ( { result, t, setSearch } ) => {
298+ const Topology : React . FC < TopologyProps > = ( { result, t } ) => {
279299 const [ loggingAvailable , loggingAvailableLoading ] = usePluginAvailable ( 'logging-view-plugin' ) ;
280300 const [ netobserveAvailable , netobserveAvailableLoading ] = usePluginAvailable ( 'netobserv-plugin' ) ;
281301
@@ -284,14 +304,13 @@ const Topology: React.FC<TopologyProps> = ({ result, t, setSearch }) => {
284304 return < Loading /> ;
285305 }
286306
287- if ( result . graph && result . graph . nodes ) {
307+ if ( result ? .graph ? .nodes ) {
288308 // Non-empty graph
289309 return (
290310 < Korrel8rTopology
291311 graph = { result . graph }
292312 loggingAvailable = { loggingAvailable }
293313 netobserveAvailable = { netobserveAvailable }
294- setSearch = { setSearch }
295314 />
296315 ) ;
297316 }
0 commit comments