Dependency Graph Visualization Tool

Dependency Graph Visualization Tool

Your Dependency Graph

Visualize the relationships and dependencies between your components or tasks.

Configure Graph Data

Add, edit, or remove nodes and define their dependencies.

Add/Edit Node

ID Name Type Description Actions

Define New Dependency

ID Source Node Target Node Label Actions

No nodes configured. Go to "Data Configuration" to add components or tasks.

'; return; } const width = graphVisualizationContainer.clientWidth; const height = graphVisualizationContainer.clientHeight; // Create SVG container const svg = d3.select(graphVisualizationContainer) .append("svg") .attr("width", width) .attr("height", height) .attr("viewBox", `0 0 ${width} ${height}`) .attr("preserveAspectRatio", "xMidYMid meet"); // Define arrow markers for links svg.append("defs").append("marker") .attr("id", "arrowhead") .attr("viewBox", "-0 -5 10 10") .attr("refX", 18) // Position arrow slightly away from node .attr("refY", 0) .attr("orient", "auto") .attr("markerWidth", 8) .attr("markerHeight", 8) .attr("xoverflow", "visible") .append("svg:path") .attr("d", "M 0,-5 L 10,0 L 0,5") .attr("fill", "#999") .attr("class", "link-arrow"); // Initialize D3 force simulation // Use a copy of nodes and dependencies for the simulation to avoid modifying original data const simulationNodes = nodes.map(d => ({ ...d })); const simulationLinks = dependencies.map(d => ({ ...d })); const simulation = d3.forceSimulation(simulationNodes) .force("link", d3.forceLink(simulationLinks).id(d => d.id).distance(100)) .force("charge", d3.forceManyBody().strength(-300)) // Node repulsion .force("center", d3.forceCenter(width / 2, height / 2)); // Center the graph // Create links const link = svg.append("g") .attr("class", "links") .selectAll("line") .data(simulationLinks) .enter().append("line") .attr("class", "link") .attr("marker-end", "url(#arrowhead)"); // Add arrowhead // Create link labels (text paths) const linkLabel = svg.append("g") .attr("class", "link-labels") .selectAll("text") .data(simulationLinks) .enter().append("text") .attr("class", "link-label") .attr("dy", -3) // Adjust position above the line .text(d => d.label); // Create nodes const node = svg.append("g") .attr("class", "nodes") .selectAll("g") .data(simulationNodes) .enter().append("g") .attr("class", "node") .call(d3.drag() // Enable dragging for nodes .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); node.append("circle") .attr("r", 10) // Node radius .attr("fill", d => { // Color nodes based on type for better visual distinction if (d.type === 'Component') return '#4CAF50'; // Green if (d.type === 'Service') return '#FFC107'; // Amber if (d.type === 'Data Source') return '#2196F3'; // Blue if (d.type === 'Task') return '#9C27B0'; // Purple return '#69b3a2'; // Default }); node.append("text") .attr("dy", -15) // Position text above circle .text(d => d.name); // Update positions on each tick of the simulation simulation.on("tick", () => { link .attr("x1", d => d.source.x) .attr("y1", d => d.source.y) .attr("x2", d => d.target.x) .attr("y2", d => d.target.y); // Calculate midpoint for link labels linkLabel .attr("x", d => (d.source.x + d.target.x) / 2) .attr("y", d => (d.source.y + d.target.y) / 2); node .attr("transform", d => `translate(${d.x},${d.y})`); }); // --- Drag functions for D3 nodes --- function dragstarted(event, d) { if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(event, d) { d.fx = event.x; d.fy = event.y; } function dragended(event, d) { if (!event.active) simulation.alphaTarget(0); d.fx = null; // Release fixed position d.fy = null; } } // --- PDF Download Function --- /** * Generates and downloads a PDF of the current dashboard content. */ window.downloadPdf = async function() { if (!dashboardTab) { console.error('Error: Dashboard tab content not found for PDF generation.'); return; } // Temporarily hide elements not needed in PDF const elementsToHide = document.querySelectorAll('.tab-nav, .nav-buttons, #downloadPdfButton'); elementsToHide.forEach(el => el.style.display = 'none'); // Ensure the dashboard tab is active for capture dashboardTab.classList.add('active'); // Use html2canvas to capture the graph visualization container const graphCanvas = await html2canvas(graphVisualizationContainer, { scale: 2, // Increase scale for better resolution in PDF useCORS: true, // Important for images if any, though not used here logging: false // Disable logging for cleaner console }); const graphImgData = graphCanvas.toDataURL('image/png'); // Re-show hidden elements immediately after capture elementsToHide.forEach(el => el.style.display = ''); const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'landscape', // Landscape for wider graphs unit: 'px', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); let yOffset = 40; // Add title to PDF pdf.setFontSize(22); pdf.text("Dependency Graph Report", pdfWidth / 2, yOffset, { align: 'center' }); yOffset += 20; // Add current date/time to PDF pdf.setFontSize(10); pdf.text(`Generated on: ${new Date().toLocaleString()}`, pdfWidth / 2, yOffset, { align: 'center' }); yOffset += 40; // Add Graph Visualization Image pdf.setFontSize(18); pdf.text("Dependency Graph Diagram", pdfWidth / 2, yOffset, { align: 'center' }); yOffset += 10; const graphImgHeight = (graphCanvas.height * (pdfWidth - 40)) / graphCanvas.width; // Check if image fits on current page, add new page if not if (yOffset + graphImgHeight > pdf.internal.pageSize.getHeight() - 20) { pdf.addPage(); yOffset = 40; // Reset yOffset for new page } pdf.addImage(graphImgData, 'PNG', 20, yOffset, pdfWidth - 40, graphImgHeight); yOffset += graphImgHeight + 30; // Add a section for detailed Node data (tabular format) pdf.addPage(); pdf.setFontSize(18); pdf.text("Detailed Node Information", pdf.internal.pageSize.getWidth() / 2, 40, { align: 'center' }); const nodeTableData = nodes.map(n => [ n.id, n.name, n.type, n.description || 'N/A' ]); pdf.autoTable({ head: [['ID', 'Name', 'Type', 'Description']], body: nodeTableData, startY: 60, theme: 'grid', styles: { fontSize: 9, cellPadding: 4 }, headStyles: { fillColor: [242, 242, 242], textColor: [51, 51, 51], fontStyle: 'bold' }, alternateRowStyles: { fillColor: [251, 251, 251] }, margin: { top: 70, left: 20, right: 20 } }); // Add a section for detailed Dependency data (tabular format) pdf.addPage(); pdf.setFontSize(18); pdf.text("Detailed Dependency Information", pdf.internal.pageSize.getWidth() / 2, 40, { align: 'center' }); const dependencyTableData = dependencies.map(dep => [ dep.id, nodes.find(n => n.id === dep.source)?.name || dep.source, nodes.find(n => n.id === dep.target)?.name || dep.target, dep.label ]); pdf.autoTable({ head: [['ID', 'Source Node', 'Target Node', 'Label']], body: dependencyTableData, startY: 60, theme: 'grid', styles: { fontSize: 9, cellPadding: 4 }, headStyles: { fillColor: [242, 242, 242], textColor: [51, 51, 51], fontStyle: 'bold' }, alternateRowStyles: { fillColor: [251, 251, 251] }, margin: { top: 70, left: 20, right: 20 } }); pdf.save('dependency_graph_report.pdf'); }; // --- Event Listeners and Initial Render --- downloadPdfButton.addEventListener('click', downloadPdf); // Initial setup updateNodesTable(); updateDependenciesTable(); populateNodeSelects(); renderGraphVisualization(); updateNavigationButtons(); // Set initial button states });
Scroll to Top