1→import { useEffect, useState, useMemo } from 'react'
     2→import { useSearchParams } from 'react-router-dom'
     3→import { Plus, X, GripVertical, Clock, User, RefreshCw, Flag, Pencil, Check, Trash2, Filter, ArrowUpDown, LayoutGrid, List, ChevronLeft, ChevronRight, Link2, Layers } from 'lucide-react'
     4→import * as Dialog from '@radix-ui/react-dialog'
     5→import * as AlertDialog from '@radix-ui/react-alert-dialog'
     6→import * as Tooltip from '@radix-ui/react-tooltip'
     7→import { useProjectStore } from '../stores/projectStore'
     8→import { useTaskStore, Task, TaskCreate, TaskUpdate } from '../stores/taskStore'
     9→import { usePhaseStore } from '../stores/phaseStore'
    10→import { useToastStore } from '../components/Toast'
    11→
    12→const ITEMS_PER_PAGE = 10
    13→
    14→const COLUMNS = [
    15→  { id: 'backlog', title: 'Backlog', color: 'border-slate-500' },
    16→  { id: 'in_progress', title: 'In Progress', color: 'border-accent' },
    17→  { id: 'qa', title: 'QA', color: 'border-primary-400' },
    18→  { id: 'done', title: 'Done', color: 'border-green-400' },
    19→  { id: 'blocked', title: 'Blocked', color: 'border-destructive' },
    20→]
    21→
    22→const PRIORITY_LABELS: Record<number, string> = {
    23→  0: 'Low',
    24→  1: 'Medium',
    25→  2: 'High',
    26→  3: 'Critical',
    27→}
    28→
    29→const STATUS_LABELS: Record<string, string> = {
    30→  backlog: 'Backlog',
    31→  in_progress: 'In Progress',
    32→  qa: 'QA',
    33→  done: 'Done',
    34→  blocked: 'Blocked',
    35→}
    36→
    37→export default function KanbanBoard() {
    38→  const [searchParams, setSearchParams] = useSearchParams()
    39→  const { currentProject } = useProjectStore()
    40→  const { tasks, fetchTasks, createTask, updateTask, updateTaskStatus, deleteTask, isTaskDeleting, isLoading } = useTaskStore()
    41→  const { phases, fetchPhases } = usePhaseStore()
    42→  const { addToast } = useToastStore()
    43→  const [showAddDialog, setShowAddDialog] = useState(false)
    44→  const [newTaskName, setNewTaskName] = useState('')
    45→  const [newTaskDescription, setNewTaskDescription] = useState('')
    46→  const [newTaskPriority, setNewTaskPriority] = useState(0)
    47→  const [newTaskPhaseId, setNewTaskPhaseId] = useState<number | null>(null)
    48→  const [isCreating, setIsCreating] = useState(false)
    49→  const [draggedTask, setDraggedTask] = useState<number | null>(null)
    50→  const [selectedTask, setSelectedTask] = useState<Task | null>(null)
    51→  const [isEditing, setIsEditing] = useState(false)
    52→  const [editName, setEditName] = useState('')
    53→  const [editDescription, setEditDescription] = useState('')
    54→  const [editPriority, setEditPriority] = useState(0)
    55→  const [editPhaseId, setEditPhaseId] = useState<number | null>(null)
    56→  const [isSaving, setIsSaving] = useState(false)
    57→  const [showDeleteDialog, setShowDeleteDialog] = useState(false)
    58→  const [isDeleting, setIsDeleting] = useState(false)
    59→  const [showUnsavedDialog, setShowUnsavedDialog] = useState(false)
    60→  const [pendingCloseAction, setPendingCloseAction] = useState<'close' | 'cancel' | null>(null)
    61→  // Filter and Sort state - initialize from URL params
    62→  const getInitialPriorityFilter = (): number | 'all' => {
    63→    const param = searchParams.get('priority')
    64→    if (param === 'all' || param === null) return 'all'
    65→    const num = parseInt(param, 10)
    66→    return [0, 1, 2, 3].includes(num) ? num : 'all'
    67→  }
    68→  const getInitialStatusFilter = (): string | 'all' => {
    69→    const param = searchParams.get('status')
    70→    if (param === 'all' || param === null) return 'all'
    71→    const validStatuses = ['backlog', 'in_progress', 'qa', 'done', 'blocked']
    72→    return validStatuses.includes(param) ? param : 'all'
    73→  }
    74→  const getInitialSortOrder = (): 'newest' | 'oldest' | 'priority' => {
    75→    const param = searchParams.get('sort')
    76→    if (param === 'newest' || param === 'oldest' || param === 'priority') return param
    77→    return 'newest'
    78→  }
    79→  const getInitialAgentTypeFilter = (): string | 'all' => {
    80→    const param = searchParams.get('agent')
    81→    if (param === 'all' || param === null) return 'all'
    82→    return param
    83→  }
    84→  const getInitialViewMode = (): 'kanban' | 'list' => {
    85→    const param = searchParams.get('view')
    86→    if (param === 'list') return 'list'
    87→    return 'kanban'
    88→  }
    89→
    90→  const [priorityFilter, setPriorityFilter] = useState<number | 'all'>(getInitialPriorityFilter)
    91→  const [statusFilter, setStatusFilter] = useState<string | 'all'>(getInitialStatusFilter)
    92→  const [sortOrder, setSortOrder] = useState<'newest' | 'oldest' | 'priority'>(getInitialSortOrder)
    93→  const [agentTypeFilter, setAgentTypeFilter] = useState<string | 'all'>(getInitialAgentTypeFilter)
    94→  // View mode and pagination state
    95→  const [viewMode, setViewMode] = useState<'kanban' | 'list'>(getInitialViewMode)
    96→  // Phase grouping state
    97→  const [phaseGrouping, setPhaseGrouping] = useState<boolean>(() => searchParams.get('groupByPhase') === 'true')
    98→  const [currentPage, setCurrentPage] = useState(1)
    99→
   100→  useEffect(() => {
   101→    if (currentProject?.id) {
   102→      fetchTasks(currentProject.id)
   103→      fetchPhases(currentProject.id)
   104→    }
   105→  }, [currentProject?.id, fetchTasks, fetchPhases])
   106→
   107→  // Handle deep linking - select task from URL param after tasks are loaded
   108→  useEffect(() => {
   109→    const taskIdParam = searchParams.get('task')
   110→    if (taskIdParam && tasks.length > 0) {
   111→      const taskId = parseInt(taskIdParam, 10)
   112→      const task = tasks.find(t => t.id === taskId)
   113→      if (task) {
   114→        setSelectedTask(task)
   115→      }
   116→    }
   117→  }, [searchParams, tasks])
   118→
   119→  // Update URL when task is selected
   120→  const handleSelectTask = (task: Task) => {
   121→    setSelectedTask(task)
   122→    const newParams = new URLSearchParams(searchParams)
   123→    newParams.set('task', task.id.toString())
   124→    setSearchParams(newParams)
   125→  }
   126→
   127→  // Clear task from URL when panel is closed
   128→  const clearTaskFromUrl = () => {
   129→    const newParams = new URLSearchParams(searchParams)
   130→    newParams.delete('task')
   131→    setSearchParams(newParams)
   132→  }
   133→
   134→  // Update URL when filter changes
   135→  const handlePriorityFilterChange = (value: number | 'all') => {
   136→    setPriorityFilter(value)
   137→    const newParams = new URLSearchParams(searchParams)
   138→    if (value === 'all') {
   139→      newParams.delete('priority')
   140→    } else {
   141→      newParams.set('priority', value.toString())
   142→    }
   143→    setSearchParams(newParams)
   144→  }
   145→
   146→  // Update URL when status filter changes
   147→  const handleStatusFilterChange = (value: string | 'all') => {
   148→    setStatusFilter(value)
   149→    const newParams = new URLSearchParams(searchParams)
   150→    if (value === 'all') {
   151→      newParams.delete('status')
   152→    } else {
   153→      newParams.set('status', value)
   154→    }
   155→    setSearchParams(newParams)
   156→  }
   157→
   158→  // Update URL when sort changes
   159→  const handleSortOrderChange = (value: 'newest' | 'oldest' | 'priority') => {
   160→    setSortOrder(value)
   161→    const newParams = new URLSearchParams(searchParams)
   162→    if (value === 'newest') {
   163→      newParams.delete('sort') // default value, no need to store
   164→    } else {
   165→      newParams.set('sort', value)
   166→    }
   167→    setSearchParams(newParams)
   168→  }
   169→
   170→  // Update URL when agent type filter changes
   171→  const handleAgentTypeFilterChange = (value: string | 'all') => {
   172→    setAgentTypeFilter(value)
   173→    const newParams = new URLSearchParams(searchParams)
   174→    if (value === 'all') {
   175→      newParams.delete('agent')
   176→    } else {
   177→      newParams.set('agent', value)
   178→    }
   179→    setSearchParams(newParams)
   180→  }
   181→
   182→  // Update URL when view mode changes
   183→  const handleViewModeChange = (value: 'kanban' | 'list') => {
   184→    setViewMode(value)
   185→    const newParams = new URLSearchParams(searchParams)
   186→    if (value === 'kanban') {
   187→      newParams.delete('view') // default value, no need to store
   188→    } else {
   189→      newParams.set('view', value)
   190→    }
   191→    setSearchParams(newParams)
   192→  }
   193→
   194→  // Reset all filters to defaults
   195→  const handleResetFilters = () => {
   196→    setPriorityFilter('all')
   197→    setStatusFilter('all')
   198→    setAgentTypeFilter('all')
   199→    setSortOrder('newest')
   200→    setCurrentPage(1)
   201→    const newParams = new URLSearchParams(searchParams)
   202→    newParams.delete('priority')
   203→    newParams.delete('status')
   204→    newParams.delete('agent')
   205→    newParams.delete('sort')
   206→    setSearchParams(newParams)
   207→  }
   208→
   209→  // Check if filters are active (non-default)
   210→  const hasActiveFilters = priorityFilter !== 'all' || statusFilter !== 'all' || agentTypeFilter !== 'all' || sortOrder !== 'newest'
   211→
   212→  // Filtered and sorted tasks
   213→  const filteredAndSortedTasks = useMemo(() => {
   214→    let result = [...tasks]
   215→
   216→    // Apply priority filter
   217→    if (priorityFilter !== 'all') {
   218→      result = result.filter(task => task.priority === priorityFilter)
   219→    }
   220→
   221→    // Apply status filter
   222→    if (statusFilter !== 'all') {
   223→      result = result.filter(task => task.status === statusFilter)
   224→    }
   225→
   226→    // Apply agent type filter
   227→    if (agentTypeFilter !== 'all') {
   228→      result = result.filter(task => task.agent_type === agentTypeFilter)
   229→    }
   230→
   231→    // Apply sort
   232→    result.sort((a, b) => {
   233→      if (sortOrder === 'newest') {
   234→        return new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
   235→      } else if (sortOrder === 'oldest') {
   236→        return new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
   237→      } else {
   238→        // priority - highest first
   239→        return b.priority - a.priority
   240→      }
   241→    })
   242→
   243→    return result
   244→  }, [tasks, priorityFilter, statusFilter, agentTypeFilter, sortOrder])
   245→
   246→  // Pagination calculations
   247→  const totalPages = Math.ceil(filteredAndSortedTasks.length / ITEMS_PER_PAGE)
   248→  const paginatedTasks = useMemo(() => {
   249→    const startIndex = (currentPage - 1) * ITEMS_PER_PAGE
   250→    return filteredAndSortedTasks.slice(startIndex, startIndex + ITEMS_PER_PAGE)
   251→  }, [filteredAndSortedTasks, currentPage])
   252→
   253→  // Reset page when filter changes
   254→  useEffect(() => {
   255→    setCurrentPage(1)
   256→  }, [priorityFilter, statusFilter, agentTypeFilter, sortOrder])
   257→
   258→  const getTasksByStatus = (status: string) => {
   259→    return filteredAndSortedTasks.filter((task) => task.status === status)
   260→  }
   261→
   262→  // Helper to get dependency task names
   263→  const getDependencyNames = (dependsOn: number[] | null): string[] => {
   264→    if (!dependsOn || dependsOn.length === 0) return []
   265→    return dependsOn
   266→      .map(taskId => tasks.find(t => t.id === taskId)?.name || `Task #${taskId}`)
   267→  }
   268→
   269→  const handleCreateTask = async () => {
   270→    if (!currentProject || !newTaskName.trim()) return
   271→    setIsCreating(true)
   272→    try {
   273→      const taskData: TaskCreate = {
   274→        name: newTaskName.trim(),
   275→        description: newTaskDescription.trim() || undefined,
   276→        priority: newTaskPriority,
   277→        phase_id: newTaskPhaseId,
   278→      }
   279→      await createTask(currentProject.id, taskData)
   280→      setNewTaskName('')
   281→      setNewTaskDescription('')
   282→      setNewTaskPriority(0)
   283→      setNewTaskPhaseId(null)
   284→      setShowAddDialog(false)
   285→    } catch (error) {
   286→      console.error('Failed to create task:', error)
   287→      addToast('error', 'Create Failed', 'Failed to create task. Please try again.')
   288→    } finally {
   289→      setIsCreating(false)
   290→    }
   291→  }
   292→
   293→  const handleDragStart = (taskId: number) => {
   294→    setDraggedTask(taskId)
   295→  }
   296→
   297→  const handleDragOver = (e: React.DragEvent) => {
   298→    e.preventDefault()
   299→  }
   300→
   301→  const handleDrop = async (targetStatus: string) => {
   302→    if (draggedTask === null) return
   303→    try {
   304→      await updateTaskStatus(draggedTask, targetStatus)
   305→    } catch (error) {
   306→      console.error('Failed to update task status:', error)
   307→      addToast('error', 'Update Failed', 'Failed to update task status. Please try again.')
   308→    }
   309→    setDraggedTask(null)
   310→  }
   311→
   312→  const handleStartEdit = () => {
   313→    if (!selectedTask) return
   314→    setEditName(selectedTask.name)
   315→    setEditDescription(selectedTask.description || '')
   316→    setEditPriority(selectedTask.priority)
   317→    setEditPhaseId(selectedTask.phase_id)
   318→    setIsEditing(true)
   319→  }
   320→
   321→  // Check if edit form has unsaved changes
   322→  const hasUnsavedChanges = () => {
   323→    if (!selectedTask || !isEditing) return false
   324→    return (
   325→      editName.trim() !== selectedTask.name ||
   326→      (editDescription.trim() || '') !== (selectedTask.description || '') ||
   327→      editPriority !== selectedTask.priority ||
   328→      editPhaseId !== selectedTask.phase_id
   329→    )
   330→  }
   331→
   332→  const handleCancelEdit = () => {
   333→    if (hasUnsavedChanges()) {
   334→      setPendingCloseAction('cancel')
   335→      setShowUnsavedDialog(true)
   336→    } else {
   337→      setIsEditing(false)
   338→    }
   339→  }
   340→
   341→  const handleClosePanel = () => {
   342→    if (hasUnsavedChanges()) {
   343→      setPendingCloseAction('close')
   344→      setShowUnsavedDialog(true)
   345→    } else {
   346→      setSelectedTask(null)
   347→      setIsEditing(false)
   348→      clearTaskFromUrl()
   349→    }
   350→  }
   351→
   352→  const handleDiscardChanges = () => {
   353→    setShowUnsavedDialog(false)
   354→    if (pendingCloseAction === 'close') {
   355→      setSelectedTask(null)
   356→      clearTaskFromUrl()
   357→    }
   358→    setIsEditing(false)
   359→    setPendingCloseAction(null)
   360→  }
   361→
   362→  const handleKeepEditing = () => {
   363→    setShowUnsavedDialog(false)
   364→    setPendingCloseAction(null)
   365→  }
   366→
   367→  const handleSaveEdit = async () => {
   368→    if (!selectedTask) return
   369→    setIsSaving(true)
   370→    try {
   371→      const updates: TaskUpdate = {
   372→        name: editName.trim(),
   373→        description: editDescription.trim() || undefined,
   374→        priority: editPriority,
   375→        phase_id: editPhaseId,
   376→      }
   377→      const updatedTask = await updateTask(selectedTask.id, updates)
   378→      setSelectedTask(updatedTask)
   379→      setIsEditing(false)
   380→    } catch (error) {
   381→      console.error('Failed to update task:', error)
   382→      addToast('error', 'Save Failed', 'Failed to save task changes. Please try again.')
   383→    } finally {
   384→      setIsSaving(false)
   385→    }
   386→  }
   387→
   388→  const handleDeleteTask = async () => {
   389→    if (!selectedTask) return
   390→    // Check if already being deleted (prevents rapid clicks)
   391→    if (isTaskDeleting(selectedTask.id)) return
   392→
   393→    const taskIdToDelete = selectedTask.id
   394→    const taskName = selectedTask.name
   395→    setIsDeleting(true)
   396→    // Close dialog immediately to prevent additional clicks
   397→    setShowDeleteDialog(false)
   398→
   399→    try {
   400→      await deleteTask(taskIdToDelete)
   401→      setSelectedTask(null)
   402→      clearTaskFromUrl()
   403→    } catch (error) {
   404→      console.error('Failed to delete task:', error)
   405→      addToast('error', 'Delete Failed', `Failed to delete task "${taskName}". Please try again.`)
   406→      // Re-open dialog if delete failed so user can retry
   407→      setShowDeleteDialog(true)
   408→    } finally {
   409→      setIsDeleting(false)
   410→    }
   411→  }
   412→
   413→  if (!currentProject) {
   414→    return (
   415→

   416→
Select a project to view its Kanban board

   417→

   418→    )
   419→  }
   420→
   421→  return (
   422→

   423→      {/* Header */}
   424→

   425→
Kanban Board

   426→

   427→          {/* Priority Filter */}
   428→

   429→            <Filter size={16} className="text-muted-foreground" />
   430→            <select
   431→              value={priorityFilter === 'all' ? 'all' : priorityFilter.toString()}
   432→              onChange={(e) => handlePriorityFilterChange(e.target.value === 'all' ? 'all' : Number(e.target.value))}
   433→              className="px-3 py-1.5 bg-card border border-border rounded-lg text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent"
   434→            >
   435→              <option value="all">All Priorities</option>
   436→              <option value="3">Critical</option>
   437→              <option value="2">High</option>
   438→              <option value="1">Medium</option>
   439→              <option value="0">Low</option>
   440→            </select>
   441→

   442→
   443→          {/* Status Filter */}
   444→

   445→            <select
   446→              value={statusFilter}
   447→              onChange={(e) => handleStatusFilterChange(e.target.value)}
   448→              className="px-3 py-1.5 bg-card border border-border rounded-lg text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent"
   449→            >
   450→              <option value="all">All Statuses</option>
   451→              <option value="backlog">Backlog</option>
   452→              <option value="in_progress">In Progress</option>
   453→              <option value="qa">QA</option>
   454→              <option value="done">Done</option>
   455→              <option value="blocked">Blocked</option>
   456→            </select>
   457→

   458→
   459→          {/* Agent Type Filter */}
   460→

   461→            <User size={16} className="text-muted-foreground" />
   462→            <select
   463→              value={agentTypeFilter}
   464→              onChange={(e) => handleAgentTypeFilterChange(e.target.value)}
   465→              className="px-3 py-1.5 bg-card border border-border rounded-lg text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent"
   466→            >
   467→              <option value="all">All Agents</option>
   468→              <option value="coder">Coder Agent</option>
   469→              <option value="qa_reviewer">QA Reviewer Agent</option>
   470→              <option value="architect">Architect Agent</option>
   471→              <option value="debugger">Debugger Agent</option>
   472→            </select>
   473→

   474→
   475→          {/* Sort Order */}
   476→

   477→            <ArrowUpDown size={16} className="text-muted-foreground" />
   478→            <select
   479→              value={sortOrder}
   480→              onChange={(e) => handleSortOrderChange(e.target.value as 'newest' | 'oldest' | 'priority')}
   481→              className="px-3 py-1.5 bg-card border border-border rounded-lg text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent"
   482→            >
   483→              <option value="newest">Newest First</option>
   484→              <option value="oldest">Oldest First</option>
   485→              <option value="priority">By Priority</option>
   486→            </select>
   487→

   488→
   489→          {/* Reset Filters Button */}
   490→          {hasActiveFilters && (
   491→            <button
   492→              onClick={handleResetFilters}
   493→              className="flex items-center gap-1.5 px-3 py-1.5 text-sm text-muted-foreground hover:text-foreground hover:bg-secondary rounded-lg transition-colors"
   494→              title="Reset Filters"
   495→            >
   496→              <RefreshCw size={14} />
   497→              Reset
   498→            </button>
   499→          )}
   500→
   501→          {/* View Mode Toggle */}
   502→

   503→            <button
   504→              onClick={() => handleViewModeChange('kanban')}
   505→              className={`p-1.5 rounded ${viewMode === 'kanban' ? 'bg-accent text-white' : 'text-muted-foreground hover:text-foreground'}`}
   506→              title="Kanban View"
   507→            >
   508→              <LayoutGrid size={18} />
   509→            </button>
   510→            <button
   511→              onClick={() => handleViewModeChange('list')}
   512→              className={`p-1.5 rounded ${viewMode === 'list' ? 'bg-accent text-white' : 'text-muted-foreground hover:text-foreground'}`}
   513→              title="List View"
   514→            >
   515→              <List size={18} />
   516→            </button>
   517→

   518→
   519→        <Dialog.Root open={showAddDialog} onOpenChange={setShowAddDialog}>
   520→          <Dialog.Trigger asChild>
   521→            <button className="btn-accent flex items-center gap-2">
   522→              <Plus size={18} />
   523→              Add Task
   524→            </button>
   525→          </Dialog.Trigger>
   526→          <Dialog.Portal>
   527→            <Dialog.Overlay className="fixed inset-0 bg-black/50 z-50" />
   528→            <Dialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg p-4 md:p-6 w-[calc(100%-2rem)] max-w-md max-h-[calc(100vh-2rem)] overflow-y-auto z-50 shadow-xl">
   529→

   530→                <Dialog.Title className="text-xl font-semibold text-foreground">
   531→                  Add New Task
   532→                </Dialog.Title>
   533→                <Dialog.Close asChild>
   534→                  <button className="text-muted-foreground hover:text-foreground" aria-label="Close dialog">
   535→                    <X size={20} />
   536→                  </button>
   537→                </Dialog.Close>
   538→

   539→              <Dialog.Description className="text-muted-foreground mb-4">
   540→                Create a new task for this project.
   541→              </Dialog.Description>
   542→

   543→

   544→
   545→                    Task Name *
   546→
   547→                  <input
   548→                    type="text"
   549→                    value={newTaskName}
   550→                    onChange={(e) => setNewTaskName(e.target.value)}
   551→                    className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
   552→                    placeholder="Enter task name"
   553→                  />
   554→

   555→

   556→
   557→                    Description
   558→
   559→                  <textarea
   560→                    value={newTaskDescription}
   561→                    onChange={(e) => setNewTaskDescription(e.target.value)}
   562→                    rows={3}
   563→                    className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent resize-none"
   564→                    placeholder="Enter task description"
   565→                  />
   566→

   567→

   568→
   569→                    Priority
   570→
   571→                  <select
   572→                    value={newTaskPriority}
   573→                    onChange={(e) => setNewTaskPriority(Number(e.target.value))}
   574→                    className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
   575→                  >
   576→                    <option value={0}>Low</option>
   577→                    <option value={1}>Medium</option>
   578→                    <option value={2}>High</option>
   579→                    <option value={3}>Critical</option>
   580→                  </select>
   581→

   582→

   583→
   584→                    Phase
   585→
   586→                  <select
   587→                    value={newTaskPhaseId ?? ''}
   588→                    onChange={(e) => setNewTaskPhaseId(e.target.value ? Number(e.target.value) : null)}
   589→                    className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
   590→                  >
   591→                    <option value="">No Phase</option>
   592→                    {phases.map((phase) => (
   593→                      <option key={phase.id} value={phase.id}>{phase.name}</option>
   594→                    ))}
   595→                  </select>
   596→

   597→

   598→

   599→                <Dialog.Close asChild>
   600→                  <button className="px-4 py-2 text-sm text-secondary-foreground hover:bg-secondary rounded-lg transition-colors">
   601→                    Cancel
   602→                  </button>
   603→                </Dialog.Close>
   604→                <button
   605→                  onClick={handleCreateTask}
   606→                  disabled={isCreating || !newTaskName.trim()}
   607→                  className="px-4 py-2 text-sm bg-accent hover:bg-accent/80 text-white rounded-lg transition-colors disabled:opacity-50"
   608→                >
   609→                  {isCreating ? 'Creating...' : 'Create Task'}
   610→                </button>
   611→

   612→            </Dialog.Content>
   613→          </Dialog.Portal>
   614→        </Dialog.Root>
   615→

   616→

   617→
   618→      {/* Main Content Area */}
   619→

   620→        {viewMode === 'list' ? (
   621→          /* List View with Pagination */
   622→

   623→            {/* Task List Table */}
   624→

   625→

   626→

   627→

   628→                    Task Name
   629→                    Status
   630→                    Priority
   631→                    Created
   632→

   633→

   634→

   635→                  {isLoading && tasks.length === 0 ? (
   636→

   637→
   638→                        Loading...
   639→
   640→

   641→                  ) : paginatedTasks.length === 0 ? (
   642→

   643→
   644→                        No tasks found
   645→
   646→

   647→                  ) : (
   648→                    paginatedTasks.map((task) => (
   649→
 handleSelectTask(task)}
   652→                        className={`border-t border-border hover:bg-secondary/50 cursor-pointer ${
   653→                          selectedTask?.id === task.id ? 'bg-secondary' : ''
   654→                        }`}
   655→                      >
   656→
   657→

   658→
{task.name}

   659→                            {/* Flagged for Review Indicator in List View */}
   660→                            {task.flagged_for_review && (
   661→                              <Tooltip.Provider>
   662→                                <Tooltip.Root>
   663→                                  <Tooltip.Trigger asChild>
   664→
   665→                                      <Flag size={12} />
   666→
   667→                                  </Tooltip.Trigger>
   668→                                  <Tooltip.Portal>
   669→                                    <Tooltip.Content
   670→                                      className="bg-popover text-popover-foreground px-3 py-2 rounded-lg shadow-lg border border-border text-sm max-w-xs z-50"
   671→                                      sideOffset={5}
   672→                                    >
   673→                                      Flagged for human review
   674→                                      <Tooltip.Arrow className="fill-popover" />
   675→                                    </Tooltip.Content>
   676→                                  </Tooltip.Portal>
   677→                                </Tooltip.Root>
   678→                              </Tooltip.Provider>
   679→                            )}
   680→                            {/* Dependency Indicator in List View */}
   681→                            {task.depends_on && task.depends_on.length > 0 && (
   682→                              <Tooltip.Provider>
   683→                                <Tooltip.Root>
   684→                                  <Tooltip.Trigger asChild>
   685→
   686→                                      <Link2 size={12} />
   687→                                      {task.depends_on.length}
   688→
   689→                                  </Tooltip.Trigger>
   690→                                  <Tooltip.Portal>
   691→                                    <Tooltip.Content
   692→                                      className="bg-popover text-popover-foreground px-3 py-2 rounded-lg shadow-lg border border-border text-sm max-w-xs z-50"
   693→                                      sideOffset={5}
   694→                                    >
   695→
Depends on:

   696→

   697→                                        {getDependencyNames(task.depends_on).map((name, idx) => (
   698→
{name}

   699→                                        ))}
   700→

   701→                                      <Tooltip.Arrow className="fill-popover" />
   702→                                    </Tooltip.Content>
   703→                                  </Tooltip.Portal>
   704→                                </Tooltip.Root>
   705→                              </Tooltip.Provider>
   706→                            )}
   707→

   708→                          {task.description && (
   709→

   710→                              {task.description}
   711→

   712→                          )}
   713→
   714→
   715→
   722→                            {STATUS_LABELS[task.status] || task.status}
   723→
   724→
   725→
   726→
   732→                            {PRIORITY_LABELS[task.priority] || 'Unknown'}
   733→
   734→
   735→
   736→                          {new Date(task.created_at).toLocaleDateString()}
   737→
   738→

   739→                    ))
   740→                  )}
   741→

   742→

   743→

   744→
   745→            {/* Pagination Controls */}
   746→            {totalPages > 1 && (
   747→

   748→

   749→                  Showing {((currentPage - 1) * ITEMS_PER_PAGE) + 1} to {Math.min(currentPage * ITEMS_PER_PAGE, filteredAndSortedTasks.length)} of {filteredAndSortedTasks.length} tasks
   750→

   751→

   752→                  <button
   753→                    onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
   754→                    disabled={currentPage === 1}
   755→                    className="p-2 rounded-lg bg-card border border-border text-muted-foreground hover:text-foreground hover:bg-secondary disabled:opacity-50 disabled:cursor-not-allowed"
   756→                  >
   757→                    <ChevronLeft size={18} />
   758→                  </button>
   759→

   760→                    {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
   761→                      <button
   762→                        key={page}
   763→                        onClick={() => setCurrentPage(page)}
   764→                        className={`w-8 h-8 rounded-lg text-sm ${
   765→                          page === currentPage
   766→                            ? 'bg-accent text-white'
   767→                            : 'bg-card border border-border text-muted-foreground hover:text-foreground hover:bg-secondary'
   768→                        }`}
   769→                      >
   770→                        {page}
   771→                      </button>
   772→                    ))}
   773→

   774→                  <button
   775→                    onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
   776→                    disabled={currentPage === totalPages}
   777→                    className="p-2 rounded-lg bg-card border border-border text-muted-foreground hover:text-foreground hover:bg-secondary disabled:opacity-50 disabled:cursor-not-allowed"
   778→                  >
   779→                    <ChevronRight size={18} />
   780→                  </button>
   781→

   782→

   783→            )}
   784→

   785→        ) : (
   786→        /* Kanban Columns */
   787→        <div className={`flex-1 flex gap-4 overflow-x-auto pb-4 ${selectedTask ? 'mr-0' : ''}`}>
   788→          {COLUMNS.map((column) => (
   789→          <div
   790→            key={column.id}
   791→            className={`flex-shrink-0 w-72 bg-card rounded-lg border-t-2 ${column.color}`}
   792→            onDragOver={handleDragOver}
   793→            onDrop={() => handleDrop(column.id)}
   794→          >
   795→            {/* Column Header */}
   796→

   797→

   798→
{column.title}

   799→
   800→                  {getTasksByStatus(column.id).length}
   801→
   802→

   803→

   804→
   805→            {/* Column Content */}
   806→

   807→              {isLoading && tasks.length === 0 ? (
   808→

   809→                  Loading...
   810→

   811→              ) : getTasksByStatus(column.id).length === 0 ? (
   812→

   813→                  No tasks
   814→

   815→              ) : (
   816→                getTasksByStatus(column.id).map((task) => (
   817→
 handleDragStart(task.id)}
   821→                    onClick={() => handleSelectTask(task)}
   822→                    className={`kanban-card bg-background rounded-lg p-4 border border-border cursor-pointer hover:border-muted-foreground ${
   823→                      draggedTask === task.id ? 'opacity-50' : ''
   824→                    } ${selectedTask?.id === task.id ? 'ring-2 ring-accent' : ''}`}
   825→                  >
   826→

   827→                      <GripVertical size={16} className="text-muted-foreground mt-0.5 flex-shrink-0" />
   828→

   829→
{task.name}

   830→                        {task.description && (
   831→

   832→                            {task.description}
   833→

   834→                        )}
   835→

   836→

   837→                            <span
   838→                              className={`badge ${
   839→                                column.id === 'backlog'
   840→                                  ? 'badge-backlog'
   841→                                  : column.id === 'in_progress'
   842→                                  ? 'badge-in-progress'
   843→                                  : column.id === 'qa'
   844→                                  ? 'badge-qa'
   845→                                  : column.id === 'done'
   846→                                  ? 'badge-done'
   847→                                  : 'badge-blocked'
   848→                              }`}
   849→                            >
   850→                              {column.title}
   851→
   852→                            {/* Flagged for Review Indicator */}
   853→                            {task.flagged_for_review && (
   854→                              <Tooltip.Provider>
   855→                                <Tooltip.Root>
   856→                                  <Tooltip.Trigger asChild>
   857→
   858→                                      <Flag size={12} />
   859→
   860→                                  </Tooltip.Trigger>
   861→                                  <Tooltip.Portal>
   862→                                    <Tooltip.Content
   863→                                      className="bg-popover text-popover-foreground px-3 py-2 rounded-lg shadow-lg border border-border text-sm max-w-xs z-50"
   864→                                      sideOffset={5}
   865→                                    >
   866→                                      Flagged for human review
   867→                                      <Tooltip.Arrow className="fill-popover" />
   868→                                    </Tooltip.Content>
   869→                                  </Tooltip.Portal>
   870→                                </Tooltip.Root>
   871→                              </Tooltip.Provider>
   872→                            )}
   873→                            {/* Dependency Indicator */}
   874→                            {task.depends_on && task.depends_on.length > 0 && (
   875→                              <Tooltip.Provider>
   876→                                <Tooltip.Root>
   877→                                  <Tooltip.Trigger asChild>
   878→
   879→                                      <Link2 size={12} />
   880→                                      {task.depends_on.length}
   881→
   882→                                  </Tooltip.Trigger>
   883→                                  <Tooltip.Portal>
   884→                                    <Tooltip.Content
   885→                                      className="bg-popover text-popover-foreground px-3 py-2 rounded-lg shadow-lg border border-border text-sm max-w-xs z-50"
   886→                                      sideOffset={5}
   887→                                    >
   888→
Depends on:

   889→

   890→                                        {getDependencyNames(task.depends_on).map((name, idx) => (
   891→
{name}

   892→                                        ))}
   893→

   894→                                      <Tooltip.Arrow className="fill-popover" />
   895→                                    </Tooltip.Content>
   896→                                  </Tooltip.Portal>
   897→                                </Tooltip.Root>
   898→                              </Tooltip.Provider>
   899→                            )}
   900→

   901→
   907→                            {task.priority === 3 ? 'Critical' :
   908→                             task.priority === 2 ? 'High' :
   909→                             task.priority === 1 ? 'Medium' : 'Low'}
   910→
   911→

   912→

   913→

   914→

   915→                ))
   916→              )}
   917→

   918→

   919→          ))}
   920→

   921→        )}
   922→
   923→        {/* Task Details Panel */}
   924→        {selectedTask && (
   925→

   926→

   927→
Task Details

   928→

   929→                {!isEditing && (
   930→                  <>
   931→                    <button
   932→                      onClick={handleStartEdit}
   933→                      className="text-muted-foreground hover:text-accent transition-colors"
   934→                      title="Edit Task"
   935→                    >
   936→                      <Pencil size={18} />
   937→                    </button>
   938→                    <AlertDialog.Root open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
   939→                      <AlertDialog.Trigger asChild>
   940→                        <button
   941→                          className="text-muted-foreground hover:text-destructive transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
   942→                          title="Delete Task"
   943→                          disabled={isDeleting || (selectedTask ? isTaskDeleting(selectedTask.id) : false)}
   944→                        >
   945→                          <Trash2 size={18} />
   946→                        </button>
   947→                      </AlertDialog.Trigger>
   948→                      <AlertDialog.Portal>
   949→                        <AlertDialog.Overlay className="fixed inset-0 bg-black/50 z-50" />
   950→                        <AlertDialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg p-4 md:p-6 w-[calc(100%-2rem)] max-w-md max-h-[calc(100vh-2rem)] overflow-y-auto z-50 shadow-xl">
   951→                          <AlertDialog.Title className="text-xl font-semibold text-foreground">
   952→                            Delete Task
   953→                          </AlertDialog.Title>
   954→                          <AlertDialog.Description className="mt-3 text-muted-foreground">
   955→                            Are you sure you want to delete {selectedTask.name}?
   956→                            This action cannot be undone.
   957→                          </AlertDialog.Description>
   958→

   959→                            <AlertDialog.Cancel asChild>
   960→                              <button className="px-4 py-2 text-sm text-secondary-foreground hover:bg-secondary rounded-lg transition-colors">
   961→                                Cancel
   962→                              </button>
   963→                            </AlertDialog.Cancel>
   964→                            <AlertDialog.Action asChild>
   965→                              <button
   966→                                onClick={handleDeleteTask}
   967→                                disabled={isDeleting || (selectedTask ? isTaskDeleting(selectedTask.id) : false)}
   968→                                className="px-4 py-2 text-sm bg-destructive hover:bg-destructive/80 text-white rounded-lg transition-colors disabled:opacity-50"
   969→                              >
   970→                                {isDeleting || (selectedTask && isTaskDeleting(selectedTask.id)) ? 'Deleting...' : 'Delete Task'}
   971→                              </button>
   972→                            </AlertDialog.Action>
   973→

   974→                        </AlertDialog.Content>
   975→                      </AlertDialog.Portal>
   976→                    </AlertDialog.Root>
   977→                  </>
   978→                )}
   979→                <button
   980→                  onClick={handleClosePanel}
   981→                  className="text-muted-foreground hover:text-foreground transition-colors"
   982→                  title="Close"
   983→                >
   984→                  <X size={20} />
   985→                </button>
   986→

   987→

   988→

   989→              {isEditing ? (
   990→                /* Edit Form */
   991→

   992→

   993→                    Name *
   994→                    <input
   995→                      type="text"
   996→                      value={editName}
   997→                      onChange={(e) => setEditName(e.target.value)}
   998→                      className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
   999→                      placeholder="Task name"
  1000→                    />
  1001→

  1002→

  1003→                    Description
  1004→                    <textarea
  1005→                      value={editDescription}
  1006→                      onChange={(e) => setEditDescription(e.target.value)}
  1007→                      rows={3}
  1008→                      className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent resize-none"
  1009→                      placeholder="Task description"
  1010→                    />
  1011→

  1012→

  1013→                    Priority
  1014→                    <select
  1015→                      value={editPriority}
  1016→                      onChange={(e) => setEditPriority(Number(e.target.value))}
  1017→                      className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
  1018→                    >
  1019→                      <option value={0}>Low</option>
  1020→                      <option value={1}>Medium</option>
  1021→                      <option value={2}>High</option>
  1022→                      <option value={3}>Critical</option>
  1023→                    </select>
  1024→

  1025→

  1026→                    Phase
  1027→                    <select
  1028→                      value={editPhaseId ?? ''}
  1029→                      onChange={(e) => setEditPhaseId(e.target.value ? Number(e.target.value) : null)}
  1030→                      className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
  1031→                    >
  1032→                      <option value="">No Phase</option>
  1033→                      {phases.map((phase) => (
  1034→                        <option key={phase.id} value={phase.id}>{phase.name}</option>
  1035→                      ))}
  1036→                    </select>
  1037→

  1038→

  1039→                    <button
  1040→                      onClick={handleCancelEdit}
  1041→                      className="flex-1 px-3 py-2 text-sm text-secondary-foreground hover:bg-secondary rounded-lg transition-colors"
  1042→                    >
  1043→                      Cancel
  1044→                    </button>
  1045→                    <button
  1046→                      onClick={handleSaveEdit}
  1047→                      disabled={isSaving || !editName.trim()}
  1048→                      className="flex-1 flex items-center justify-center gap-2 px-3 py-2 text-sm bg-accent hover:bg-accent/80 text-white rounded-lg transition-colors disabled:opacity-50"
  1049→                    >
  1050→                      {isSaving ? 'Saving...' : <><Check size={16} /> Save</>}
  1051→                    </button>
  1052→

  1053→

  1054→              ) : (
  1055→                /* View Mode */
  1056→                <>
  1057→                  {/* Task Name */}
  1058→

  1059→                    Name
  1060→
{selectedTask.name}

  1061→

  1062→
  1063→                  {/* Description */}
  1064→

  1065→                    Description
  1066→

  1067→                      {selectedTask.description || No description}
  1068→

  1069→

  1070→
  1071→                  {/* Status */}
  1072→

  1073→                    Status
  1074→
  1081→                      {STATUS_LABELS[selectedTask.status] || selectedTask.status}
  1082→
  1083→

  1084→
  1085→                  {/* Priority */}
  1086→

  1087→                    Priority
  1088→
  1094→                      {PRIORITY_LABELS[selectedTask.priority] || 'Unknown'}
  1095→
  1096→

  1097→
  1098→                  {/* Phase */}
  1099→

  1100→                    Phase
  1101→

  1102→                      {selectedTask.phase_id ? (
  1103→                        phases.find(p => p.id === selectedTask.phase_id)?.name || 'Unknown Phase'
  1104→                      ) : (
  1105→                        No phase
  1106→                      )}
  1107→

  1108→

  1109→
  1110→                  {/* Dependencies */}
  1111→

  1112→
  1113→                      <Link2 size={14} />
  1114→                      Dependencies
  1115→
  1116→                    {selectedTask.depends_on && selectedTask.depends_on.length > 0 ? (
  1117→

  1118→                        {getDependencyNames(selectedTask.depends_on).map((name, idx) => (
  1119→

  1120→
  1121→                            {name}
  1122→

  1123→                        ))}
  1124→

  1125→                    ) : (
  1126→
No dependencies

  1127→                    )}
  1128→

  1129→
  1130→                  {/* Agent Assignment */}
  1131→

  1132→
  1133→                      <User size={14} />
  1134→                      Agent Assignment
  1135→
  1136→

  1137→                      {selectedTask.agent_type ? (
  1138→                        {selectedTask.agent_type.replace('_', ' ')}
  1139→                      ) : (
  1140→                        Not assigned
  1141→                      )}
  1142→

  1143→

  1144→
  1145→                  {/* Iteration Count */}
  1146→

  1147→
  1148→                      <RefreshCw size={14} />
  1149→                      Iteration Count
  1150→
  1151→
{selectedTask.iteration_count}

  1152→

  1153→
  1154→                  {/* Flagged for Review */}
  1155→

  1156→
  1157→                      <Flag size={14} />
  1158→                      Flagged for Review
  1159→
  1160→

  1161→                      {selectedTask.flagged_for_review ? 'Yes' : 'No'}
  1162→

  1163→

  1164→
  1165→                  {/* Time Estimates */}
  1166→                  {(selectedTask.estimated_time || selectedTask.actual_time) && (
  1167→

  1168→
  1169→                        <Clock size={14} />
  1170→                        Time
  1171→
  1172→

  1173→                        {selectedTask.estimated_time &&
Estimated: {selectedTask.estimated_time} min
}
  1174→                        {selectedTask.actual_time &&
Actual: {selectedTask.actual_time} min
}
  1175→

  1176→

  1177→                  )}
  1178→
  1179→                  {/* Created/Updated timestamps */}
  1180→

  1181→
Created: {new Date(selectedTask.created_at).toLocaleString()}

  1182→
Updated: {new Date(selectedTask.updated_at).toLocaleString()}

  1183→

  1184→                </>
  1185→              )}
  1186→

  1187→

  1188→        )}
  1189→
  1190→        {/* Unsaved Changes Warning Dialog */}
  1191→        <AlertDialog.Root open={showUnsavedDialog} onOpenChange={setShowUnsavedDialog}>
  1192→          <AlertDialog.Portal>
  1193→            <AlertDialog.Overlay className="fixed inset-0 bg-black/50 z-50" />
  1194→            <AlertDialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg p-4 md:p-6 w-[calc(100%-2rem)] max-w-md max-h-[calc(100vh-2rem)] overflow-y-auto z-50 shadow-xl">
  1195→              <AlertDialog.Title className="text-xl font-semibold text-foreground">
  1196→                Unsaved Changes
  1197→              </AlertDialog.Title>
  1198→              <AlertDialog.Description className="mt-3 text-muted-foreground">
  1199→                You have unsaved changes. Are you sure you want to discard them?
  1200→              </AlertDialog.Description>
  1201→

  1202→                <AlertDialog.Cancel asChild>
  1203→                  <button
  1204→                    onClick={handleKeepEditing}
  1205→                    className="px-4 py-2 text-sm text-secondary-foreground hover:bg-secondary rounded-lg transition-colors"
  1206→                  >
  1207→                    Keep Editing
  1208→                  </button>
  1209→                </AlertDialog.Cancel>
  1210→                <AlertDialog.Action asChild>
  1211→                  <button
  1212→                    onClick={handleDiscardChanges}
  1213→                    className="px-4 py-2 text-sm bg-destructive hover:bg-destructive/80 text-white rounded-lg transition-colors"
  1214→                  >
  1215→                    Discard Changes
  1216→                  </button>
  1217→                </AlertDialog.Action>
  1218→

  1219→            </AlertDialog.Content>
  1220→          </AlertDialog.Portal>
  1221→        </AlertDialog.Root>
  1222→

  1223→

  1224→  )
  1225→}
  1226→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

