mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
add node actions
This commit is contained in:
@@ -1040,6 +1040,9 @@ export function BoardView() {
|
|||||||
onSearchQueryChange={setSearchQuery}
|
onSearchQueryChange={setSearchQuery}
|
||||||
onEditFeature={(feature) => setEditingFeature(feature)}
|
onEditFeature={(feature) => setEditingFeature(feature)}
|
||||||
onViewOutput={handleViewOutput}
|
onViewOutput={handleViewOutput}
|
||||||
|
onStartTask={handleStartImplementation}
|
||||||
|
onStopTask={handleForceStopFeature}
|
||||||
|
onResumeTask={handleResumeFeature}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import {
|
|||||||
Play,
|
Play,
|
||||||
Pause,
|
Pause,
|
||||||
Eye,
|
Eye,
|
||||||
MoreHorizontal,
|
MoreVertical,
|
||||||
GitBranch,
|
GitBranch,
|
||||||
|
Terminal,
|
||||||
|
RotateCcw,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { TaskNodeData } from '../hooks/use-graph-nodes';
|
import { TaskNodeData } from '../hooks/use-graph-nodes';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -82,6 +84,9 @@ export const TaskNode = memo(function TaskNode({ data, selected }: TaskNodeProps
|
|||||||
const isHighlighted = data.isHighlighted ?? false;
|
const isHighlighted = data.isHighlighted ?? false;
|
||||||
const isDimmed = data.isDimmed ?? false;
|
const isDimmed = data.isDimmed ?? false;
|
||||||
|
|
||||||
|
// Task is stopped if it's in_progress but not actively running
|
||||||
|
const isStopped = data.status === 'in_progress' && !data.isRunning;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Target handle (left side - receives dependencies) */}
|
{/* Target handle (left side - receives dependencies) */}
|
||||||
@@ -167,35 +172,114 @@ export const TaskNode = memo(function TaskNode({ data, selected }: TaskNodeProps
|
|||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Stopped indicator - task is in_progress but not actively running */}
|
||||||
|
{isStopped && (
|
||||||
|
<TooltipProvider delayDuration={200}>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div className="p-1 rounded bg-[var(--status-warning-bg)]">
|
||||||
|
<Pause className="w-3 h-3 text-[var(--status-warning)]" />
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="top" className="text-xs max-w-[200px]">
|
||||||
|
<p>Task paused - click menu to resume</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Actions dropdown */}
|
{/* Actions dropdown */}
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="ghost" size="sm" className="h-6 w-6 p-0 hover:bg-background/50">
|
<Button
|
||||||
<MoreHorizontal className="w-4 h-4" />
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className={cn(
|
||||||
|
'h-7 w-7 p-0 rounded-md',
|
||||||
|
'bg-background/60 hover:bg-background',
|
||||||
|
'border border-border/50 hover:border-border',
|
||||||
|
'shadow-sm',
|
||||||
|
'transition-all duration-150'
|
||||||
|
)}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<MoreVertical className="w-4 h-4 text-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end" className="w-40">
|
<DropdownMenuContent
|
||||||
<DropdownMenuItem className="text-xs">
|
align="end"
|
||||||
|
className="w-44"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="text-xs cursor-pointer"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
data.onViewLogs?.();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Terminal className="w-3 h-3 mr-2" />
|
||||||
|
View Agent Logs
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="text-xs cursor-pointer"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
data.onViewLogs?.();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Eye className="w-3 h-3 mr-2" />
|
<Eye className="w-3 h-3 mr-2" />
|
||||||
View Details
|
View Details
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{data.status === 'backlog' && !data.isBlocked && (
|
{data.status === 'backlog' && !data.isBlocked && (
|
||||||
<DropdownMenuItem className="text-xs">
|
<DropdownMenuItem
|
||||||
|
className="text-xs cursor-pointer"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
data.onStartTask?.();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Play className="w-3 h-3 mr-2" />
|
<Play className="w-3 h-3 mr-2" />
|
||||||
Start Task
|
Start Task
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
{data.isRunning && (
|
{data.isRunning && (
|
||||||
<DropdownMenuItem className="text-xs text-[var(--status-error)]">
|
<DropdownMenuItem
|
||||||
|
className="text-xs text-[var(--status-error)] cursor-pointer"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
data.onStopTask?.();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Pause className="w-3 h-3 mr-2" />
|
<Pause className="w-3 h-3 mr-2" />
|
||||||
Stop Task
|
Stop Task
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
<DropdownMenuSeparator />
|
{isStopped && (
|
||||||
<DropdownMenuItem className="text-xs">
|
<DropdownMenuItem
|
||||||
<GitBranch className="w-3 h-3 mr-2" />
|
className="text-xs text-[var(--status-success)] cursor-pointer"
|
||||||
View Branch
|
onClick={(e) => {
|
||||||
</DropdownMenuItem>
|
e.stopPropagation();
|
||||||
|
data.onResumeTask?.();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RotateCcw className="w-3 h-3 mr-2" />
|
||||||
|
Resume Task
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
{Boolean(data.branchName) && <DropdownMenuSeparator />}
|
||||||
|
{Boolean(data.branchName) && (
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="text-xs cursor-pointer"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
data.onViewBranch?.();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GitBranch className="w-3 h-3 mr-2" />
|
||||||
|
View Branch
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
@@ -223,6 +307,16 @@ export const TaskNode = memo(function TaskNode({ data, selected }: TaskNodeProps
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Paused indicator for stopped tasks */}
|
||||||
|
{isStopped && (
|
||||||
|
<div className="mt-2 flex items-center gap-2">
|
||||||
|
<div className="flex-1 h-1.5 bg-muted rounded-full overflow-hidden">
|
||||||
|
<div className="h-full w-1/2 bg-[var(--status-warning)] rounded-full" />
|
||||||
|
</div>
|
||||||
|
<span className="text-[10px] text-[var(--status-warning)] font-medium">Paused</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Branch name if assigned */}
|
{/* Branch name if assigned */}
|
||||||
{data.branchName && (
|
{data.branchName && (
|
||||||
<div className="mt-2 flex items-center gap-1 text-[10px] text-muted-foreground">
|
<div className="mt-2 flex items-center gap-1 text-[10px] text-muted-foreground">
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
useGraphFilter,
|
useGraphFilter,
|
||||||
type TaskNodeData,
|
type TaskNodeData,
|
||||||
type GraphFilterState,
|
type GraphFilterState,
|
||||||
|
type NodeActionCallbacks,
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
@@ -46,8 +47,8 @@ interface GraphCanvasProps {
|
|||||||
runningAutoTasks: string[];
|
runningAutoTasks: string[];
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
onSearchQueryChange: (query: string) => void;
|
onSearchQueryChange: (query: string) => void;
|
||||||
onNodeClick?: (featureId: string) => void;
|
|
||||||
onNodeDoubleClick?: (featureId: string) => void;
|
onNodeDoubleClick?: (featureId: string) => void;
|
||||||
|
nodeActionCallbacks?: NodeActionCallbacks;
|
||||||
backgroundStyle?: React.CSSProperties;
|
backgroundStyle?: React.CSSProperties;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
@@ -57,8 +58,8 @@ function GraphCanvasInner({
|
|||||||
runningAutoTasks,
|
runningAutoTasks,
|
||||||
searchQuery,
|
searchQuery,
|
||||||
onSearchQueryChange,
|
onSearchQueryChange,
|
||||||
onNodeClick,
|
|
||||||
onNodeDoubleClick,
|
onNodeDoubleClick,
|
||||||
|
nodeActionCallbacks,
|
||||||
backgroundStyle,
|
backgroundStyle,
|
||||||
className,
|
className,
|
||||||
}: GraphCanvasProps) {
|
}: GraphCanvasProps) {
|
||||||
@@ -84,6 +85,7 @@ function GraphCanvasInner({
|
|||||||
features,
|
features,
|
||||||
runningAutoTasks,
|
runningAutoTasks,
|
||||||
filterResult,
|
filterResult,
|
||||||
|
actionCallbacks: nodeActionCallbacks,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply layout
|
// Apply layout
|
||||||
@@ -118,14 +120,6 @@ function GraphCanvasInner({
|
|||||||
setIsNegativeFilter(false);
|
setIsNegativeFilter(false);
|
||||||
}, [onSearchQueryChange]);
|
}, [onSearchQueryChange]);
|
||||||
|
|
||||||
// Handle node click
|
|
||||||
const handleNodeClick = useCallback(
|
|
||||||
(_event: React.MouseEvent, node: Node<TaskNodeData>) => {
|
|
||||||
onNodeClick?.(node.id);
|
|
||||||
},
|
|
||||||
[onNodeClick]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle node double click
|
// Handle node double click
|
||||||
const handleNodeDoubleClick = useCallback(
|
const handleNodeDoubleClick = useCallback(
|
||||||
(_event: React.MouseEvent, node: Node<TaskNodeData>) => {
|
(_event: React.MouseEvent, node: Node<TaskNodeData>) => {
|
||||||
@@ -160,7 +154,6 @@ function GraphCanvasInner({
|
|||||||
edges={edges}
|
edges={edges}
|
||||||
onNodesChange={isLocked ? undefined : onNodesChange}
|
onNodesChange={isLocked ? undefined : onNodesChange}
|
||||||
onEdgesChange={onEdgesChange}
|
onEdgesChange={onEdgesChange}
|
||||||
onNodeClick={handleNodeClick}
|
|
||||||
onNodeDoubleClick={handleNodeDoubleClick}
|
onNodeDoubleClick={handleNodeDoubleClick}
|
||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
edgeTypes={edgeTypes}
|
edgeTypes={edgeTypes}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useMemo, useCallback } from 'react';
|
|||||||
import { Feature, useAppStore } from '@/store/app-store';
|
import { Feature, useAppStore } from '@/store/app-store';
|
||||||
import { GraphCanvas } from './graph-canvas';
|
import { GraphCanvas } from './graph-canvas';
|
||||||
import { useBoardBackground } from '../board-view/hooks';
|
import { useBoardBackground } from '../board-view/hooks';
|
||||||
|
import { NodeActionCallbacks } from './hooks';
|
||||||
|
|
||||||
interface GraphViewProps {
|
interface GraphViewProps {
|
||||||
features: Feature[];
|
features: Feature[];
|
||||||
@@ -13,6 +14,9 @@ interface GraphViewProps {
|
|||||||
onSearchQueryChange: (query: string) => void;
|
onSearchQueryChange: (query: string) => void;
|
||||||
onEditFeature: (feature: Feature) => void;
|
onEditFeature: (feature: Feature) => void;
|
||||||
onViewOutput: (feature: Feature) => void;
|
onViewOutput: (feature: Feature) => void;
|
||||||
|
onStartTask?: (feature: Feature) => void;
|
||||||
|
onStopTask?: (feature: Feature) => void;
|
||||||
|
onResumeTask?: (feature: Feature) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GraphView({
|
export function GraphView({
|
||||||
@@ -25,6 +29,9 @@ export function GraphView({
|
|||||||
onSearchQueryChange,
|
onSearchQueryChange,
|
||||||
onEditFeature,
|
onEditFeature,
|
||||||
onViewOutput,
|
onViewOutput,
|
||||||
|
onStartTask,
|
||||||
|
onStopTask,
|
||||||
|
onResumeTask,
|
||||||
}: GraphViewProps) {
|
}: GraphViewProps) {
|
||||||
const { currentProject } = useAppStore();
|
const { currentProject } = useAppStore();
|
||||||
|
|
||||||
@@ -56,17 +63,6 @@ export function GraphView({
|
|||||||
});
|
});
|
||||||
}, [features, currentWorktreePath, currentWorktreeBranch, projectPath]);
|
}, [features, currentWorktreePath, currentWorktreeBranch, projectPath]);
|
||||||
|
|
||||||
// Handle node click - view details
|
|
||||||
const handleNodeClick = useCallback(
|
|
||||||
(featureId: string) => {
|
|
||||||
const feature = features.find((f) => f.id === featureId);
|
|
||||||
if (feature) {
|
|
||||||
onViewOutput(feature);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[features, onViewOutput]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle node double click - edit
|
// Handle node double click - edit
|
||||||
const handleNodeDoubleClick = useCallback(
|
const handleNodeDoubleClick = useCallback(
|
||||||
(featureId: string) => {
|
(featureId: string) => {
|
||||||
@@ -78,6 +74,44 @@ export function GraphView({
|
|||||||
[features, onEditFeature]
|
[features, onEditFeature]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Node action callbacks for dropdown menu
|
||||||
|
const nodeActionCallbacks: NodeActionCallbacks = useMemo(
|
||||||
|
() => ({
|
||||||
|
onViewLogs: (featureId: string) => {
|
||||||
|
const feature = features.find((f) => f.id === featureId);
|
||||||
|
if (feature) {
|
||||||
|
onViewOutput(feature);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onStartTask: (featureId: string) => {
|
||||||
|
const feature = features.find((f) => f.id === featureId);
|
||||||
|
if (feature) {
|
||||||
|
onStartTask?.(feature);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onStopTask: (featureId: string) => {
|
||||||
|
const feature = features.find((f) => f.id === featureId);
|
||||||
|
if (feature) {
|
||||||
|
onStopTask?.(feature);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onResumeTask: (featureId: string) => {
|
||||||
|
const feature = features.find((f) => f.id === featureId);
|
||||||
|
if (feature) {
|
||||||
|
onResumeTask?.(feature);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onViewBranch: (featureId: string) => {
|
||||||
|
const feature = features.find((f) => f.id === featureId);
|
||||||
|
if (feature?.branchName) {
|
||||||
|
// TODO: Implement view branch action
|
||||||
|
console.log('View branch:', feature.branchName);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[features, onViewOutput, onStartTask, onStopTask, onResumeTask]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 overflow-hidden relative">
|
<div className="flex-1 overflow-hidden relative">
|
||||||
<GraphCanvas
|
<GraphCanvas
|
||||||
@@ -85,8 +119,8 @@ export function GraphView({
|
|||||||
runningAutoTasks={runningAutoTasks}
|
runningAutoTasks={runningAutoTasks}
|
||||||
searchQuery={searchQuery}
|
searchQuery={searchQuery}
|
||||||
onSearchQueryChange={onSearchQueryChange}
|
onSearchQueryChange={onSearchQueryChange}
|
||||||
onNodeClick={handleNodeClick}
|
|
||||||
onNodeDoubleClick={handleNodeDoubleClick}
|
onNodeDoubleClick={handleNodeDoubleClick}
|
||||||
|
nodeActionCallbacks={nodeActionCallbacks}
|
||||||
backgroundStyle={backgroundImageStyle}
|
backgroundStyle={backgroundImageStyle}
|
||||||
className="h-full"
|
className="h-full"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export {
|
|||||||
type TaskNode,
|
type TaskNode,
|
||||||
type DependencyEdge,
|
type DependencyEdge,
|
||||||
type TaskNodeData,
|
type TaskNodeData,
|
||||||
|
type NodeActionCallbacks,
|
||||||
} from './use-graph-nodes';
|
} from './use-graph-nodes';
|
||||||
export { useGraphLayout } from './use-graph-layout';
|
export { useGraphLayout } from './use-graph-layout';
|
||||||
export { useGraphFilter, type GraphFilterState, type GraphFilterResult } from './use-graph-filter';
|
export { useGraphFilter, type GraphFilterState, type GraphFilterResult } from './use-graph-filter';
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ export interface TaskNodeData extends Feature {
|
|||||||
isMatched?: boolean;
|
isMatched?: boolean;
|
||||||
isHighlighted?: boolean;
|
isHighlighted?: boolean;
|
||||||
isDimmed?: boolean;
|
isDimmed?: boolean;
|
||||||
|
// Action callbacks
|
||||||
|
onViewLogs?: () => void;
|
||||||
|
onStartTask?: () => void;
|
||||||
|
onStopTask?: () => void;
|
||||||
|
onResumeTask?: () => void;
|
||||||
|
onViewBranch?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TaskNode = Node<TaskNodeData, 'task'>;
|
export type TaskNode = Node<TaskNodeData, 'task'>;
|
||||||
@@ -22,17 +28,31 @@ export type DependencyEdge = Edge<{
|
|||||||
isDimmed?: boolean;
|
isDimmed?: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
export interface NodeActionCallbacks {
|
||||||
|
onViewLogs?: (featureId: string) => void;
|
||||||
|
onStartTask?: (featureId: string) => void;
|
||||||
|
onStopTask?: (featureId: string) => void;
|
||||||
|
onResumeTask?: (featureId: string) => void;
|
||||||
|
onViewBranch?: (featureId: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
interface UseGraphNodesProps {
|
interface UseGraphNodesProps {
|
||||||
features: Feature[];
|
features: Feature[];
|
||||||
runningAutoTasks: string[];
|
runningAutoTasks: string[];
|
||||||
filterResult?: GraphFilterResult;
|
filterResult?: GraphFilterResult;
|
||||||
|
actionCallbacks?: NodeActionCallbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms features into React Flow nodes and edges
|
* Transforms features into React Flow nodes and edges
|
||||||
* Creates dependency edges based on feature.dependencies array
|
* Creates dependency edges based on feature.dependencies array
|
||||||
*/
|
*/
|
||||||
export function useGraphNodes({ features, runningAutoTasks, filterResult }: UseGraphNodesProps) {
|
export function useGraphNodes({
|
||||||
|
features,
|
||||||
|
runningAutoTasks,
|
||||||
|
filterResult,
|
||||||
|
actionCallbacks,
|
||||||
|
}: UseGraphNodesProps) {
|
||||||
const { nodes, edges } = useMemo(() => {
|
const { nodes, edges } = useMemo(() => {
|
||||||
const nodeList: TaskNode[] = [];
|
const nodeList: TaskNode[] = [];
|
||||||
const edgeList: DependencyEdge[] = [];
|
const edgeList: DependencyEdge[] = [];
|
||||||
@@ -70,6 +90,22 @@ export function useGraphNodes({ features, runningAutoTasks, filterResult }: UseG
|
|||||||
isMatched,
|
isMatched,
|
||||||
isHighlighted,
|
isHighlighted,
|
||||||
isDimmed,
|
isDimmed,
|
||||||
|
// Action callbacks (bound to this feature's ID)
|
||||||
|
onViewLogs: actionCallbacks?.onViewLogs
|
||||||
|
? () => actionCallbacks.onViewLogs!(feature.id)
|
||||||
|
: undefined,
|
||||||
|
onStartTask: actionCallbacks?.onStartTask
|
||||||
|
? () => actionCallbacks.onStartTask!(feature.id)
|
||||||
|
: undefined,
|
||||||
|
onStopTask: actionCallbacks?.onStopTask
|
||||||
|
? () => actionCallbacks.onStopTask!(feature.id)
|
||||||
|
: undefined,
|
||||||
|
onResumeTask: actionCallbacks?.onResumeTask
|
||||||
|
? () => actionCallbacks.onResumeTask!(feature.id)
|
||||||
|
: undefined,
|
||||||
|
onViewBranch: actionCallbacks?.onViewBranch
|
||||||
|
? () => actionCallbacks.onViewBranch!(feature.id)
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -107,7 +143,7 @@ export function useGraphNodes({ features, runningAutoTasks, filterResult }: UseG
|
|||||||
});
|
});
|
||||||
|
|
||||||
return { nodes: nodeList, edges: edgeList };
|
return { nodes: nodeList, edges: edgeList };
|
||||||
}, [features, runningAutoTasks, filterResult]);
|
}, [features, runningAutoTasks, filterResult, actionCallbacks]);
|
||||||
|
|
||||||
return { nodes, edges };
|
return { nodes, edges };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user