Fix tree view indentation and kanban subtask handling

Tree View:
- Fix nested task indentation to scale properly with depth (1.5rem per level)
- Sub-subtasks now indent correctly relative to their parents

Kanban View:
- Show ALL tasks as draggable cards (not just root tasks)
- Subtasks display parent task name for context
- Each subtask can be independently dragged to different status columns
- Remove nested subtask expansion since all tasks are now individual cards
This commit is contained in:
Claude
2025-11-20 01:50:23 +00:00
parent 855b91ba78
commit f3fc87e715
2 changed files with 19 additions and 36 deletions

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { Plus, Edit2, Trash2, Check, X, ChevronDown, ChevronRight } from 'lucide-react' import { Plus, Edit2, Trash2, Check, X } from 'lucide-react'
import { import {
getProjectTasks, getProjectTasks,
createTask, createTask,
@@ -17,10 +17,11 @@ const STATUSES = [
function TaskCard({ task, allTasks, onUpdate, onDragStart }) { function TaskCard({ task, allTasks, onUpdate, onDragStart }) {
const [isEditing, setIsEditing] = useState(false) const [isEditing, setIsEditing] = useState(false)
const [editTitle, setEditTitle] = useState(task.title) const [editTitle, setEditTitle] = useState(task.title)
const [showSubtasks, setShowSubtasks] = useState(false)
const subtasks = allTasks.filter(t => t.parent_task_id === task.id) // Find parent task if this is a subtask
const hasSubtasks = subtasks.length > 0 const parentTask = task.parent_task_id
? allTasks.find(t => t.id === task.parent_task_id)
: null
const handleSave = async () => { const handleSave = async () => {
try { try {
@@ -75,8 +76,15 @@ function TaskCard({ task, allTasks, onUpdate, onDragStart }) {
</div> </div>
) : ( ) : (
<> <>
<div className="flex justify-between items-start mb-2"> <div className="flex justify-between items-start">
<span className="text-gray-200 text-sm flex-1">{task.title}</span> <div className="flex-1">
<div className="text-gray-200 text-sm">{task.title}</div>
{parentTask && (
<div className="text-xs text-gray-500 mt-1">
subtask of: <span className="text-cyber-orange">{parentTask.title}</span>
</div>
)}
</div>
<div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity"> <div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<button <button
onClick={() => setIsEditing(true)} onClick={() => setIsEditing(true)}
@@ -92,27 +100,6 @@ function TaskCard({ task, allTasks, onUpdate, onDragStart }) {
</button> </button>
</div> </div>
</div> </div>
{hasSubtasks && (
<div className="mt-2 pt-2 border-t border-cyber-orange/20">
<button
onClick={() => setShowSubtasks(!showSubtasks)}
className="flex items-center gap-1 text-xs text-cyber-orange hover:text-cyber-orange-bright"
>
{showSubtasks ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
{subtasks.length} subtask{subtasks.length !== 1 ? 's' : ''}
</button>
{showSubtasks && (
<div className="mt-2 pl-3 space-y-1">
{subtasks.map(subtask => (
<div key={subtask.id} className="text-xs text-gray-400">
{subtask.title}
</div>
))}
</div>
)}
</div>
)}
</> </>
)} )}
</div> </div>
@@ -255,9 +242,6 @@ function KanbanView({ projectId }) {
return <div className="text-center text-red-400 py-12">{error}</div> return <div className="text-center text-red-400 py-12">{error}</div>
} }
// Only show root-level tasks in Kanban (tasks without parents)
const rootTasks = allTasks.filter(t => !t.parent_task_id)
return ( return (
<div> <div>
<h3 className="text-xl font-semibold text-gray-300 mb-4">Kanban Board</h3> <h3 className="text-xl font-semibold text-gray-300 mb-4">Kanban Board</h3>
@@ -267,7 +251,7 @@ function KanbanView({ projectId }) {
<KanbanColumn <KanbanColumn
key={status.key} key={status.key}
status={status} status={status}
tasks={rootTasks.filter(t => t.status === status.key)} tasks={allTasks.filter(t => t.status === status.key)}
allTasks={allTasks} allTasks={allTasks}
projectId={projectId} projectId={projectId}
onUpdate={loadTasks} onUpdate={loadTasks}
@@ -277,7 +261,7 @@ function KanbanView({ projectId }) {
))} ))}
</div> </div>
{rootTasks.length === 0 && ( {allTasks.length === 0 && (
<div className="text-center py-16 text-gray-500"> <div className="text-center py-16 text-gray-500">
<p className="text-lg mb-2">No tasks yet</p> <p className="text-lg mb-2">No tasks yet</p>
<p className="text-sm">Add tasks using the + button in any column</p> <p className="text-sm">Add tasks using the + button in any column</p>

View File

@@ -85,9 +85,8 @@ function TaskNode({ task, projectId, onUpdate, level = 0 }) {
return ( return (
<div className="mb-2"> <div className="mb-2">
<div <div
className={`flex items-center gap-2 p-3 bg-cyber-darkest border border-cyber-orange/20 rounded hover:border-cyber-orange/40 transition-all group ${ style={{ marginLeft: `${level * 1.5}rem` }}
level > 0 ? 'ml-6' : '' className="flex items-center gap-2 p-3 bg-cyber-darkest border border-cyber-orange/20 rounded hover:border-cyber-orange/40 transition-all group"
}`}
> >
{/* Expand/Collapse */} {/* Expand/Collapse */}
{hasSubtasks && ( {hasSubtasks && (
@@ -176,7 +175,7 @@ function TaskNode({ task, projectId, onUpdate, level = 0 }) {
{/* Add Subtask Form */} {/* Add Subtask Form */}
{showAddSubtask && ( {showAddSubtask && (
<div className={`mt-2 ${level > 0 ? 'ml-6' : ''}`}> <div style={{ marginLeft: `${level * 1.5}rem` }} className="mt-2">
<form onSubmit={handleAddSubtask} className="flex gap-2"> <form onSubmit={handleAddSubtask} className="flex gap-2">
<input <input
type="text" type="text"