|
1 | 1 | import { useState, useEffect } from 'react'; |
2 | | -import { X, Link2, Search, CheckCircle, ArrowRight, Target, ExternalLink, Filter, ChevronDown, CheckCircle2 } from 'lucide-react'; |
| 2 | +import { X, Link2, Search, CheckCircle, ArrowRight, Target, ExternalLink, Filter, ChevronDown, CheckCircle2, Trash2 } from 'lucide-react'; |
3 | 3 | import { useQuery, useMutation } from '@apollo/client'; |
4 | | -import { GET_WORK_ITEMS, CREATE_EDGE, GET_EDGES } from '../lib/queries'; |
| 4 | +import { GET_WORK_ITEMS, CREATE_EDGE, GET_EDGES, DELETE_EDGE } from '../lib/queries'; |
5 | 5 | import { useAuth } from '../contexts/AuthContext'; |
6 | 6 | import { useGraph } from '../contexts/GraphContext'; |
7 | 7 | import { useNotifications } from '../contexts/NotificationContext'; |
@@ -123,6 +123,34 @@ export function ConnectNodeModal({ isOpen, onClose, sourceNode }: ConnectNodeMod |
123 | 123 | awaitRefetchQueries: true |
124 | 124 | }); |
125 | 125 |
|
| 126 | + const [deleteEdgeMutation, { loading: deletingConnection }] = useMutation(DELETE_EDGE, { |
| 127 | + refetchQueries: [ |
| 128 | + // Refetch edges for the source node |
| 129 | + { |
| 130 | + query: GET_EDGES, |
| 131 | + variables: { |
| 132 | + where: { |
| 133 | + OR: [ |
| 134 | + { source: { id: sourceNode.id } }, |
| 135 | + { target: { id: sourceNode.id } } |
| 136 | + ] |
| 137 | + } |
| 138 | + } |
| 139 | + }, |
| 140 | + // Refetch all edges for graph visualization |
| 141 | + { |
| 142 | + query: GET_EDGES, |
| 143 | + variables: {} |
| 144 | + }, |
| 145 | + // Refetch work items |
| 146 | + { |
| 147 | + query: GET_WORK_ITEMS, |
| 148 | + variables: { options: { limit: 100 } } |
| 149 | + } |
| 150 | + ], |
| 151 | + awaitRefetchQueries: true |
| 152 | + }); |
| 153 | + |
126 | 154 | // Initialize data arrays |
127 | 155 | const workItems: WorkItem[] = workItemsData?.workItems || []; |
128 | 156 | const existingEdges: Edge[] = edgesData?.edges || []; |
@@ -284,6 +312,37 @@ export function ConnectNodeModal({ isOpen, onClose, sourceNode }: ConnectNodeMod |
284 | 312 | } |
285 | 313 | }; |
286 | 314 |
|
| 315 | + const handleDisconnectEdge = async (connectionId: string, connectedNodeTitle: string, relationshipType: string) => { |
| 316 | + try { |
| 317 | + console.log('Disconnecting edge:', { connectionId, connectedNodeTitle, relationshipType }); |
| 318 | + |
| 319 | + await deleteEdgeMutation({ |
| 320 | + variables: { |
| 321 | + where: { id: connectionId } |
| 322 | + } |
| 323 | + }); |
| 324 | + |
| 325 | + showSuccess( |
| 326 | + 'Connection Removed Successfully!', |
| 327 | + `Disconnected "${sourceNode.title}" from "${connectedNodeTitle}"` |
| 328 | + ); |
| 329 | + |
| 330 | + } catch (error: any) { |
| 331 | + console.error('Failed to disconnect:', error); |
| 332 | + |
| 333 | + let errorMessage = 'Please try again or contact support.'; |
| 334 | + if (error.graphQLErrors && error.graphQLErrors.length > 0) { |
| 335 | + errorMessage = error.graphQLErrors[0].message; |
| 336 | + } else if (error.networkError) { |
| 337 | + errorMessage = 'Network error. Please check your connection.'; |
| 338 | + } else if (error.message) { |
| 339 | + errorMessage = error.message; |
| 340 | + } |
| 341 | + |
| 342 | + showError('Failed to Remove Connection', errorMessage); |
| 343 | + } |
| 344 | + }; |
| 345 | + |
287 | 346 | const selectedRelation = RELATIONSHIP_TYPES.find(r => r.type === selectedRelationType); |
288 | 347 |
|
289 | 348 | return ( |
@@ -675,6 +734,22 @@ export function ConnectNodeModal({ isOpen, onClose, sourceNode }: ConnectNodeMod |
675 | 734 | <span className="text-gray-200 font-medium text-xs max-w-16 truncate">{connection.connectedNode.title}</span> |
676 | 735 | </div> |
677 | 736 | </div> |
| 737 | + |
| 738 | + {/* Disconnect Button - Only show for Edge entities (not WorkItem dependencies) */} |
| 739 | + {connection.id.startsWith('edge-') || (!connection.id.startsWith('workitem-')) ? ( |
| 740 | + <button |
| 741 | + onClick={() => handleDisconnectEdge(connection.id, connection.connectedNode.title, connection.type)} |
| 742 | + disabled={deletingConnection} |
| 743 | + className="opacity-0 group-hover:opacity-100 transition-opacity duration-200 p-2 rounded-lg bg-red-500/20 hover:bg-red-500/30 border border-red-400/30 hover:border-red-400/50 disabled:opacity-50 disabled:cursor-not-allowed" |
| 744 | + title={`Disconnect ${relationshipType?.label || connection.type} relationship`} |
| 745 | + > |
| 746 | + <Trash2 className="h-3 w-3 text-red-400" /> |
| 747 | + </button> |
| 748 | + ) : ( |
| 749 | + <div className="text-xs text-gray-500 px-2 py-1 rounded bg-gray-600/20 border border-gray-600/30"> |
| 750 | + Legacy |
| 751 | + </div> |
| 752 | + )} |
678 | 753 | </div> |
679 | 754 | ); |
680 | 755 | })} |
@@ -808,7 +883,7 @@ export function ConnectNodeModal({ isOpen, onClose, sourceNode }: ConnectNodeMod |
808 | 883 | </div> |
809 | 884 | <span className="text-gray-400">•</span> |
810 | 885 | <span className="text-gray-300 font-medium">{node.type}</span> |
811 | | - {node.priorityComp && ( |
| 886 | + {(node.priorityComp !== undefined && node.priorityComp !== null) && ( |
812 | 887 | <> |
813 | 888 | <span className="text-gray-400">•</span> |
814 | 889 | <span className="text-emerald-300 font-medium">{Math.round(node.priorityComp * 100)}%</span> |
@@ -842,13 +917,14 @@ export function ConnectNodeModal({ isOpen, onClose, sourceNode }: ConnectNodeMod |
842 | 917 | </button> |
843 | 918 | <button |
844 | 919 | onClick={handleCreateConnections} |
845 | | - disabled={selectedNodes.size === 0 || creatingConnection || isRelationshipDisabled(selectedRelationType)} |
| 920 | + disabled={selectedNodes.size === 0 || creatingConnection || deletingConnection || isRelationshipDisabled(selectedRelationType)} |
846 | 921 | className="px-8 py-3 bg-gradient-to-r from-emerald-600 to-green-700 hover:from-emerald-500 hover:to-green-600 text-white rounded-xl transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center space-x-3 font-semibold shadow-lg shadow-emerald-500/20 disabled:shadow-none" |
847 | 922 | title={isRelationshipDisabled(selectedRelationType) ? 'This relationship already exists between the selected nodes' : ''} |
848 | 923 | > |
849 | 924 | <Link2 className="w-5 h-5" /> |
850 | 925 | <span> |
851 | 926 | {creatingConnection ? 'Connecting...' : |
| 927 | + deletingConnection ? 'Removing connection...' : |
852 | 928 | isRelationshipDisabled(selectedRelationType) ? 'Already Connected' : |
853 | 929 | `Connect ${selectedNodes.size} Node${selectedNodes.size !== 1 ? 's' : ''}`} |
854 | 930 | </span> |
|
0 commit comments