import { useEffect, useState, useMemo } from 'react'
import { useSearchParams } from 'react-router-dom'
import { Plus, X, GripVertical, Clock, User, RefreshCw, Flag, Pencil, Check, Trash2, Filter, ArrowUpDown, LayoutGrid, List, ChevronLeft, ChevronRight, Link2, Layers } from 'lucide-react'
import * as Dialog from '@radix-ui/react-dialog'
import * as AlertDialog from '@radix-ui/react-alert-dialog'
import * as Tooltip from '@radix-ui/react-tooltip'
import { useProjectStore } from '../stores/projectStore'
import { useTaskStore, Task, TaskCreate, TaskUpdate } from '../stores/taskStore'
import { usePhaseStore } from '../stores/phaseStore'
import { useToastStore } from '../components/Toast'

const ITEMS_PER_PAGE = 10

const COLUMNS = [
  { id: 'backlog', title: 'Backlog', color: 'border-slate-500' },
  { id: 'in_progress', title: 'In Progress', color: 'border-accent' },
  { id: 'qa', title: 'QA', color: 'border-primary-400' },
  { id: 'done', title: 'Done', color: 'border-green-400' },
  { id: 'blocked', title: 'Blocked', color: 'border-destructive' },
]

const PRIORITY_LABELS: Record<number, string> = {
  0: 'Low',
  1: 'Medium',
  2: 'High',
  3: 'Critical',
}

