11import React , { useState , useEffect } from 'react' ;
22import { useMutation , useQuery } from '@apollo/client' ;
33import { X , Trash2 , AlertTriangle , Shield , CheckCircle , GitBranch } from 'lucide-react' ;
4- import { DELETE_WORK_ITEM , GET_WORK_ITEMS , GET_EDGES } from '../lib/queries' ;
4+ import { DELETE_WORK_ITEM , GET_WORK_ITEMS , GET_EDGES , DELETE_EDGE } from '../lib/queries' ;
55import { useAuth } from '../contexts/AuthContext' ;
66import { useNotifications } from '../contexts/NotificationContext' ;
77import { getRelationshipConfig , type RelationshipType } from '../constants/workItemConstants' ;
@@ -106,13 +106,77 @@ export function DeleteNodeModal({ isOpen, onClose, nodeId, nodeTitle, nodeType,
106106 }
107107 } ) ;
108108
109+ const [ deleteEdge , { loading : deletingConnection } ] = useMutation ( DELETE_EDGE , {
110+ refetchQueries : [
111+ {
112+ query : GET_EDGES ,
113+ variables : {
114+ where : {
115+ OR : [
116+ { source : { id : nodeId } } ,
117+ { target : { id : nodeId } }
118+ ]
119+ }
120+ }
121+ }
122+ ] ,
123+ awaitRefetchQueries : true
124+ } ) ;
125+
109126 const handleGoToDisconnect = ( ) => {
110127 onClose ( ) ; // Close delete modal
111128 if ( onOpenDisconnectModal ) {
112129 onOpenDisconnectModal ( ) ; // Open disconnect modal
113130 }
114131 } ;
115132
133+ const handleDisconnectAllConnections = async ( ) => {
134+ if ( nodeConnections . length === 0 ) return ;
135+
136+ try {
137+ // Disconnect all edges for this node
138+ for ( const edge of nodeConnections ) {
139+ await deleteEdge ( {
140+ variables : {
141+ where : { id : edge . id }
142+ }
143+ } ) ;
144+ }
145+
146+ showSuccess (
147+ 'All Connections Removed!' ,
148+ `Disconnected all ${ nodeConnections . length } connection${ nodeConnections . length !== 1 ? 's' : '' } from "${ nodeTitle } ".`
149+ ) ;
150+
151+ } catch ( error ) {
152+ showError (
153+ 'Failed to Remove All Connections' ,
154+ error instanceof Error ? error . message : 'Please try again.'
155+ ) ;
156+ }
157+ } ;
158+
159+ const handleDisconnectEdge = async ( edgeId : string , sourceTitle : string , targetTitle : string ) => {
160+ try {
161+ await deleteEdge ( {
162+ variables : {
163+ where : { id : edgeId }
164+ }
165+ } ) ;
166+
167+ showSuccess (
168+ 'Connection Removed!' ,
169+ `Disconnected "${ sourceTitle } " from "${ targetTitle } ".`
170+ ) ;
171+
172+ } catch ( error ) {
173+ showError (
174+ 'Failed to Remove Connection' ,
175+ error instanceof Error ? error . message : 'Please try again.'
176+ ) ;
177+ }
178+ } ;
179+
116180 const handleDelete = async ( ) => {
117181 try {
118182
@@ -212,7 +276,7 @@ export function DeleteNodeModal({ isOpen, onClose, nodeId, nodeTitle, nodeType,
212276 </ div >
213277 ) }
214278
215- { /* Connections blocking screen */ }
279+ { /* Connections blocking screen - same pattern as DeleteGraphModal */ }
216280 { ! loadingEdges && hasConnections && (
217281 < div className = "p-8" >
218282 < div className = "mb-8" >
@@ -238,75 +302,73 @@ export function DeleteNodeModal({ isOpen, onClose, nodeId, nodeTitle, nodeType,
238302 </ div >
239303 </ div >
240304
241- < div className = "mb-8" >
242- < div className = "flex items-center justify-center mb-4" >
243- < div className = "flex-1 h-px bg-gradient-to-r from-transparent via-gray-600 to-transparent" > </ div >
244- < span className = "px-4 text-sm font-medium text-gray-400 bg-gray-800" > Active Connections</ span >
245- < div className = "flex-1 h-px bg-gradient-to-r from-transparent via-gray-600 to-transparent" > </ div >
246- </ div >
247- < div className = "max-h-40 overflow-y-auto space-y-3 custom-scrollbar" >
248- { nodeConnections . map ( ( edge : any , index : number ) => (
249- < div
250- key = { edge . id }
251- className = "group relative bg-gradient-to-r from-gray-800/80 to-gray-700/60 border border-gray-600/50 rounded-xl p-4 hover:border-orange-400/30 transition-all duration-200 hover:shadow-lg hover:shadow-orange-500/10 animate-slide-in-up"
252- style = { { animationDelay : `${ index * 0.1 } s` } }
253- >
254- < div className = "flex items-center justify-between" >
255- < div className = "flex items-center space-x-3" >
256- < div className = "flex-shrink-0 w-2 h-2 bg-orange-400 rounded-full animate-pulse" > </ div >
257- < div >
258- < span className = "text-gray-200 font-medium" >
259- { edge . source . id === nodeId ? 'Connected to' : 'Connected from' }
260- </ span >
261- < div className = "text-white font-semibold text-lg" >
262- { edge . source . id === nodeId ? edge . target . title : edge . source . title }
305+ { /* Inline disconnect actions - matching DeleteGraphModal pattern */ }
306+ < div className = "bg-orange-900/20 border border-orange-600/30 rounded-lg p-5 mb-6" >
307+ < div className = "flex items-start" >
308+ < AlertTriangle className = "h-6 w-6 text-orange-400 mt-0.5 mr-3 flex-shrink-0" />
309+ < div className = "flex-1" >
310+ < h4 className = "text-orange-200 font-semibold mb-3" > Disconnect Connections First</ h4 >
311+ < p className = "text-orange-300 text-sm mb-4" >
312+ This node has < strong className = "text-orange-200" > { nodeConnections . length } connection{ nodeConnections . length !== 1 ? 's' : '' } </ strong > . Remove them to enable deletion:
313+ </ p >
314+
315+ { /* Disconnect All Button */ }
316+ < div className = "mb-4" >
317+ < button
318+ onClick = { handleDisconnectAllConnections }
319+ disabled = { deletingConnection }
320+ className = "w-full px-4 py-3 bg-orange-600 hover:bg-orange-700 disabled:bg-gray-600 text-white rounded-lg transition-colors flex items-center justify-center space-x-2 font-medium disabled:cursor-not-allowed"
321+ >
322+ < GitBranch className = "h-4 w-4" />
323+ < span > { deletingConnection ? 'Disconnecting...' : 'Disconnect All Connections' } </ span >
324+ </ button >
325+ </ div >
326+
327+ { /* Individual connections with disconnect buttons */ }
328+ < div className = "space-y-3" >
329+ { nodeConnections . map ( ( edge : any , index : number ) => (
330+ < div key = { edge . id } className = "flex items-center justify-between p-3 bg-gray-700/50 border border-gray-600/30 rounded-lg" >
331+ < div className = "flex items-center space-x-3" >
332+ < div className = "w-2 h-2 bg-orange-400 rounded-full" > </ div >
333+ < div >
334+ < span className = "text-gray-300 text-sm" >
335+ { edge . source . id === nodeId ? 'connects to' : 'connected from' } "{ edge . source . id === nodeId ? edge . target . title : edge . source . title } "
336+ </ span >
337+ < div className = "flex items-center space-x-2 mt-1" >
338+ < span className = "inline-flex items-center px-2 py-1 bg-gradient-to-r from-orange-500/20 to-amber-500/20 border border-orange-400/30 rounded-full text-xs font-semibold text-orange-200" >
339+ { getRelationshipConfig ( edge . type as RelationshipType ) . label }
340+ </ span >
341+ </ div >
263342 </ div >
264343 </ div >
344+ < button
345+ onClick = { ( ) => handleDisconnectEdge (
346+ edge . id ,
347+ edge . source . title ,
348+ edge . target . title
349+ ) }
350+ disabled = { deletingConnection }
351+ className = "px-2 py-1 bg-orange-600 hover:bg-orange-700 disabled:bg-gray-600 text-white rounded text-xs flex items-center space-x-1 disabled:cursor-not-allowed"
352+ title = "Disconnect this relationship"
353+ >
354+ < X className = "h-3 w-3" />
355+ < span > Disconnect</ span >
356+ </ button >
265357 </ div >
266- < div className = "text-right" >
267- < span className = "inline-flex items-center px-3 py-1 bg-gradient-to-r from-orange-500/20 to-amber-500/20 border border-orange-400/30 rounded-full text-xs font-semibold text-orange-200 backdrop-blur-sm" >
268- { getRelationshipConfig ( edge . type as RelationshipType ) . label }
269- </ span >
270- </ div >
271- </ div >
272- </ div >
273- ) ) }
274- </ div >
275- </ div >
276-
277- < div className = "space-y-6" >
278- { /* Modern info banner */ }
279- < div className = "relative bg-gradient-to-r from-blue-500/10 to-indigo-500/10 border border-blue-400/20 rounded-xl p-4 backdrop-blur-sm" >
280- < div className = "flex items-center space-x-3" >
281- < div className = "flex-shrink-0 w-8 h-8 bg-blue-500/20 border border-blue-400/30 rounded-full flex items-center justify-center" >
282- < GitBranch className = "h-4 w-4 text-blue-400" />
283- </ div >
284- < div >
285- < p className = "text-blue-200 font-medium text-sm" > Next Step Required</ p >
286- < p className = "text-gray-300 text-sm" > Use the disconnect tool to safely remove all connections</ p >
358+ ) ) }
287359 </ div >
288360 </ div >
289361 </ div >
362+ </ div >
290363
291- { /* Modern button group */ }
292- < div className = "flex flex-col sm:flex-row gap-3 justify-end" >
293- { onOpenDisconnectModal && (
294- < button
295- onClick = { handleGoToDisconnect }
296- className = "group relative px-6 py-3 bg-gradient-to-r from-orange-500 to-amber-500 hover:from-orange-600 hover:to-amber-600 text-white font-semibold rounded-xl transition-all duration-200 transform hover:scale-[1.02] hover:shadow-lg hover:shadow-orange-500/25 flex items-center justify-center space-x-2"
297- >
298- < div className = "absolute inset-0 bg-gradient-to-r from-orange-400/20 to-amber-400/20 rounded-xl blur opacity-0 group-hover:opacity-100 transition-opacity duration-200" > </ div >
299- < GitBranch className = "h-5 w-5 relative z-10" />
300- < span className = "relative z-10" > Go to Disconnect</ span >
301- </ button >
302- ) }
303- < button
304- onClick = { onClose }
305- className = "px-6 py-3 bg-gray-700/80 hover:bg-gray-600/80 border border-gray-600/50 hover:border-gray-500/50 text-gray-200 font-medium rounded-xl transition-all duration-200 backdrop-blur-sm"
306- >
307- Close
308- </ button >
309- </ div >
364+ { /* Modern button group */ }
365+ < div className = "flex justify-end" >
366+ < button
367+ onClick = { onClose }
368+ className = "px-6 py-3 bg-gray-700/80 hover:bg-gray-600/80 border border-gray-600/50 hover:border-gray-500/50 text-gray-200 font-medium rounded-xl transition-all duration-200 backdrop-blur-sm"
369+ >
370+ Close
371+ </ button >
310372 </ div >
311373 </ div >
312374 ) }
0 commit comments