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