const STATUS_LABELS: Record<string, string> = {
  backlog: 'Backlog',
  in_progress: 'In Progress',
  qa: 'QA',
  done: 'Done',
  blocked: 'Blocked',
}

export default function KanbanBoard() {
  const [searchParams, setSearchParams] = useSearchParams()
  const { currentProject } = useProjectStore()
  const { tasks, fetchTasks, createTask, updateTask, updateTaskStatus, deleteTask, isTaskDeleting, isLoading } = useTaskStore()
  const { phases, fetchPhases } = usePhaseStore()
  const { addToast } = useToastStore()
  const [showAddDialog, setShowAddDialog] = useState(false)
  const [newTaskName, setNewTaskName] = useState('')
  const [newTaskDescription, setNewTaskDescription] = useState('')
  const [newTaskPriority, setNewTaskPriority] = useState(0)
  const [newTaskPhaseId, setNewTaskPhaseId] = useState<number | null>(null)
  const [isCreating, setIsCreating] = useState(false)
  const [draggedTask, setDraggedTask] = useState<number | null>(null)
  const [selectedTask, setSelectedTask] = useState<Task | null>(null)
  const [isEditing, setIsEditing] = useState(false)
  const [editName, setEditName] = useState('')
  const [editDescription, setEditDescription] = useState('')
  const [editPriority, setEditPriority] = useState(0)
  const [editPhaseId, setEditPhaseId] = useState<number | null>(null)
  const [isSaving, setIsSaving] = useState(false)
  const [showDeleteDialog, setShowDeleteDialog] = useState(false)
  const [isDeleting, setIsDeleting] = useState(false)
  const [showUnsavedDialog, setShowUnsavedDialog] = useState(false)
  const [pendingCloseAction, setPendingCloseAction] = useState<'close' | 'cancel' | null>(null)
  // Filter and Sort state - initialize from URL params
  const getInitialPriorityFilter = (): number | 'all' => {
    const param = searchParams.get('priority')
    if (param === 'all' || param === null) return 'all'
    const num = parseInt(param, 10)
    return [0, 1, 2, 3].includes(num) ? num : 'all'
  }
  const getInitialStatusFilter = (): string | 'all' => {
    const param = searchParams.get('status')
    if (param === 'all' || param === null) return 'all'
    const validStatuses = ['backlog', 'in_progress', 'qa', 'done', 'blocked']
    return validStatuses.includes(param) ? param : 'all'
  }
  const getInitialSortOrder = (): 'newest' | 'oldest' | 'priority' => {
    const param = searchParams.get('sort')
    if (param === 'newest' || param === 'oldest' || param === 'priority') return param
    return 'newest'
  }
  const getInitialAgentTypeFilter = (): string | 'all' => {
    const param = searchParams.get('agent')
    if (param === 'all' || param === null) return 'all'
    return param
  }
  const getInitialViewMode = (): 'kanban' | 'list' => {
    const param = searchParams.get('view')
    if (param === 'list') return 'list'
    return 'kanban'
  }

  const [priorityFilter, setPriorityFilter] = useState<number | 'all'>(getInitialPriorityFilter)
  const [statusFilter, setStatusFilter] = useState<string | 'all'>(getInitialStatusFilter)
  const [sortOrder, setSortOrder] = useState<'newest' | 'oldest' | 'priority'>(getInitialSortOrder)
  const [agentTypeFilter, setAgentTypeFilter] = useState<string | 'all'>(getInitialAgentTypeFilter)
  // View mode and pagination state
  const [viewMode, setViewMode] = useState<'kanban' | 'list'>(getInitialViewMode)
  // Phase grouping state
  const [phaseGrouping, setPhaseGrouping] = useState<boolean>(() => searchParams.get('groupByPhase') === 'true')
  const [currentPage, setCurrentPage] = useState(1)

  useEffect(() => {
    if (currentProject?.id) {
      fetchTasks(currentProject.id)
      fetchPhases(currentProject.id)
    }
  }, [currentProject?.id, fetchTasks, fetchPhases])

  // Handle deep linking - select task from URL param after tasks are loaded
  useEffect(() => {
    const taskIdParam = searchParams.get('task')
    if (taskIdParam && tasks.length > 0) {
      const taskId = parseInt(taskIdParam, 10)
      const task = tasks.find(t => t.id === taskId)
      if (task) {
        setSelectedTask(task)
      }
    }
  }, [searchParams, tasks])

  // Update URL when task is selected
  const handleSelectTask = (task: Task) => {
    setSelectedTask(task)
    const newParams = new URLSearchParams(searchParams)
    newParams.set('task', task.id.toString())
    setSearchParams(newParams)
  }

  // Clear task from URL when panel is closed
  const clearTaskFromUrl = () => {
    const newParams = new URLSearchParams(searchParams)
    newParams.delete('task')
    setSearchParams(newParams)
  }

  // Update URL when filter changes
  const handlePriorityFilterChange = (value: number | 'all') => {
    setPriorityFilter(value)
    const newParams = new URLSearchParams(searchParams)
    if (value === 'all') {
      newParams.delete('priority')
    } else {
      newParams.set('priority', value.toString())
    }
    setSearchParams(newParams)
  }

  // Update URL when status filter changes
  const handleStatusFilterChange = (value: string | 'all') => {
    setStatusFilter(value)
    const newParams = new URLSearchParams(searchParams)
    if (value === 'all') {
      newParams.delete('status')
    } else {
      newParams.set('status', value)
    }
    setSearchParams(newParams)
  }

  // Update URL when sort changes
  const handleSortOrderChange = (value: 'newest' | 'oldest' | 'priority') => {
    setSortOrder(value)
    const newParams = new URLSearchParams(searchParams)
    if (value === 'newest') {
      newParams.delete('sort') // default value, no need to store
    } else {
      newParams.set('sort', value)
    }
    setSearchParams(newParams)
  }

  // Update URL when agent type filter changes
  const handleAgentTypeFilterChange = (value: string | 'all') => {
    setAgentTypeFilter(value)
    const newParams = new URLSearchParams(searchParams)
    if (value === 'all') {
      newParams.delete('agent')
    } else {
      newParams.set('agent', value)
    }
    setSearchParams(newParams)
  }

  // Update URL when view mode changes
  const handleViewModeChange = (value: 'kanban' | 'list') => {
    setViewMode(value)
    const newParams = new URLSearchParams(searchParams)
    if (value === 'kanban') {
      newParams.delete('view') // default value, no need to store
    } else {
      newParams.set('view', value)
    }
    setSearchParams(newParams)
  }

  // Reset all filters to defaults
  const handleResetFilters = () => {
    setPriorityFilter('all')
    setStatusFilter('all')
    setAgentTypeFilter('all')
    setSortOrder('newest')
    setCurrentPage(1)
    const newParams = new URLSearchParams(searchParams)
    newParams.delete('priority')
    newParams.delete('status')
    newParams.delete('agent')
    newParams.delete('sort')
    setSearchParams(newParams)
  }

  // Check if filters are active (non-default)
  const hasActiveFilters = priorityFilter !== 'all' || statusFilter !== 'all' || agentTypeFilter !== 'all' || sortOrder !== 'newest'

  // Filtered and sorted tasks
  const filteredAndSortedTasks = useMemo(() => {
    let result = [...tasks]

    // Apply priority filter
    if (priorityFilter !== 'all') {
      result = result.filter(task => task.priority === priorityFilter)
    }

    // Apply status filter
    if (statusFilter !== 'all') {
      result = result.filter(task => task.status === statusFilter)
    }

    // Apply agent type filter
    if (agentTypeFilter !== 'all') {
      result = result.filter(task => task.agent_type === agentTypeFilter)
    }

    // Apply sort
    result.sort((a, b) => {
      if (sortOrder === 'newest') {
        return new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
      } else if (sortOrder === 'oldest') {
        return new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
      } else {
        // priority - highest first
        return b.priority - a.priority
      }
    })

    return result
  }, [tasks, priorityFilter, statusFilter, agentTypeFilter, sortOrder])

  // Pagination calculations
  const totalPages = Math.ceil(filteredAndSortedTasks.length / ITEMS_PER_PAGE)
  const paginatedTasks = useMemo(() => {
    const startIndex = (currentPage - 1) * ITEMS_PER_PAGE
    return filteredAndSortedTasks.slice(startIndex, startIndex + ITEMS_PER_PAGE)
  }, [filteredAndSortedTasks, currentPage])

  // Reset page when filter changes
  useEffect(() => {
    setCurrentPage(1)
  }, [priorityFilter, statusFilter, agentTypeFilter, sortOrder])

  const getTasksByStatus = (status: string) => {
    return filteredAndSortedTasks.filter((task) => task.status === status)
  }

  // Helper to get dependency task names
  const getDependencyNames = (dependsOn: number[] | null): string[] => {
    if (!dependsOn || dependsOn.length === 0) return []
    return dependsOn
      .map(taskId => tasks.find(t => t.id === taskId)?.name || `Task #${taskId}`)
  }

  const handleCreateTask = async () => {
    if (!currentProject || !newTaskName.trim()) return
    setIsCreating(true)
    try {
      const taskData: TaskCreate = {
        name: newTaskName.trim(),
        description: newTaskDescription.trim() || undefined,
        priority: newTaskPriority,
        phase_id: newTaskPhaseId,
      }
      await createTask(currentProject.id, taskData)
      setNewTaskName('')
      setNewTaskDescription('')
      setNewTaskPriority(0)
      setNewTaskPhaseId(null)
      setShowAddDialog(false)
    } catch (error) {
      console.error('Failed to create task:', error)
      addToast('error', 'Create Failed', 'Failed to create task. Please try again.')
    } finally {
      setIsCreating(false)
    }
  }

  const handleDragStart = (taskId: number) => {
    setDraggedTask(taskId)
  }

  const handleDragOver = (e: React.DragEvent) => {
    e.preventDefault()
  }

  const handleDrop = async (targetStatus: string) => {
    if (draggedTask === null) return
    try {
      await updateTaskStatus(draggedTask, targetStatus)
    } catch (error) {
      console.error('Failed to update task status:', error)
      addToast('error', 'Update Failed', 'Failed to update task status. Please try again.')
    }
    setDraggedTask(null)
  }

  const handleStartEdit = () => {
    if (!selectedTask) return
    setEditName(selectedTask.name)
    setEditDescription(selectedTask.description || '')
    setEditPriority(selectedTask.priority)
    setEditPhaseId(selectedTask.phase_id)
    setIsEditing(true)
  }

  // Check if edit form has unsaved changes
  const hasUnsavedChanges = () => {
    if (!selectedTask || !isEditing) return false
    return (
      editName.trim() !== selectedTask.name ||
      (editDescription.trim() || '') !== (selectedTask.description || '') ||
      editPriority !== selectedTask.priority ||
      editPhaseId !== selectedTask.phase_id
    )
  }

  const handleCancelEdit = () => {
    if (hasUnsavedChanges()) {
      setPendingCloseAction('cancel')
      setShowUnsavedDialog(true)
    } else {
      setIsEditing(false)
    }
  }

  const handleClosePanel = () => {
    if (hasUnsavedChanges()) {
      setPendingCloseAction('close')
      setShowUnsavedDialog(true)
    } else {
      setSelectedTask(null)
      setIsEditing(false)
      clearTaskFromUrl()
    }
  }

  const handleDiscardChanges = () => {
    setShowUnsavedDialog(false)
    if (pendingCloseAction === 'close') {
      setSelectedTask(null)
      clearTaskFromUrl()
    }
    setIsEditing(false)
    setPendingCloseAction(null)
  }

  const handleKeepEditing = () => {
    setShowUnsavedDialog(false)
    setPendingCloseAction(null)
  }

  const handleSaveEdit = async () => {
    if (!selectedTask) return
    setIsSaving(true)
    try {
      const updates: TaskUpdate = {
        name: editName.trim(),
        description: editDescription.trim() || undefined,
        priority: editPriority,
        phase_id: editPhaseId,
      }
      const updatedTask = await updateTask(selectedTask.id, updates)
      setSelectedTask(updatedTask)
      setIsEditing(false)
    } catch (error) {
      console.error('Failed to update task:', error)
      addToast('error', 'Save Failed', 'Failed to save task changes. Please try again.')
    } finally {
      setIsSaving(false)
    }
  }

  const handleDeleteTask = async () => {
    if (!selectedTask) return
    // Check if already being deleted (prevents rapid clicks)
    if (isTaskDeleting(selectedTask.id)) return

    const taskIdToDelete = selectedTask.id
    const taskName = selectedTask.name
    setIsDeleting(true)
    // Close dialog immediately to prevent additional clicks
    setShowDeleteDialog(false)

    try {
      await deleteTask(taskIdToDelete)
      setSelectedTask(null)
      clearTaskFromUrl()
    } catch (error) {
      console.error('Failed to delete task:', error)
      addToast('error', 'Delete Failed', `Failed to delete task "${taskName}". Please try again.`)
      // Re-open dialog if delete failed so user can retry
      setShowDeleteDialog(true)
    } finally {
      setIsDeleting(false)
    }
  }

  if (!currentProject) {
    return (

Select a project to view its Kanban board

    )
  }

  return (

      {/* Header */}

Kanban Board

          {/* Priority Filter */}

            <Filter size={16} className="text-muted-foreground" />
            <select
              value={priorityFilter === 'all' ? 'all' : priorityFilter.toString()}
              onChange={(e) => handlePriorityFilterChange(e.target.value === 'all' ? 'all' : Number(e.target.value))}
              className="px-3 py-1.5 bg-card border border-border rounded-lg text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent"
            >
              <option value="all">All Priorities</option>
              <option value="3">Critical</option>
              <option value="2">High</option>
              <option value="1">Medium</option>
              <option value="0">Low</option>
            </select>

          {/* Status Filter */}

            <select
              value={statusFilter}
              onChange={(e) => handleStatusFilterChange(e.target.value)}
              className="px-3 py-1.5 bg-card border border-border rounded-lg text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent"
            >
              <option value="all">All Statuses</option>
              <option value="backlog">Backlog</option>
              <option value="in_progress">In Progress</option>
              <option value="qa">QA</option>
              <option value="done">Done</option>
              <option value="blocked">Blocked</option>
            </select>

          {/* Agent Type Filter */}

            <User size={16} className="text-muted-foreground" />
            <select
              value={agentTypeFilter}
              onChange={(e) => handleAgentTypeFilterChange(e.target.value)}
              className="px-3 py-1.5 bg-card border border-border rounded-lg text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent"
            >
              <option value="all">All Agents</option>
              <option value="coder">Coder Agent</option>
              <option value="qa_reviewer">QA Reviewer Agent</option>
              <option value="architect">Architect Agent</option>
              <option value="debugger">Debugger Agent</option>
            </select>

          {/* Sort Order */}

            <ArrowUpDown size={16} className="text-muted-foreground" />
            <select
              value={sortOrder}
              onChange={(e) => handleSortOrderChange(e.target.value as 'newest' | 'oldest' | 'priority')}
              className="px-3 py-1.5 bg-card border border-border rounded-lg text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent"
            >
              <option value="newest">Newest First</option>
              <option value="oldest">Oldest First</option>
              <option value="priority">By Priority</option>
            </select>

          {/* Reset Filters Button */}
          {hasActiveFilters && (
            <button
              onClick={handleResetFilters}
              className="flex items-center gap-1.5 px-3 py-1.5 text-sm text-muted-foreground hover:text-foreground hover:bg-secondary rounded-lg transition-colors"
              title="Reset Filters"
            >
              <RefreshCw size={14} />
              Reset
            </button>
          )}

          {/* View Mode Toggle */}

            <button
              onClick={() => handleViewModeChange('kanban')}
              className={`p-1.5 rounded ${viewMode === 'kanban' ? 'bg-accent text-white' : 'text-muted-foreground hover:text-foreground'}`}
              title="Kanban View"
            >
              <LayoutGrid size={18} />
            </button>
            <button
              onClick={() => handleViewModeChange('list')}
              className={`p-1.5 rounded ${viewMode === 'list' ? 'bg-accent text-white' : 'text-muted-foreground hover:text-foreground'}`}
              title="List View"
            >
              <List size={18} />
            </button>

        <Dialog.Root open={showAddDialog} onOpenChange={setShowAddDialog}>
          <Dialog.Trigger asChild>
            <button className="btn-accent flex items-center gap-2">
              <Plus size={18} />
              Add Task
            </button>
          </Dialog.Trigger>
          <Dialog.Portal>
            <Dialog.Overlay className="fixed inset-0 bg-black/50 z-50" />
            <Dialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg p-4 md:p-6 w-[calc(100%-2rem)] max-w-md max-h-[calc(100vh-2rem)] overflow-y-auto z-50 shadow-xl">

                <Dialog.Title className="text-xl font-semibold text-foreground">
                  Add New Task
                </Dialog.Title>
                <Dialog.Close asChild>
                  <button className="text-muted-foreground hover:text-foreground" aria-label="Close dialog">
                    <X size={20} />
                  </button>
                </Dialog.Close>

              <Dialog.Description className="text-muted-foreground mb-4">
                Create a new task for this project.
              </Dialog.Description>

                    Task Name *

                  <input
                    type="text"
                    value={newTaskName}
                    onChange={(e) => setNewTaskName(e.target.value)}
                    className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
                    placeholder="Enter task name"
                  />

                    Description

                  <textarea
                    value={newTaskDescription}
                    onChange={(e) => setNewTaskDescription(e.target.value)}
                    rows={3}
                    className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent resize-none"
                    placeholder="Enter task description"
                  />

                    Priority

                  <select
                    value={newTaskPriority}
                    onChange={(e) => setNewTaskPriority(Number(e.target.value))}
                    className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
                  >
                    <option value={0}>Low</option>
                    <option value={1}>Medium</option>
                    <option value={2}>High</option>
                    <option value={3}>Critical</option>
                  </select>

                    Phase

                  <select
                    value={newTaskPhaseId ?? ''}
                    onChange={(e) => setNewTaskPhaseId(e.target.value ? Number(e.target.value) : null)}
                    className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
                  >
                    <option value="">No Phase</option>
                    {phases.map((phase) => (
                      <option key={phase.id} value={phase.id}>{phase.name}</option>
                    ))}
                  </select>

                <Dialog.Close asChild>
                  <button className="px-4 py-2 text-sm text-secondary-foreground hover:bg-secondary rounded-lg transition-colors">
                    Cancel
                  </button>
                </Dialog.Close>
                <button
                  onClick={handleCreateTask}
                  disabled={isCreating || !newTaskName.trim()}
                  className="px-4 py-2 text-sm bg-accent hover:bg-accent/80 text-white rounded-lg transition-colors disabled:opacity-50"
                >
                  {isCreating ? 'Creating...' : 'Create Task'}
                </button>

            </Dialog.Content>
          </Dialog.Portal>
        </Dialog.Root>

      {/* Main Content Area */}

        {viewMode === 'list' ? (
          /* List View with Pagination */

            {/* Task List Table */}

                    Task Name
                    Status
                    Priority
                    Created

                  {isLoading && tasks.length === 0 ? (

                        Loading...

                  ) : paginatedTasks.length === 0 ? (

                        No tasks found

                  ) : (
                    paginatedTasks.map((task) => (

 handleSelectTask(task)}
                        className={`border-t border-border hover:bg-secondary/50 cursor-pointer ${
                          selectedTask?.id === task.id ? 'bg-secondary' : ''
                        }`}
                      >

{task.name}

                            {/* Flagged for Review Indicator in List View */}
                            {task.flagged_for_review && (
                              <Tooltip.Provider>
                                <Tooltip.Root>
                                  <Tooltip.Trigger asChild>

                                      <Flag size={12} />

                                  </Tooltip.Trigger>
                                  <Tooltip.Portal>
                                    <Tooltip.Content
                                      className="bg-popover text-popover-foreground px-3 py-2 rounded-lg shadow-lg border border-border text-sm max-w-xs z-50"
                                      sideOffset={5}
                                    >
                                      Flagged for human review
                                      <Tooltip.Arrow className="fill-popover" />
                                    </Tooltip.Content>
                                  </Tooltip.Portal>
                                </Tooltip.Root>
                              </Tooltip.Provider>
                            )}
                            {/* Dependency Indicator in List View */}
                            {task.depends_on && task.depends_on.length > 0 && (
                              <Tooltip.Provider>
                                <Tooltip.Root>
                                  <Tooltip.Trigger asChild>

                                      <Link2 size={12} />
                                      {task.depends_on.length}

                                  </Tooltip.Trigger>
                                  <Tooltip.Portal>
                                    <Tooltip.Content
                                      className="bg-popover text-popover-foreground px-3 py-2 rounded-lg shadow-lg border border-border text-sm max-w-xs z-50"
                                      sideOffset={5}
                                    >

Depends on:

                                        {getDependencyNames(task.depends_on).map((name, idx) => (

{name}

                                        ))}

                                      <Tooltip.Arrow className="fill-popover" />
                                    </Tooltip.Content>
                                  </Tooltip.Portal>
                                </Tooltip.Root>
                              </Tooltip.Provider>
                            )}

                          {task.description && (

                              {task.description}

                          )}

                            {STATUS_LABELS[task.status] || task.status}

                            {PRIORITY_LABELS[task.priority] || 'Unknown'}

                          {new Date(task.created_at).toLocaleDateString()}

                    ))
                  )}

            {/* Pagination Controls */}
            {totalPages > 1 && (

                  Showing {((currentPage - 1) * ITEMS_PER_PAGE) + 1} to {Math.min(currentPage * ITEMS_PER_PAGE, filteredAndSortedTasks.length)} of {filteredAndSortedTasks.length} tasks

                  <button
                    onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
                    disabled={currentPage === 1}
                    className="p-2 rounded-lg bg-card border border-border text-muted-foreground hover:text-foreground hover:bg-secondary disabled:opacity-50 disabled:cursor-not-allowed"
                  >
                    <ChevronLeft size={18} />
                  </button>

                    {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
                      <button
                        key={page}
                        onClick={() => setCurrentPage(page)}
                        className={`w-8 h-8 rounded-lg text-sm ${
                          page === currentPage
                            ? 'bg-accent text-white'
                            : 'bg-card border border-border text-muted-foreground hover:text-foreground hover:bg-secondary'
                        }`}
                      >
                        {page}
                      </button>
                    ))}

                  <button
                    onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
                    disabled={currentPage === totalPages}
                    className="p-2 rounded-lg bg-card border border-border text-muted-foreground hover:text-foreground hover:bg-secondary disabled:opacity-50 disabled:cursor-not-allowed"
                  >
                    <ChevronRight size={18} />
                  </button>

            )}

        ) : (
        /* Kanban Columns */
        <div className={`flex-1 flex gap-4 overflow-x-auto pb-4 ${selectedTask ? 'mr-0' : ''}`}>
          {COLUMNS.map((column) => (
          <div
            key={column.id}
            className={`flex-shrink-0 w-72 bg-card rounded-lg border-t-2 ${column.color}`}
            onDragOver={handleDragOver}
            onDrop={() => handleDrop(column.id)}
          >
            {/* Column Header */}

{column.title}

                  {getTasksByStatus(column.id).length}

            {/* Column Content */}

              {isLoading && tasks.length === 0 ? (

                  Loading...

              ) : getTasksByStatus(column.id).length === 0 ? (

                  No tasks

              ) : (
                getTasksByStatus(column.id).map((task) => (

 handleDragStart(task.id)}
                    onClick={() => handleSelectTask(task)}
                    className={`kanban-card bg-background rounded-lg p-4 border border-border cursor-pointer hover:border-muted-foreground ${
                      draggedTask === task.id ? 'opacity-50' : ''
                    } ${selectedTask?.id === task.id ? 'ring-2 ring-accent' : ''}`}
                  >

                      <GripVertical size={16} className="text-muted-foreground mt-0.5 flex-shrink-0" />

{task.name}

                        {task.description && (

                            {task.description}

                        )}

                            <span
                              className={`badge ${
                                column.id === 'backlog'
                                  ? 'badge-backlog'
                                  : column.id === 'in_progress'
                                  ? 'badge-in-progress'
                                  : column.id === 'qa'
                                  ? 'badge-qa'
                                  : column.id === 'done'
                                  ? 'badge-done'
                                  : 'badge-blocked'
                              }`}
                            >
                              {column.title}

                            {/* Flagged for Review Indicator */}
                            {task.flagged_for_review && (
                              <Tooltip.Provider>
                                <Tooltip.Root>
                                  <Tooltip.Trigger asChild>

                                      <Flag size={12} />

                                  </Tooltip.Trigger>
                                  <Tooltip.Portal>
                                    <Tooltip.Content
                                      className="bg-popover text-popover-foreground px-3 py-2 rounded-lg shadow-lg border border-border text-sm max-w-xs z-50"
                                      sideOffset={5}
                                    >
                                      Flagged for human review
                                      <Tooltip.Arrow className="fill-popover" />
                                    </Tooltip.Content>
                                  </Tooltip.Portal>
                                </Tooltip.Root>
                              </Tooltip.Provider>
                            )}
                            {/* Dependency Indicator */}
                            {task.depends_on && task.depends_on.length > 0 && (
                              <Tooltip.Provider>
                                <Tooltip.Root>
                                  <Tooltip.Trigger asChild>

                                      <Link2 size={12} />
                                      {task.depends_on.length}

                                  </Tooltip.Trigger>
                                  <Tooltip.Portal>
                                    <Tooltip.Content
                                      className="bg-popover text-popover-foreground px-3 py-2 rounded-lg shadow-lg border border-border text-sm max-w-xs z-50"
                                      sideOffset={5}
                                    >

Depends on:

                                        {getDependencyNames(task.depends_on).map((name, idx) => (

{name}

                                        ))}

                                      <Tooltip.Arrow className="fill-popover" />
                                    </Tooltip.Content>
                                  </Tooltip.Portal>
                                </Tooltip.Root>
                              </Tooltip.Provider>
                            )}

                            {task.priority === 3 ? 'Critical' :
                             task.priority === 2 ? 'High' :
                             task.priority === 1 ? 'Medium' : 'Low'}

                ))
              )}

          ))}

        )}

        {/* Task Details Panel */}
        {selectedTask && (

Task Details

                {!isEditing && (
                  <>
                    <button
                      onClick={handleStartEdit}
                      className="text-muted-foreground hover:text-accent transition-colors"
                      title="Edit Task"
                    >
                      <Pencil size={18} />
                    </button>
                    <AlertDialog.Root open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
                      <AlertDialog.Trigger asChild>
                        <button
                          className="text-muted-foreground hover:text-destructive transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
                          title="Delete Task"
                          disabled={isDeleting || (selectedTask ? isTaskDeleting(selectedTask.id) : false)}
                        >
                          <Trash2 size={18} />
                        </button>
                      </AlertDialog.Trigger>
                      <AlertDialog.Portal>
                        <AlertDialog.Overlay className="fixed inset-0 bg-black/50 z-50" />
                        <AlertDialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg p-4 md:p-6 w-[calc(100%-2rem)] max-w-md max-h-[calc(100vh-2rem)] overflow-y-auto z-50 shadow-xl">
                          <AlertDialog.Title className="text-xl font-semibold text-foreground">
                            Delete Task
                          </AlertDialog.Title>
                          <AlertDialog.Description className="mt-3 text-muted-foreground">
                            Are you sure you want to delete {selectedTask.name}?
                            This action cannot be undone.
                          </AlertDialog.Description>

                            <AlertDialog.Cancel asChild>
                              <button className="px-4 py-2 text-sm text-secondary-foreground hover:bg-secondary rounded-lg transition-colors">
                                Cancel
                              </button>
                            </AlertDialog.Cancel>
                            <AlertDialog.Action asChild>
                              <button
                                onClick={handleDeleteTask}
                                disabled={isDeleting || (selectedTask ? isTaskDeleting(selectedTask.id) : false)}
                                className="px-4 py-2 text-sm bg-destructive hover:bg-destructive/80 text-white rounded-lg transition-colors disabled:opacity-50"
                              >
                                {isDeleting || (selectedTask && isTaskDeleting(selectedTask.id)) ? 'Deleting...' : 'Delete Task'}
                              </button>
                            </AlertDialog.Action>

                        </AlertDialog.Content>
                      </AlertDialog.Portal>
                    </AlertDialog.Root>
                  </>
                )}
                <button
                  onClick={handleClosePanel}
                  className="text-muted-foreground hover:text-foreground transition-colors"
                  title="Close"
                >
                  <X size={20} />
                </button>

              {isEditing ? (
                /* Edit Form */

                    Name *
                    <input
                      type="text"
                      value={editName}
                      onChange={(e) => setEditName(e.target.value)}
                      className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
                      placeholder="Task name"
                    />

                    Description
                    <textarea
                      value={editDescription}
                      onChange={(e) => setEditDescription(e.target.value)}
                      rows={3}
                      className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent resize-none"
                      placeholder="Task description"
                    />

                    Priority
                    <select
                      value={editPriority}
                      onChange={(e) => setEditPriority(Number(e.target.value))}
                      className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
                    >
                      <option value={0}>Low</option>
                      <option value={1}>Medium</option>
                      <option value={2}>High</option>
                      <option value={3}>Critical</option>
                    </select>

                    Phase
                    <select
                      value={editPhaseId ?? ''}
                      onChange={(e) => setEditPhaseId(e.target.value ? Number(e.target.value) : null)}
                      className="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
                    >
                      <option value="">No Phase</option>
                      {phases.map((phase) => (
                        <option key={phase.id} value={phase.id}>{phase.name}</option>
                      ))}
                    </select>

                    <button
                      onClick={handleCancelEdit}
                      className="flex-1 px-3 py-2 text-sm text-secondary-foreground hover:bg-secondary rounded-lg transition-colors"
                    >
                      Cancel
                    </button>
                    <button
                      onClick={handleSaveEdit}
                      disabled={isSaving || !editName.trim()}
                      className="flex-1 flex items-center justify-center gap-2 px-3 py-2 text-sm bg-accent hover:bg-accent/80 text-white rounded-lg transition-colors disabled:opacity-50"
                    >
                      {isSaving ? 'Saving...' : <><Check size={16} /> Save</>}
                    </button>

              ) : (
                /* View Mode */
                <>
                  {/* Task Name */}

                    Name

{selectedTask.name}

                  {/* Description */}

                    Description

                      {selectedTask.description || No description}

                  {/* Status */}

                    Status

                      {STATUS_LABELS[selectedTask.status] || selectedTask.status}

                  {/* Priority */}

                    Priority

                      {PRIORITY_LABELS[selectedTask.priority] || 'Unknown'}

                  {/* Phase */}

                    Phase

                      {selectedTask.phase_id ? (
                        phases.find(p => p.id === selectedTask.phase_id)?.name || 'Unknown Phase'
                      ) : (
                        No phase
                      )}

                  {/* Dependencies */}

                      <Link2 size={14} />
                      Dependencies

                    {selectedTask.depends_on && selectedTask.depends_on.length > 0 ? (

                        {getDependencyNames(selectedTask.depends_on).map((name, idx) => (

                            {name}

                        ))}

                    ) : (

No dependencies

                    )}

                  {/* Agent Assignment */}

                      <User size={14} />
                      Agent Assignment

                      {selectedTask.agent_type ? (
                        {selectedTask.agent_type.replace('_', ' ')}
                      ) : (
                        Not assigned
                      )}

                  {/* Iteration Count */}

                      <RefreshCw size={14} />
                      Iteration Count

{selectedTask.iteration_count}

                  {/* Flagged for Review */}

                      <Flag size={14} />
                      Flagged for Review

                      {selectedTask.flagged_for_review ? 'Yes' : 'No'}

                  {/* Time Estimates */}
                  {(selectedTask.estimated_time || selectedTask.actual_time) && (

                        <Clock size={14} />
                        Time

                        {selectedTask.estimated_time &&
Estimated: {selectedTask.estimated_time} min
}
                        {selectedTask.actual_time &&
Actual: {selectedTask.actual_time} min
}

                  )}

                  {/* Created/Updated timestamps */}

Created: {new Date(selectedTask.created_at).toLocaleString()}

Updated: {new Date(selectedTask.updated_at).toLocaleString()}

                </>
              )}

        )}

        {/* Unsaved Changes Warning Dialog */}
        <AlertDialog.Root open={showUnsavedDialog} onOpenChange={setShowUnsavedDialog}>
          <AlertDialog.Portal>
            <AlertDialog.Overlay className="fixed inset-0 bg-black/50 z-50" />
            <AlertDialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg p-4 md:p-6 w-[calc(100%-2rem)] max-w-md max-h-[calc(100vh-2rem)] overflow-y-auto z-50 shadow-xl">
              <AlertDialog.Title className="text-xl font-semibold text-foreground">
                Unsaved Changes
              </AlertDialog.Title>
              <AlertDialog.Description className="mt-3 text-muted-foreground">
                You have unsaved changes. Are you sure you want to discard them?
              </AlertDialog.Description>

                <AlertDialog.Cancel asChild>
                  <button
                    onClick={handleKeepEditing}
                    className="px-4 py-2 text-sm text-secondary-foreground hover:bg-secondary rounded-lg transition-colors"
                  >
                    Keep Editing
                  </button>
                </AlertDialog.Cancel>
                <AlertDialog.Action asChild>
                  <button
                    onClick={handleDiscardChanges}
                    className="px-4 py-2 text-sm bg-destructive hover:bg-destructive/80 text-white rounded-lg transition-colors"
                  >
                    Discard Changes
                  </button>
                </AlertDialog.Action>

            </AlertDialog.Content>
          </AlertDialog.Portal>
        </AlertDialog.Root>

  )
}


