feat: implement backlog plan management and UI enhancements

- Added functionality to save, clear, and load backlog plans within the application.
- Introduced a new API endpoint for clearing saved backlog plans.
- Enhanced the backlog plan dialog to allow users to review and apply changes to their features.
- Integrated dependency management features in the UI, allowing users to select parent and child dependencies for features.
- Improved the graph view with options to manage plans and visualize dependencies effectively.
- Updated the sidebar and settings to include provider visibility toggles for better user control over model selection.

These changes aim to enhance the user experience by providing robust backlog management capabilities and improving the overall UI for feature planning.
This commit is contained in:
webdevcody
2026-01-15 22:21:46 -05:00
parent cb544e0011
commit 03436103d1
46 changed files with 1719 additions and 418 deletions

View File

@@ -15,6 +15,7 @@ import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox';
import { CategoryAutocomplete } from '@/components/ui/category-autocomplete';
import { DependencySelector } from '@/components/ui/dependency-selector';
import {
DescriptionImageDropZone,
FeatureImagePath as DescriptionImagePath,
@@ -63,6 +64,8 @@ interface EditFeatureDialogProps {
priority: number;
planningMode: PlanningMode;
requirePlanApproval: boolean;
dependencies?: string[];
childDependencies?: string[]; // Feature IDs that should depend on this feature
},
descriptionHistorySource?: 'enhance' | 'edit',
enhancementMode?: EnhancementMode,
@@ -127,6 +130,21 @@ export function EditFeatureDialog({
feature?.descriptionHistory ?? []
);
// Dependency state
const [parentDependencies, setParentDependencies] = useState<string[]>(
feature?.dependencies ?? []
);
// Child dependencies are features that have this feature in their dependencies
const [childDependencies, setChildDependencies] = useState<string[]>(() => {
if (!feature) return [];
return allFeatures.filter((f) => f.dependencies?.includes(feature.id)).map((f) => f.id);
});
// Track original child dependencies to detect changes
const [originalChildDependencies, setOriginalChildDependencies] = useState<string[]>(() => {
if (!feature) return [];
return allFeatures.filter((f) => f.dependencies?.includes(feature.id)).map((f) => f.id);
});
useEffect(() => {
setEditingFeature(feature);
if (feature) {
@@ -145,13 +163,23 @@ export function EditFeatureDialog({
thinkingLevel: feature.thinkingLevel || 'none',
reasoningEffort: feature.reasoningEffort || 'none',
});
// Reset dependency state
setParentDependencies(feature.dependencies ?? []);
const childDeps = allFeatures
.filter((f) => f.dependencies?.includes(feature.id))
.map((f) => f.id);
setChildDependencies(childDeps);
setOriginalChildDependencies(childDeps);
} else {
setEditFeaturePreviewMap(new Map());
setDescriptionChangeSource(null);
setPreEnhancementDescription(null);
setLocalHistory([]);
setParentDependencies([]);
setChildDependencies([]);
setOriginalChildDependencies([]);
}
}, [feature]);
}, [feature, allFeatures]);
const handleModelChange = (entry: PhaseModelEntry) => {
setModelEntry(entry);
@@ -180,6 +208,12 @@ export function EditFeatureDialog({
// For 'custom' mode, use the specified branch name
const finalBranchName = workMode === 'custom' ? editingFeature.branchName || '' : '';
// Check if child dependencies changed
const childDepsChanged =
childDependencies.length !== originalChildDependencies.length ||
childDependencies.some((id) => !originalChildDependencies.includes(id)) ||
originalChildDependencies.some((id) => !childDependencies.includes(id));
const updates = {
title: editingFeature.title ?? '',
category: editingFeature.category,
@@ -195,6 +229,8 @@ export function EditFeatureDialog({
planningMode,
requirePlanApproval,
workMode,
dependencies: parentDependencies,
childDependencies: childDepsChanged ? childDependencies : undefined,
};
// Determine if description changed and what source to use
@@ -547,6 +583,40 @@ export function EditFeatureDialog({
testIdPrefix="edit-feature-work-mode"
/>
</div>
{/* Dependencies */}
{allFeatures.length > 1 && (
<div className="pt-2 space-y-3">
<div className="space-y-1.5">
<Label className="text-xs text-muted-foreground">
Parent Dependencies (this feature depends on)
</Label>
<DependencySelector
currentFeatureId={editingFeature.id}
value={parentDependencies}
onChange={setParentDependencies}
features={allFeatures}
type="parent"
placeholder="Select features this depends on..."
data-testid="edit-feature-parent-deps"
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs text-muted-foreground">
Child Dependencies (features that depend on this)
</Label>
<DependencySelector
currentFeatureId={editingFeature.id}
value={childDependencies}
onChange={setChildDependencies}
features={allFeatures}
type="child"
placeholder="Select features that depend on this..."
data-testid="edit-feature-child-deps"
/>
</div>
</div>
)}
</div>
</div>