rm edit add delete from UI (#147)

* rm edit add delete from UI

* rm unneeded CRUD utils props funcs

* fix build
This commit is contained in:
Dylan Boudro 2025-06-12 16:11:37 -07:00 • committed by GitHub
parent e45b961007
commit 713686a250
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 65 additions and 365 deletions

View file

@ -1,8 +1,6 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { import {
Collapsible, Collapsible,
CollapsibleContent, CollapsibleContent,
@ -15,10 +13,6 @@ import {
ChevronDown, ChevronDown,
ChevronRight, ChevronRight,
ChevronLeft, ChevronLeft,
Edit2,
Save,
Plus,
Trash2,
Clock, Clock,
Filter, Filter,
PanelLeftClose, PanelLeftClose,
@ -36,27 +30,11 @@ interface TasksSidebarProps {
taskPlan: TaskPlan; taskPlan: TaskPlan;
className?: string; className?: string;
onTaskChange?: (taskId: string) => void; onTaskChange?: (taskId: string) => void;
onRevisionChange?: (taskId: string, revisionIndex: number) => void;
onEditPlanItem?: (
taskId: string,
planItemIndex: number,
newPlan: string,
) => void;
onAddPlanItem?: (taskId: string, plan: string) => void;
onDeletePlanItem?: (taskId: string, planItemIndex: number) => void;
} }
interface TaskPlanViewProps { interface TaskPlanViewProps {
taskPlan: TaskPlan; taskPlan: TaskPlan;
onTaskChange?: (taskId: string) => void; onTaskChange?: (taskId: string) => void;
onRevisionChange?: (taskId: string, revisionIndex: number) => void;
onEditPlanItem?: (
taskId: string,
planItemIndex: number,
newPlan: string,
) => void;
onAddPlanItem?: (taskId: string, plan: string) => void;
onDeletePlanItem?: (taskId: string, planItemIndex: number) => void;
} }
type FilterType = "all" | "completed" | "current" | "pending"; type FilterType = "all" | "completed" | "current" | "pending";
@ -67,10 +45,6 @@ export function TasksSidebar({
onClose, onClose,
taskPlan, taskPlan,
onTaskChange, onTaskChange,
onRevisionChange,
onEditPlanItem,
onAddPlanItem,
onDeletePlanItem,
}: TasksSidebarProps) { }: TasksSidebarProps) {
const [currentTaskIndex, setCurrentTaskIndex] = useState( const [currentTaskIndex, setCurrentTaskIndex] = useState(
taskPlan.activeTaskIndex, taskPlan.activeTaskIndex,
@ -79,10 +53,6 @@ export function TasksSidebar({
const [expandedSummaries, setExpandedSummaries] = useState<Set<number>>( const [expandedSummaries, setExpandedSummaries] = useState<Set<number>>(
new Set(), new Set(),
); );
const [editingPlanItem, setEditingPlanItem] = useState<number | null>(null);
const [editingText, setEditingText] = useState("");
const [newPlanItemText, setNewPlanItemText] = useState("");
const [showAddPlanItem, setShowAddPlanItem] = useState(false);
const [filter, setFilter] = useState<FilterType>("all"); const [filter, setFilter] = useState<FilterType>("all");
const currentTask = taskPlan.tasks[currentTaskIndex]; const currentTask = taskPlan.tasks[currentTaskIndex];
@ -94,7 +64,6 @@ export function TasksSidebar({
if (currentTask?.planRevisions) { if (currentTask?.planRevisions) {
setCurrentRevisionIndex(currentTask.activeRevisionIndex); setCurrentRevisionIndex(currentTask.activeRevisionIndex);
setExpandedSummaries(new Set()); setExpandedSummaries(new Set());
setEditingPlanItem(null);
} }
}, [currentTask]); }, [currentTask]);
@ -120,15 +89,6 @@ export function TasksSidebar({
return true; return true;
}); });
const isPlanItemEditable = (item: PlanItem) => {
return (
isLatestTask &&
isLatestRevision &&
!item.completed &&
item.index !== currentPlanItemIndex
);
};
const getItemState = ( const getItemState = (
item: PlanItem, item: PlanItem,
): "completed" | "current" | "remaining" => { ): "completed" | "current" | "remaining" => {
@ -148,34 +108,11 @@ export function TasksSidebar({
} }
}; };
const startEditing = (item: PlanItem) => {
setEditingPlanItem(item.index);
setEditingText(item.plan);
};
const saveEdit = () => {
if (editingPlanItem !== null && editingText.trim()) {
onEditPlanItem?.(currentTask.id, editingPlanItem, editingText.trim());
setEditingPlanItem(null);
setEditingText("");
}
};
const addNewPlanItem = () => {
if (newPlanItemText.trim()) {
onAddPlanItem?.(currentTask.id, newPlanItemText.trim());
setNewPlanItemText("");
setShowAddPlanItem(false);
}
};
const goToPreviousRevision = () => { const goToPreviousRevision = () => {
if (currentRevisionIndex > 0) { if (currentRevisionIndex > 0) {
const newIndex = currentRevisionIndex - 1; const newIndex = currentRevisionIndex - 1;
setCurrentRevisionIndex(newIndex); setCurrentRevisionIndex(newIndex);
onRevisionChange?.(currentTask.id, newIndex);
setExpandedSummaries(new Set()); setExpandedSummaries(new Set());
setEditingPlanItem(null);
} }
}; };
@ -183,18 +120,14 @@ export function TasksSidebar({
if (currentRevisionIndex < currentTask.planRevisions.length - 1) { if (currentRevisionIndex < currentTask.planRevisions.length - 1) {
const newIndex = currentRevisionIndex + 1; const newIndex = currentRevisionIndex + 1;
setCurrentRevisionIndex(newIndex); setCurrentRevisionIndex(newIndex);
onRevisionChange?.(currentTask.id, newIndex);
setExpandedSummaries(new Set()); setExpandedSummaries(new Set());
setEditingPlanItem(null);
} }
}; };
const goToLatestRevision = () => { const goToLatestRevision = () => {
const latestIndex = currentTask.activeRevisionIndex; const latestIndex = currentTask.activeRevisionIndex;
setCurrentRevisionIndex(latestIndex); setCurrentRevisionIndex(latestIndex);
onRevisionChange?.(currentTask.id, latestIndex);
setExpandedSummaries(new Set()); setExpandedSummaries(new Set());
setEditingPlanItem(null);
}; };
const formatDate = (timestamp: number) => { const formatDate = (timestamp: number) => {
@ -353,12 +286,6 @@ export function TasksSidebar({
<option value="pending">Pending</option> <option value="pending">Pending</option>
</select> </select>
</div> </div>
{isLatestTask && isLatestRevision && (
<span className="rounded bg-green-50 px-2 py-1 text-xs text-green-600">
Editable
</span>
)}
</div> </div>
</div> </div>
@ -373,8 +300,6 @@ export function TasksSidebar({
filteredItems.map((item) => { filteredItems.map((item) => {
const state = getItemState(item); const state = getItemState(item);
const isExpanded = expandedSummaries.has(item.index); const isExpanded = expandedSummaries.has(item.index);
const isEditing = editingPlanItem === item.index;
const editable = isPlanItemEditable(item);
return ( return (
<div <div
@ -392,188 +317,78 @@ export function TasksSidebar({
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
{isEditing ? ( <>
<div className="space-y-2"> <div className="mb-1 flex items-start justify-between gap-2">
<Textarea <p className="text-sm leading-relaxed text-gray-900">
value={editingText} {item.plan}
onChange={(e) => setEditingText(e.target.value)} </p>
className="min-h-[60px] text-sm"
placeholder="Enter plan item description..."
/>
<div className="flex items-center gap-2">
<Button
size="sm"
onClick={saveEdit}
className="h-7 text-xs"
>
<Save className="mr-1 h-3 w-3" />
Save
</Button>
<Button
size="sm"
variant="outline"
onClick={() => {
setEditingPlanItem(null);
setEditingText("");
}}
className="h-7 text-xs"
>
Cancel
</Button>
</div>
</div> </div>
) : (
<> <div className="flex items-center justify-between">
<div className="mb-1 flex items-start justify-between gap-2"> <span className="text-xs text-gray-500">
<p className="text-sm leading-relaxed text-gray-900"> Plan Item #{item.index + 1}
{item.plan} </span>
</p> <span
{editable && ( className={cn(
<div className="flex flex-shrink-0 items-center gap-1"> "rounded-full px-2 py-1 text-xs",
<Button state === "completed" &&
variant="ghost" "bg-green-100 text-green-700",
size="sm" state === "current" &&
onClick={() => startEditing(item)} "bg-blue-100 text-blue-700",
className="h-6 w-6 p-0" state === "remaining" &&
> "bg-gray-100 text-gray-700",
<Edit2 className="h-3 w-3" />
</Button>
<Button
variant="ghost"
size="sm"
onClick={() =>
onDeletePlanItem?.(
currentTask.id,
item.index,
)
}
className="h-6 w-6 p-0 text-red-500"
>
<Trash2 className="h-3 w-3" />
</Button>
</div>
)} )}
</div> >
{state === "completed"
? "Completed"
: state === "current"
? "In Progress"
: "Pending"}
</span>
</div>
<div className="flex items-center justify-between"> {item.completed && item.summary && (
<span className="text-xs text-gray-500"> <Collapsible
Plan Item #{item.index + 1} open={isExpanded}
</span> onOpenChange={() => {
<span const newExpanded = new Set(expandedSummaries);
className={cn( if (newExpanded.has(item.index)) {
"rounded-full px-2 py-1 text-xs", newExpanded.delete(item.index);
state === "completed" && } else {
"bg-green-100 text-green-700", newExpanded.add(item.index);
state === "current" && }
"bg-blue-100 text-blue-700", setExpandedSummaries(newExpanded);
state === "remaining" && }}
"bg-gray-100 text-gray-700", className="mt-2"
)} >
> <CollapsibleTrigger asChild>
{state === "completed" <Button
? "Completed" variant="ghost"
: state === "current" size="sm"
? "In Progress" className="h-6 p-0 text-xs text-green-700"
: "Pending"} >
</span> {isExpanded ? (
</div> <ChevronDown className="mr-1 h-3 w-3" />
) : (
{item.completed && item.summary && ( <ChevronRight className="mr-1 h-3 w-3" />
<Collapsible )}
open={isExpanded} View summary
onOpenChange={() => { </Button>
const newExpanded = new Set( </CollapsibleTrigger>
expandedSummaries, <CollapsibleContent className="mt-2">
); <div className="rounded border border-green-200 bg-green-50 p-2 text-xs text-green-800">
if (newExpanded.has(item.index)) { {item.summary}
newExpanded.delete(item.index); </div>
} else { </CollapsibleContent>
newExpanded.add(item.index); </Collapsible>
} )}
setExpandedSummaries(newExpanded); </>
}}
className="mt-2"
>
<CollapsibleTrigger asChild>
<Button
variant="ghost"
size="sm"
className="h-6 p-0 text-xs text-green-700"
>
{isExpanded ? (
<ChevronDown className="mr-1 h-3 w-3" />
) : (
<ChevronRight className="mr-1 h-3 w-3" />
)}
View summary
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="mt-2">
<div className="rounded border border-green-200 bg-green-50 p-2 text-xs text-green-800">
{item.summary}
</div>
</CollapsibleContent>
</Collapsible>
)}
</>
)}
</div> </div>
</div> </div>
</div> </div>
); );
}) })
)} )}
{/* Add New Plan Item - Only for latest task and revision */}
{isLatestTask &&
isLatestRevision &&
!planItems.every((item) => item.completed) && (
<div>
{showAddPlanItem ? (
<div className="rounded-lg border border-dashed border-gray-300 p-3">
<div className="space-y-2">
<Textarea
value={newPlanItemText}
onChange={(e) => setNewPlanItemText(e.target.value)}
placeholder="Enter new plan item description..."
className="min-h-[60px] text-sm"
/>
<div className="flex items-center gap-2">
<Button
size="sm"
onClick={addNewPlanItem}
disabled={!newPlanItemText.trim()}
className="h-7 text-xs"
>
<Plus className="mr-1 h-3 w-3" />
Add Plan Item
</Button>
<Button
size="sm"
variant="outline"
onClick={() => {
setShowAddPlanItem(false);
setNewPlanItemText("");
}}
className="h-7 text-xs"
>
Cancel
</Button>
</div>
</div>
</div>
) : (
<Button
variant="outline"
className="h-10 w-full border-dashed border-gray-300 text-sm"
onClick={() => setShowAddPlanItem(true)}
>
<Plus className="mr-2 h-4 w-4" />
Add New Plan Item
</Button>
)}
</div>
)}
</div> </div>
</div> </div>
</div> </div>
@ -582,14 +397,7 @@ export function TasksSidebar({
} }
// Main TaskPlan View Component // Main TaskPlan View Component
export function TaskPlanView({ export function TaskPlanView({ taskPlan, onTaskChange }: TaskPlanViewProps) {
taskPlan,
onTaskChange,
onRevisionChange,
onEditPlanItem,
onAddPlanItem,
onDeletePlanItem,
}: TaskPlanViewProps) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isSidebarOpen, setIsSidebarOpen] = useState(false);
if (taskPlan.tasks.length === 0) { if (taskPlan.tasks.length === 0) {
@ -616,10 +424,6 @@ export function TaskPlanView({
onClose={() => setIsSidebarOpen(false)} onClose={() => setIsSidebarOpen(false)}
taskPlan={taskPlan} taskPlan={taskPlan}
onTaskChange={onTaskChange} onTaskChange={onTaskChange}
onRevisionChange={onRevisionChange}
onEditPlanItem={onEditPlanItem}
onAddPlanItem={onAddPlanItem}
onDeletePlanItem={onDeletePlanItem}
/> />
</> </>
); );

View file

@ -14,102 +14,7 @@ export function useTaskPlan() {
} }
}, [values?.plan]); }, [values?.plan]);
const handleEditPlanItem = (
taskId: string,
planItemIndex: number,
newPlan: string,
) => {
setTaskPlan((prevTaskPlan) => {
const prevTaskPlan_ = prevTaskPlan ?? {
tasks: [],
activeTaskIndex: 0,
};
return {
...prevTaskPlan_,
tasks: prevTaskPlan_.tasks.map((task) => {
if (task.id === taskId) {
const updatedRevisions = [...task.planRevisions];
const activeRevision = updatedRevisions[task.activeRevisionIndex];
updatedRevisions[task.activeRevisionIndex] = {
...activeRevision,
plans: activeRevision.plans.map((item) =>
item.index === planItemIndex
? { ...item, plan: newPlan }
: item,
),
};
return { ...task, planRevisions: updatedRevisions };
}
return task;
}),
};
});
};
const handleAddPlanItem = (taskId: string, plan: string) => {
setTaskPlan((prevTaskPlan) => {
const prevTaskPlan_ = prevTaskPlan ?? {
tasks: [],
activeTaskIndex: 0,
};
return {
...prevTaskPlan_,
tasks: prevTaskPlan_.tasks.map((task) => {
if (task.id === taskId) {
const updatedRevisions = [...task.planRevisions];
const activeRevision = updatedRevisions[task.activeRevisionIndex];
const maxIndex = Math.max(
...activeRevision.plans.map((item) => item.index),
0,
);
const newPlanItem = {
index: maxIndex + 1,
plan,
completed: false,
};
updatedRevisions[task.activeRevisionIndex] = {
...activeRevision,
plans: [...activeRevision.plans, newPlanItem],
};
return { ...task, planRevisions: updatedRevisions };
}
return task;
}),
};
});
};
const handleDeletePlanItem = (taskId: string, planItemIndex: number) => {
setTaskPlan((prevTaskPlan) => {
const prevTaskPlan_ = prevTaskPlan ?? {
tasks: [],
activeTaskIndex: 0,
};
return {
...prevTaskPlan_,
tasks: prevTaskPlan_.tasks.map((task) => {
if (task.id === taskId) {
const updatedRevisions = [...task.planRevisions];
const activeRevision = updatedRevisions[task.activeRevisionIndex];
updatedRevisions[task.activeRevisionIndex] = {
...activeRevision,
plans: activeRevision.plans.filter(
(item) => item.index !== planItemIndex,
),
};
return { ...task, planRevisions: updatedRevisions };
}
return task;
}),
};
});
};
return { return {
taskPlan, taskPlan,
handleEditPlanItem,
handleAddPlanItem,
handleDeletePlanItem,
}; };
} }

View file

@ -103,12 +103,7 @@ export function Thread() {
const [artifactOpen, closeArtifact] = useArtifactOpen(); const [artifactOpen, closeArtifact] = useArtifactOpen();
const { selectedRepository } = useGitHubAppProvider(); const { selectedRepository } = useGitHubAppProvider();
const { getConfig } = useConfigStore(); const { getConfig } = useConfigStore();
const { const { taskPlan } = useTaskPlan();
taskPlan,
handleEditPlanItem,
handleAddPlanItem,
handleDeletePlanItem,
} = useTaskPlan();
const [threadId, _setThreadId] = useQueryState("threadId"); const [threadId, _setThreadId] = useQueryState("threadId");
const [taskId, setTaskId] = useQueryState("taskId", parseAsString); const [taskId, setTaskId] = useQueryState("taskId", parseAsString);
@ -440,10 +435,6 @@ export function Thread() {
<TaskPlanView <TaskPlanView
taskPlan={taskPlan} taskPlan={taskPlan}
onTaskChange={() => {}} onTaskChange={() => {}}
onRevisionChange={() => {}}
onEditPlanItem={handleEditPlanItem}
onAddPlanItem={handleAddPlanItem}
onDeletePlanItem={handleDeletePlanItem}
/> />
)} )}
</div> </div>