Good, I have the file contents. Now let me make the required edits. I'll proceed with the four changes:

1. Add `handlePhaseGroupingChange` function after `handleViewModeChange`
2. Update `handleResetFilters` to include phase grouping reset
3. Update `hasActiveFilters` to include `|| phaseGrouping`
4. Add phase grouping toggle button in JSX before View Mode Toggle


Edit

  // Update URL when view mode changes
  const handleViewModeChange = (value: 'kanban' | 'list') => {
    setViewMode(value)
    const newParams = new URLSearchParams(searchParams)
    if (value === 'kanban') {
      newParams.delete('view') // default value, no need to store
    } else {
      newParams.set('view', value)
    }
    setSearchParams(newParams)
  }

  // Reset all filters to defaults

  // Update URL when view mode changes
  const handleViewModeChange = (value: 'kanban' | 'list') => {
    setViewMode(value)
    const newParams = new URLSearchParams(searchParams)
    if (value === 'kanban') {
      newParams.delete('view') // default value, no need to store
    } else {
      newParams.set('view', value)
    }
    setSearchParams(newParams)
  }

  // Update URL when phase grouping changes
  const handlePhaseGroupingChange = (value: boolean) => {
    setPhaseGrouping(value)
    const newParams = new URLSearchParams(searchParams)
    if (!value) {
      newParams.delete('groupByPhase')
    } else {
      newParams.set('groupByPhase', 'true')
    }
    setSearchParams(newParams)
  }

  // Reset all filters to defaults


