diff --git a/dashboard/src/app/graph/[namespace]/[runId]/page.tsx b/dashboard/src/app/graph/[namespace]/[runId]/page.tsx
index 200869f4..59fe66b8 100644
--- a/dashboard/src/app/graph/[namespace]/[runId]/page.tsx
+++ b/dashboard/src/app/graph/[namespace]/[runId]/page.tsx
@@ -2,6 +2,7 @@
import React, { useState, useCallback } from 'react';
import { useParams, useRouter } from 'next/navigation';
+import { ReactFlowProvider } from 'reactflow';
import { GraphVisualization } from '@/components/GraphVisualization';
import { GraphTemplateDetail } from '@/components/GraphTemplateDetail';
import { ThemeToggle } from '@/components/ThemeToggle';
@@ -103,11 +104,13 @@ export default function GraphPage() {
{/* Main Content */}
-
+
+
+
{/* Graph Template Detail Modal - Inline at bottom */}
diff --git a/dashboard/src/components/GraphTemplateDetail.tsx b/dashboard/src/components/GraphTemplateDetail.tsx
index 4ae40d38..0273a3d1 100644
--- a/dashboard/src/components/GraphTemplateDetail.tsx
+++ b/dashboard/src/components/GraphTemplateDetail.tsx
@@ -1,6 +1,6 @@
'use client';
-import React, { useMemo } from 'react';
+import React, { useMemo, useState, useCallback } from 'react';
import { UpsertGraphTemplateResponse, NodeTemplate } from '@/types/state-manager';
import { X, GitBranch, Settings, Database, Workflow, Clock } from 'lucide-react';
import ReactFlow, {
@@ -257,6 +257,9 @@ const GraphVisualizer: React.FC<{ nodes: NodeTemplate[] }> = ({ nodes }) => {
);
}
+ const [isInteractive, setIsInteractive] = useState(true);
+ const handleInteractiveChange = useCallback((val: boolean) => setIsInteractive(val), []);
+
return (
@@ -279,12 +282,20 @@ const GraphVisualizer: React.FC<{ nodes: NodeTemplate[] }> = ({ nodes }) => {
minZoom={0.1}
maxZoom={2}
defaultViewport={{ x: 0, y: 0, zoom: 1.5 }}
- elementsSelectable={true}
+ elementsSelectable={isInteractive}
nodesConnectable={false}
- nodesDraggable={false}
+ nodesDraggable={isInteractive}
+ zoomOnScroll={isInteractive}
+ panOnScroll={isInteractive}
+ panOnDrag={isInteractive}
+ zoomOnPinch={isInteractive}
+ zoomOnDoubleClick={isInteractive}
className="bg-background"
>
-
+
diff --git a/dashboard/src/components/GraphVisualization.tsx b/dashboard/src/components/GraphVisualization.tsx
index 7536bfcc..f94934e7 100644
--- a/dashboard/src/components/GraphVisualization.tsx
+++ b/dashboard/src/components/GraphVisualization.tsx
@@ -12,7 +12,9 @@ import ReactFlow, {
MarkerType,
NodeTypes,
ConnectionLineType,
- Handle
+ Handle,
+ useReactFlow,
+ Panel
} from 'reactflow';
import 'reactflow/dist/style.css';
import { clientApiService } from '@/services/clientApi';
@@ -29,7 +31,8 @@ import {
XCircle,
Loader2,
Network,
- BarChart3
+ BarChart3,
+ Maximize2
} from 'lucide-react';
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
@@ -124,6 +127,7 @@ export const GraphVisualization: React.FC = ({
runId,
onGraphTemplateRequest
}) => {
+ const { fitView } = useReactFlow();
const [graphData, setGraphData] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
@@ -131,6 +135,14 @@ export const GraphVisualization: React.FC = ({
const [selectedNodeDetails, setSelectedNodeDetails] = useState(null);
const [isLoadingNodeDetails, setIsLoadingNodeDetails] = useState(false);
const [nodeDetailsError, setNodeDetailsError] = useState(null);
+ const [isInteractive, setIsInteractive] = useState(true);
+
+ const handleFitView = useCallback(() => {
+ fitView({
+ duration: 800, // Animation duration in ms
+ padding: 0.2 // 20% padding around the graph
+ });
+ }, [fitView]);
const loadGraphStructure = useCallback(async () => {
setIsLoading(true);
@@ -525,12 +537,44 @@ export const GraphVisualization: React.FC = ({
defaultViewport={{ x: 0, y: 0, zoom: 0.7 }}
proOptions={{ hideAttribution: true }}
connectionLineType={ConnectionLineType.Straight}
- elementsSelectable={true}
+ elementsSelectable={isInteractive}
nodesConnectable={false}
- nodesDraggable={false}
+ nodesDraggable={isInteractive}
+ zoomOnScroll={isInteractive}
+ panOnScroll={isInteractive}
+ panOnDrag={isInteractive}
+ zoomOnPinch={isInteractive}
+ zoomOnDoubleClick={isInteractive}
>
-
+
+
+
+
+