@@ -175,6 +175,12 @@ const Transition = React.forwardRef(function Transition (
175175function RunningSessions ( props ) {
176176 const [ rowOpen , setRowOpen ] = useState ( '' )
177177 const [ rowLiveViewOpen , setRowLiveViewOpen ] = useState ( '' )
178+ const [ confirmDeleteOpen , setConfirmDeleteOpen ] = useState ( false )
179+ const [ sessionToDelete , setSessionToDelete ] = useState ( '' )
180+ const [ deleteLocation , setDeleteLocation ] = useState ( '' ) // 'info' or 'liveview'
181+ const [ feedbackMessage , setFeedbackMessage ] = useState ( '' )
182+ const [ feedbackOpen , setFeedbackOpen ] = useState ( false )
183+ const [ feedbackSeverity , setFeedbackSeverity ] = useState ( 'success' )
178184 const [ order , setOrder ] = useState < Order > ( 'asc' )
179185 const [ orderBy , setOrderBy ] = useState < keyof SessionData > ( 'sessionDurationMillis' )
180186 const [ selected , setSelected ] = useState < string [ ] > ( [ ] )
@@ -244,6 +250,79 @@ function RunningSessions (props) {
244250
245251 const isSelected = ( name : string ) : boolean => selected . includes ( name )
246252
253+ const handleDeleteConfirmation = ( sessionId : string , location : string ) => {
254+ setSessionToDelete ( sessionId )
255+ setDeleteLocation ( location )
256+ setConfirmDeleteOpen ( true )
257+ }
258+
259+ const handleDeleteSession = async ( ) => {
260+ try {
261+ const session = sessions . find ( s => s . id === sessionToDelete )
262+ if ( ! session ) {
263+ setFeedbackMessage ( 'Session not found' )
264+ setFeedbackSeverity ( 'error' )
265+ setConfirmDeleteOpen ( false )
266+ setFeedbackOpen ( true )
267+ return
268+ }
269+
270+ let deleteUrl = ''
271+
272+ const parsed = JSON . parse ( session . capabilities )
273+ let wsUrl = parsed [ 'webSocketUrl' ] ?? ''
274+ if ( wsUrl . length > 0 ) {
275+ try {
276+ const url = new URL ( origin )
277+ const sessionUrl = new URL ( wsUrl )
278+ url . pathname = sessionUrl . pathname . split ( '/se/' ) [ 0 ] // Remove /se/ and everything after
279+ url . protocol = sessionUrl . protocol === 'wss:' ? 'https:' : 'http:'
280+ deleteUrl = url . href
281+ } catch ( error ) {
282+ deleteUrl = ''
283+ }
284+ }
285+
286+ if ( ! deleteUrl ) {
287+ const currentUrl = window . location . href
288+ const baseUrl = currentUrl . split ( '/ui/' ) [ 0 ] // Remove /ui/ and everything after
289+ deleteUrl = `${ baseUrl } /session/${ sessionToDelete } `
290+ }
291+
292+ const response = await fetch ( deleteUrl , {
293+ method : 'DELETE'
294+ } )
295+
296+ if ( response . ok ) {
297+ setFeedbackMessage ( 'Session deleted successfully' )
298+ setFeedbackSeverity ( 'success' )
299+ if ( deleteLocation === 'liveview' ) {
300+ handleDialogClose ( )
301+ } else {
302+ setRowOpen ( '' )
303+ }
304+ } else {
305+ setFeedbackMessage ( 'Failed to delete session' )
306+ setFeedbackSeverity ( 'error' )
307+ }
308+ } catch ( error ) {
309+ console . error ( 'Error deleting session:' , error )
310+ setFeedbackMessage ( 'Error deleting session' )
311+ setFeedbackSeverity ( 'error' )
312+ }
313+
314+ setConfirmDeleteOpen ( false )
315+ setFeedbackOpen ( true )
316+ setSessionToDelete ( '' )
317+ setDeleteLocation ( '' )
318+ }
319+
320+ const handleCancelDelete = ( ) => {
321+ setConfirmDeleteOpen ( false )
322+ setSessionToDelete ( '' )
323+ setDeleteLocation ( '' )
324+ }
325+
247326 const displaySessionInfo = ( id : string ) : JSX . Element => {
248327 const handleInfoIconClick = ( ) : void => {
249328 setRowOpen ( id )
@@ -280,15 +359,15 @@ function RunningSessions (props) {
280359 try {
281360 const capabilities = JSON . parse ( capabilitiesStr as string )
282361 const value = capabilities [ key ]
283-
362+
284363 if ( value === undefined || value === null ) {
285364 return ''
286365 }
287-
366+
288367 if ( typeof value === 'object' ) {
289368 return JSON . stringify ( value )
290369 }
291-
370+
292371 return String ( value )
293372 } catch ( e ) {
294373 return ''
@@ -307,11 +386,11 @@ function RunningSessions (props) {
307386 session . slot ,
308387 origin
309388 )
310-
389+
311390 selectedColumns . forEach ( column => {
312391 sessionData [ column ] = getCapabilityValue ( session . capabilities , column )
313392 } )
314-
393+
315394 return sessionData
316395 } )
317396 const emptyRows = rowsPerPage - Math . min ( rowsPerPage , rows . length - page * rowsPerPage )
@@ -328,14 +407,14 @@ function RunningSessions (props) {
328407 setRowLiveViewOpen ( s )
329408 }
330409 } , [ sessionId , sessions ] )
331-
410+
332411 useEffect ( ( ) => {
333412 const dynamicHeadCells = selectedColumns . map ( column => ( {
334413 id : column ,
335414 numeric : false ,
336415 label : column
337416 } ) )
338-
417+
339418 setHeadCells ( [ ...fixedHeadCells , ...dynamicHeadCells ] )
340419 } , [ selectedColumns ] )
341420
@@ -346,7 +425,7 @@ function RunningSessions (props) {
346425 < Paper sx = { { width : '100%' , marginBottom : 2 } } >
347426 < EnhancedTableToolbar title = 'Running' >
348427 < Box display = "flex" alignItems = "center" >
349- < ColumnSelector
428+ < ColumnSelector
350429 sessions = { sessions }
351430 selectedColumns = { selectedColumns }
352431 onColumnSelectionChange = { ( columns ) => {
@@ -532,6 +611,14 @@ function RunningSessions (props) {
532611 </ Typography >
533612 </ DialogContent >
534613 < DialogActions >
614+ < Button
615+ onClick = { ( ) => handleDeleteConfirmation ( row . id as string , 'info' ) }
616+ color = 'error'
617+ variant = 'contained'
618+ sx = { { marginRight : 1 } }
619+ >
620+ Delete
621+ </ Button >
535622 < Button
536623 onClick = { ( ) => setRowOpen ( '' ) }
537624 color = 'primary'
@@ -586,6 +673,63 @@ function RunningSessions (props) {
586673 />
587674 </ div >
588675 ) }
676+ { /* Confirmation Dialog */ }
677+ < Dialog
678+ open = { confirmDeleteOpen }
679+ onClose = { handleCancelDelete }
680+ aria-labelledby = 'delete-confirmation-dialog'
681+ >
682+ < DialogTitle id = 'delete-confirmation-dialog' >
683+ Confirm Session Deletion
684+ </ DialogTitle >
685+ < DialogContent >
686+ < Typography >
687+ Are you sure you want to delete this session? This action cannot be undone.
688+ </ Typography >
689+ </ DialogContent >
690+ < DialogActions >
691+ < Button
692+ onClick = { handleCancelDelete }
693+ color = 'primary'
694+ variant = 'outlined'
695+ >
696+ Cancel
697+ </ Button >
698+ < Button
699+ onClick = { handleDeleteSession }
700+ color = 'error'
701+ variant = 'contained'
702+ autoFocus
703+ >
704+ Delete
705+ </ Button >
706+ </ DialogActions >
707+ </ Dialog >
708+
709+ { /* Feedback Dialog */ }
710+ < Dialog
711+ open = { feedbackOpen }
712+ onClose = { ( ) => setFeedbackOpen ( false ) }
713+ aria-labelledby = 'feedback-dialog'
714+ >
715+ < DialogTitle id = 'feedback-dialog' >
716+ { feedbackSeverity === 'success' ? 'Success' : 'Error' }
717+ </ DialogTitle >
718+ < DialogContent >
719+ < Typography color = { feedbackSeverity === 'success' ? 'success.main' : 'error.main' } >
720+ { feedbackMessage }
721+ </ Typography >
722+ </ DialogContent >
723+ < DialogActions >
724+ < Button
725+ onClick = { ( ) => setFeedbackOpen ( false ) }
726+ color = 'primary'
727+ variant = 'contained'
728+ >
729+ OK
730+ </ Button >
731+ </ DialogActions >
732+ </ Dialog >
589733 </ Box >
590734 )
591735}
0 commit comments