From 3f309163b694a1deb18bdc4bb78c116b2f237978 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Nov 2025 09:01:45 +0000 Subject: [PATCH] Add status change dropdown and aggregated time estimates Features: - Add "Change Status" option to TaskMenu dropdown - Allows changing task status (backlog/in progress/blocked/done) from tree view - Shows current status with checkmark - No longer need to switch to Kanban view to change status - Implement recursive time aggregation for subtasks - Tasks now show total time including all descendant subtasks - Display format varies based on estimates: - "1.5h" - only task's own estimate - "(2h from subtasks)" - only subtask estimates - "1h (3h total)" - both own and subtask estimates - Works in both TreeView (hierarchical) and KanbanView (flat list) - New utility functions: calculateTotalTime, calculateTotalTimeFlat, formatTimeWithTotal This allows better project planning by showing total time investment for tasks with subtasks. --- frontend/src/components/KanbanView.jsx | 8 ++-- frontend/src/components/TaskMenu.jsx | 58 +++++++++++++++++++++++++- frontend/src/components/TreeView.jsx | 8 ++-- frontend/src/utils/format.js | 56 +++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/KanbanView.jsx b/frontend/src/components/KanbanView.jsx index fe8f91f..9d63c29 100644 --- a/frontend/src/components/KanbanView.jsx +++ b/frontend/src/components/KanbanView.jsx @@ -6,7 +6,7 @@ import { updateTask, deleteTask } from '../utils/api' -import { formatTime } from '../utils/format' +import { formatTimeWithTotal } from '../utils/format' import TaskMenu from './TaskMenu' const STATUSES = [ @@ -106,13 +106,13 @@ function TaskCard({ task, allTasks, onUpdate, onDragStart }) { )} {/* Metadata row */} - {(task.estimated_minutes || (task.tags && task.tags.length > 0)) && ( + {(formatTimeWithTotal(task, allTasks) || (task.tags && task.tags.length > 0)) && (
{/* Time estimate */} - {task.estimated_minutes && ( + {formatTimeWithTotal(task, allTasks) && (
- {formatTime(task.estimated_minutes)} + {formatTimeWithTotal(task, allTasks)}
)} diff --git a/frontend/src/components/TaskMenu.jsx b/frontend/src/components/TaskMenu.jsx index 4224864..ddd08a2 100644 --- a/frontend/src/components/TaskMenu.jsx +++ b/frontend/src/components/TaskMenu.jsx @@ -1,5 +1,5 @@ import { useState, useRef, useEffect } from 'react' -import { MoreVertical, Clock, Tag, Flag, Edit2, Trash2, X, Check } from 'lucide-react' +import { MoreVertical, Clock, Tag, Flag, Edit2, Trash2, X, Check, ListTodo } from 'lucide-react' import { updateTask } from '../utils/api' const FLAG_COLORS = [ @@ -12,11 +12,19 @@ const FLAG_COLORS = [ { name: 'pink', color: 'bg-pink-500' } ] +const STATUSES = [ + { key: 'backlog', label: 'Backlog', color: 'text-gray-400' }, + { key: 'in_progress', label: 'In Progress', color: 'text-blue-400' }, + { key: 'blocked', label: 'Blocked', color: 'text-red-400' }, + { key: 'done', label: 'Done', color: 'text-green-400' } +] + function TaskMenu({ task, onUpdate, onDelete, onEdit }) { const [isOpen, setIsOpen] = useState(false) const [showTimeEdit, setShowTimeEdit] = useState(false) const [showTagsEdit, setShowTagsEdit] = useState(false) const [showFlagEdit, setShowFlagEdit] = useState(false) + const [showStatusEdit, setShowStatusEdit] = useState(false) const [editTime, setEditTime] = useState(task.estimated_minutes || '') const [editTags, setEditTags] = useState(task.tags ? task.tags.join(', ') : '') const menuRef = useRef(null) @@ -28,6 +36,7 @@ function TaskMenu({ task, onUpdate, onDelete, onEdit }) { setShowTimeEdit(false) setShowTagsEdit(false) setShowFlagEdit(false) + setShowStatusEdit(false) } } @@ -85,6 +94,17 @@ function TaskMenu({ task, onUpdate, onDelete, onEdit }) { } } + const handleUpdateStatus = async (newStatus) => { + try { + await updateTask(task.id, { status: newStatus }) + setShowStatusEdit(false) + setIsOpen(false) + onUpdate() + } catch (err) { + alert(`Error: ${err.message}`) + } + } + return (
+ ))} +
+
+ ) : ( + + )} + {/* Edit Title */}