Error: File has been unexpectedly modified. Read it again before attempting to write it.


The file was modified again. Let me wait a moment and try again.


Bash

Wait for hot reload to settle


Now let me make the first edit - adding the `handlePhaseGroupingChange` function after `handleViewModeChange`.


Edit

  // Update URL when view mode changes
  const handleViewModeChange = (value: 'kanban' | 'list') => {
    setViewMode(value)
    const newParams = new URLSearchParams(searchParams)
    if (value === 'kanban') {
      newParams.delete('view') // default value, no need to store
    } else {
      newParams.set('view', value)
    }
    setSearchParams(newParams)
  }

  // Reset all filters to defaults

  // Update URL when view mode changes
  const handleViewModeChange = (value: 'kanban' | 'list') => {
    setViewMode(value)
    const newParams = new URLSearchParams(searchParams)
    if (value === 'kanban') {
      newParams.delete('view') // default value, no need to store
    } else {
      newParams.set('view', value)
    }
    setSearchParams(newParams)
  }

  // Update URL when phase grouping changes
  const handlePhaseGroupingChange = (value: boolean) => {
    setPhaseGrouping(value)
    const newParams = new URLSearchParams(searchParams)
    if (!value) {
      newParams.delete('groupByPhase')
    } else {
      newParams.set('groupByPhase', 'true')
    }
    setSearchParams(newParams)
  }

  // Reset all filters to defaults


The file keeps being modified by hot reload. Let me try a different approach - I'll stop waiting and just try multiple immediate read-edit cycles.