Add Save button to task edit dialog - changes only save when clicked

This commit is contained in:
OpenClaw Bot 2026-02-19 20:02:04 -06:00
parent 33d8822b45
commit 1db22ef509

View File

@ -91,12 +91,20 @@ export default function Home() {
const [newComment, setNewComment] = useState("") const [newComment, setNewComment] = useState("")
const [editingTask, setEditingTask] = useState<Task | null>(null) const [editingTask, setEditingTask] = useState<Task | null>(null)
const [viewMode, setViewMode] = useState<'kanban' | 'backlog'>('kanban') const [viewMode, setViewMode] = useState<'kanban' | 'backlog'>('kanban')
const [editedTask, setEditedTask] = useState<Task | null>(null)
// Sync from server on mount // Sync from server on mount
useEffect(() => { useEffect(() => {
syncFromServer() syncFromServer()
}, [syncFromServer]) }, [syncFromServer])
// Set editedTask when selectedTask changes
useEffect(() => {
if (selectedTask) {
setEditedTask({ ...selectedTask })
}
}, [selectedTask])
const selectedProject = projects.find((p) => p.id === selectedProjectId) const selectedProject = projects.find((p) => p.id === selectedProjectId)
const selectedTask = tasks.find((t) => t.id === selectedTaskId) const selectedTask = tasks.find((t) => t.id === selectedTaskId)
const projectTasks = selectedProjectId ? getTasksByProject(selectedProjectId) : [] const projectTasks = selectedProjectId ? getTasksByProject(selectedProjectId) : []
@ -528,47 +536,41 @@ export default function Home() {
</Dialog> </Dialog>
{/* Task Detail Dialog with Comments */} {/* Task Detail Dialog with Comments */}
<Dialog open={!!selectedTaskId} onOpenChange={() => selectTask(null)}> <Dialog open={!!selectedTaskId} onOpenChange={() => {
selectTask(null)
setEditedTask(null)
}}>
<DialogContent className="bg-slate-900 border-slate-800 text-white w-[95vw] max-w-2xl max-h-[90vh] overflow-y-auto p-4 md:p-6"> <DialogContent className="bg-slate-900 border-slate-800 text-white w-[95vw] max-w-2xl max-h-[90vh] overflow-y-auto p-4 md:p-6">
{selectedTask && ( {selectedTask && editedTask && (
<> <>
<DialogHeader> <DialogHeader>
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center gap-2 mb-2">
<Badge className={`${typeColors[selectedTask.type]} text-white border-0`}> <Badge className={`${typeColors[editedTask.type]} text-white border-0`}>
{typeLabels[selectedTask.type]} {typeLabels[editedTask.type]}
</Badge> </Badge>
<Badge variant="outline" className={priorityColors[selectedTask.priority]}> <Badge variant="outline" className={priorityColors[editedTask.priority]}>
{selectedTask.priority} {editedTask.priority}
</Badge> </Badge>
</div> </div>
<DialogTitle className="text-xl">{selectedTask.title}</DialogTitle> <input
type="text"
value={editedTask.title}
onChange={(e) => setEditedTask({ ...editedTask, title: e.target.value })}
className="w-full text-xl font-semibold bg-slate-800 border border-slate-700 rounded px-3 py-2 text-white"
/>
</DialogHeader> </DialogHeader>
<div className="space-y-6 py-4"> <div className="space-y-6 py-4">
{/* Description */} {/* Description */}
{selectedTask.description && ( <div>
<div> <Label className="text-slate-400">Description</Label>
<Label className="text-slate-400">Description</Label> <textarea
<p className="mt-2 text-slate-300 whitespace-pre-wrap"> value={editedTask.description || ""}
{selectedTask.description} onChange={(e) => setEditedTask({ ...editedTask, description: e.target.value })}
</p> rows={3}
</div> className="w-full mt-2 px-3 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white"
)} />
</div>
{/* Tags */}
{selectedTask.tags.length > 0 && (
<div>
<Label className="text-slate-400">Tags</Label>
<div className="flex flex-wrap gap-2 mt-2">
{selectedTask.tags.map((tag) => (
<Badge key={tag} variant="secondary" className="bg-slate-800 text-slate-300">
<Tag className="w-3 h-3 mr-1" />
{tag}
</Badge>
))}
</div>
</div>
)}
{/* Priority */} {/* Priority */}
<div> <div>
@ -577,9 +579,9 @@ export default function Home() {
{(["low", "medium", "high", "urgent"] as Priority[]).map((priority) => ( {(["low", "medium", "high", "urgent"] as Priority[]).map((priority) => (
<button <button
key={priority} key={priority}
onClick={() => updateTask(selectedTask.id, { priority })} onClick={() => setEditedTask({ ...editedTask, priority })}
className={`px-3 py-1.5 rounded-lg text-sm transition-colors capitalize ${ className={`px-3 py-1.5 rounded-lg text-sm transition-colors capitalize ${
selectedTask.priority === priority editedTask.priority === priority
? priority === "urgent" ? priority === "urgent"
? "bg-red-600 text-white" ? "bg-red-600 text-white"
: priority === "high" : priority === "high"
@ -600,8 +602,8 @@ export default function Home() {
<div> <div>
<Label className="text-slate-400">Status</Label> <Label className="text-slate-400">Status</Label>
<select <select
value={selectedTask.status} value={editedTask.status}
onChange={(e) => handleUpdateTaskStatus(selectedTask.id, e.target.value as TaskStatus)} onChange={(e) => setEditedTask({ ...editedTask, status: e.target.value as TaskStatus })}
className="w-full mt-2 px-3 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-blue-500" className="w-full mt-2 px-3 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-blue-500"
> >
{allStatuses.map((status) => ( {allStatuses.map((status) => (
@ -614,8 +616,8 @@ export default function Home() {
<div> <div>
<Label className="text-slate-400">Sprint</Label> <Label className="text-slate-400">Sprint</Label>
<select <select
value={selectedTask.sprintId || ""} value={editedTask.sprintId || ""}
onChange={(e) => updateTask(selectedTask.id, { sprintId: e.target.value || undefined })} onChange={(e) => setEditedTask({ ...editedTask, sprintId: e.target.value || undefined })}
className="w-full mt-2 px-3 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-blue-500" className="w-full mt-2 px-3 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-blue-500"
> >
<option value="">No Sprint</option> <option value="">No Sprint</option>
@ -631,15 +633,15 @@ export default function Home() {
<div className="border-t border-slate-800 pt-6"> <div className="border-t border-slate-800 pt-6">
<h4 className="font-medium text-white mb-4 flex items-center gap-2"> <h4 className="font-medium text-white mb-4 flex items-center gap-2">
<MessageSquare className="w-4 h-4" /> <MessageSquare className="w-4 h-4" />
Notes & Comments ({selectedTask.comments.length}) Notes & Comments ({editedTask.comments.length})
</h4> </h4>
{/* Comment List */} {/* Comment List */}
<div className="space-y-4 mb-4"> <div className="space-y-4 mb-4">
{selectedTask.comments.length === 0 ? ( {editedTask.comments.length === 0 ? (
<p className="text-slate-500 text-sm">No notes yet. Add the first one!</p> <p className="text-slate-500 text-sm">No notes yet. Add the first one!</p>
) : ( ) : (
selectedTask.comments.map((comment) => ( editedTask.comments.map((comment) => (
<div <div
key={comment.id} key={comment.id}
className={`flex gap-3 p-3 rounded-lg ${ className={`flex gap-3 p-3 rounded-lg ${
@ -707,15 +709,25 @@ export default function Home() {
onClick={() => { onClick={() => {
deleteTask(selectedTask.id) deleteTask(selectedTask.id)
selectTask(null) selectTask(null)
setEditedTask(null)
}} }}
> >
<Trash2 className="w-4 h-4 mr-2" /> <Trash2 className="w-4 h-4 mr-2" />
Delete Task Delete Task
</Button> </Button>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="text-sm text-slate-500">Changes saved automatically</span> <Button variant="ghost" onClick={() => {
<Button onClick={() => selectTask(null)}> selectTask(null)
Close setEditedTask(null)
}}>
Cancel
</Button>
<Button onClick={() => {
updateTask(editedTask.id, editedTask)
selectTask(null)
setEditedTask(null)
}}>
Save Changes
</Button> </Button>
</div> </div>
</DialogFooter> </DialogFooter>