feat(graph-view): implement task deletion and dependency management enhancements

- Added onDeleteTask functionality to allow task deletion from both board and graph views.
- Integrated delete options for dependencies in the graph view, enhancing user interaction.
- Updated ancestor context section to clarify the role of parent tasks in task descriptions.
- Improved layout handling in graph view to preserve node positions during updates.

This update enhances task management capabilities and improves user experience in the graph view.
This commit is contained in:
James
2025-12-23 20:25:06 -05:00
parent 76b7cfec9e
commit 502043f6de
11 changed files with 546 additions and 55 deletions

View File

@@ -21,6 +21,7 @@ interface GraphViewProps {
onResumeTask?: (feature: Feature) => void;
onUpdateFeature?: (featureId: string, updates: Partial<Feature>) => void;
onSpawnTask?: (feature: Feature) => void;
onDeleteTask?: (feature: Feature) => void;
}
export function GraphView({
@@ -38,6 +39,7 @@ export function GraphView({
onResumeTask,
onUpdateFeature,
onSpawnTask,
onDeleteTask,
}: GraphViewProps) {
const { currentProject } = useAppStore();
@@ -83,6 +85,34 @@ export function GraphView({
// Handle creating a dependency via edge connection
const handleCreateDependency = useCallback(
async (sourceId: string, targetId: string): Promise<boolean> => {
const sourceFeature = features.find((f) => f.id === sourceId);
const targetFeature = features.find((f) => f.id === targetId);
// Debug logging
console.log('[Dependency Check] ===========================');
console.log('[Dependency Check] Source (prerequisite):', {
id: sourceId,
title: sourceFeature?.title || sourceFeature?.description?.slice(0, 50),
dependencies: sourceFeature?.dependencies,
});
console.log('[Dependency Check] Target (will depend on source):', {
id: targetId,
title: targetFeature?.title || targetFeature?.description?.slice(0, 50),
dependencies: targetFeature?.dependencies,
});
console.log(
'[Dependency Check] Action:',
`${targetFeature?.title || targetId} will depend on ${sourceFeature?.title || sourceId}`
);
console.log(
'[Dependency Check] All features:',
features.map((f) => ({
id: f.id,
title: f.title || f.description?.slice(0, 30),
deps: f.dependencies,
}))
);
// Prevent self-dependency
if (sourceId === targetId) {
toast.error('A task cannot depend on itself');
@@ -96,7 +126,18 @@ export function GraphView({
}
// Check for circular dependency
if (wouldCreateCircularDependency(features, sourceId, targetId)) {
// This checks: if we make targetId depend on sourceId, would it create a cycle?
// A cycle would occur if sourceId already depends on targetId (transitively)
console.log('[Cycle Check] Checking if adding dependency would create cycle...');
console.log(
'[Cycle Check] Would create cycle if:',
sourceFeature?.title || sourceId,
'already depends on',
targetFeature?.title || targetId
);
const wouldCycle = wouldCreateCircularDependency(features, sourceId, targetId);
console.log('[Cycle Check] Result:', wouldCycle ? 'WOULD CREATE CYCLE' : 'Safe to add');
if (wouldCycle) {
toast.error('Cannot create circular dependency', {
description: 'This would create a dependency cycle',
});
@@ -104,13 +145,12 @@ export function GraphView({
}
// Get target feature and update its dependencies
const targetFeature = features.find((f) => f.id === targetId);
if (!targetFeature) {
toast.error('Target task not found');
return false;
}
const currentDeps = targetFeature.dependencies || [];
const currentDeps = (targetFeature.dependencies as string[] | undefined) || [];
// Add the dependency
onUpdateFeature?.(targetId, {
@@ -162,8 +202,38 @@ export function GraphView({
onSpawnTask?.(feature);
}
},
onDeleteTask: (featureId: string) => {
const feature = features.find((f) => f.id === featureId);
if (feature) {
onDeleteTask?.(feature);
}
},
onDeleteDependency: (sourceId: string, targetId: string) => {
// Find the target feature and remove the source from its dependencies
const targetFeature = features.find((f) => f.id === targetId);
if (!targetFeature) return;
const currentDeps = (targetFeature.dependencies as string[] | undefined) || [];
const newDeps = currentDeps.filter((depId) => depId !== sourceId);
onUpdateFeature?.(targetId, {
dependencies: newDeps,
});
toast.success('Dependency removed');
},
}),
[features, onViewOutput, onEditFeature, onStartTask, onStopTask, onResumeTask, onSpawnTask]
[
features,
onViewOutput,
onEditFeature,
onStartTask,
onStopTask,
onResumeTask,
onSpawnTask,
onDeleteTask,
onUpdateFeature,
]
);
return (