Improve backlog drag targets and persistent handle visibility
This commit is contained in:
parent
957907c87a
commit
c5cf7703b8
@ -33,7 +33,7 @@
|
||||
"projectId": "2",
|
||||
"sprintId": "sprint-1",
|
||||
"createdAt": "2026-02-18T17:01:23.109Z",
|
||||
"updatedAt": "2026-02-18T17:01:23.109Z",
|
||||
"updatedAt": "2026-02-20T05:01:04.207Z",
|
||||
"comments": [
|
||||
{
|
||||
"id": "c1",
|
||||
@ -57,7 +57,6 @@
|
||||
{
|
||||
"id": "2",
|
||||
"title": "MoodWeave App Idea - UPDATED",
|
||||
"sprintId": "1771551323429",
|
||||
"projectId": "1",
|
||||
"status": "backlog",
|
||||
"priority": "high",
|
||||
@ -69,7 +68,7 @@
|
||||
"OpenClaw iOS"
|
||||
],
|
||||
"createdAt": "2026-02-18T17:01:23.109Z",
|
||||
"updatedAt": "2026-02-20T02:28:23.700Z"
|
||||
"updatedAt": "2026-02-20T05:02:47.264Z"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
@ -323,7 +322,7 @@
|
||||
"priority": "low",
|
||||
"projectId": "3",
|
||||
"createdAt": "2026-02-18T17:01:23.109Z",
|
||||
"updatedAt": "2026-02-18T17:01:23.109Z",
|
||||
"updatedAt": "2026-02-20T05:02:42.002Z",
|
||||
"comments": [
|
||||
{
|
||||
"id": "c52",
|
||||
@ -472,7 +471,7 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"lastUpdated": 1771556223745,
|
||||
"lastUpdated": 1771563767290,
|
||||
"sprints": [
|
||||
{
|
||||
"name": "Sprint 1",
|
||||
@ -506,4 +505,4 @@
|
||||
"createdAt": "2026-02-20T01:37:45.241Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useState, type ReactNode } from "react"
|
||||
import {
|
||||
DndContext,
|
||||
DragEndEvent,
|
||||
DragOverlay,
|
||||
DragStartEvent,
|
||||
PointerSensor,
|
||||
useDroppable,
|
||||
useSensor,
|
||||
useSensors,
|
||||
closestCorners,
|
||||
@ -17,8 +18,8 @@ import {
|
||||
useSortable,
|
||||
} from "@dnd-kit/sortable"
|
||||
import { CSS } from "@dnd-kit/utilities"
|
||||
import { useTaskStore, Task, Sprint } from "@/stores/useTaskStore"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { useTaskStore, Task } from "@/stores/useTaskStore"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Plus, GripVertical, ChevronDown, ChevronRight, Calendar } from "lucide-react"
|
||||
@ -66,13 +67,15 @@ function SortableTaskRow({
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
className="flex items-center gap-3 p-3 bg-slate-800/50 border border-slate-700/50 rounded-lg hover:border-slate-600 cursor-pointer group"
|
||||
className="flex items-center gap-3 p-3 bg-slate-800/50 border border-slate-700/50 rounded-lg hover:border-slate-600 cursor-pointer"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className="opacity-0 group-hover:opacity-100 transition-opacity cursor-grab active:cursor-grabbing"
|
||||
className="shrink-0 h-8 w-6 rounded border border-slate-700/70 bg-slate-900/70 flex items-center justify-center cursor-grab active:cursor-grabbing text-slate-400 hover:text-slate-200"
|
||||
aria-label="Drag task"
|
||||
title="Drag task"
|
||||
>
|
||||
<GripVertical className="w-4 h-4 text-slate-500" />
|
||||
</div>
|
||||
@ -106,6 +109,19 @@ function DragOverlayItem({ task }: { task: Task }) {
|
||||
)
|
||||
}
|
||||
|
||||
function SectionDropZone({ id, children }: { id: string; children: ReactNode }) {
|
||||
const { isOver, setNodeRef } = useDroppable({ id })
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
className={`rounded-lg transition-colors ${isOver ? "ring-1 ring-blue-500/60 bg-blue-500/5" : ""}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Collapsible Section
|
||||
function TaskSection({
|
||||
title,
|
||||
@ -242,14 +258,23 @@ export function BacklogView() {
|
||||
|
||||
const taskId = active.id as string
|
||||
const overId = over.id as string
|
||||
const overTask = tasks.find((t) => t.id === overId)
|
||||
|
||||
const destinationId = overTask
|
||||
? overTask.sprintId
|
||||
? currentSprint && overTask.sprintId === currentSprint.id
|
||||
? "current"
|
||||
: `sprint-${overTask.sprintId}`
|
||||
: "backlog"
|
||||
: overId
|
||||
|
||||
// If dropped over a section header, move task to that section's sprint
|
||||
if (overId === "backlog") {
|
||||
if (destinationId === "backlog") {
|
||||
updateTask(taskId, { sprintId: undefined })
|
||||
} else if (overId === "current" && currentSprint) {
|
||||
} else if (destinationId === "current" && currentSprint) {
|
||||
updateTask(taskId, { sprintId: currentSprint.id })
|
||||
} else if (overId.startsWith("sprint-")) {
|
||||
const sprintId = overId.replace("sprint-", "")
|
||||
} else if (destinationId.startsWith("sprint-")) {
|
||||
const sprintId = destinationId.replace("sprint-", "")
|
||||
updateTask(taskId, { sprintId })
|
||||
}
|
||||
}
|
||||
@ -283,7 +308,7 @@ export function BacklogView() {
|
||||
>
|
||||
<div className="space-y-4">
|
||||
{/* Current Sprint Section */}
|
||||
<div id="current">
|
||||
<SectionDropZone id="current">
|
||||
<TaskSection
|
||||
title={currentSprint?.name || "Current Sprint"}
|
||||
tasks={currentSprintTasks}
|
||||
@ -305,7 +330,7 @@ export function BacklogView() {
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</SectionDropZone>
|
||||
|
||||
{/* Other Sprints Sections - ordered by start date */}
|
||||
{otherSprints
|
||||
@ -314,7 +339,7 @@ export function BacklogView() {
|
||||
const sprintTasks = tasks.filter((t) => t.sprintId === sprint.id)
|
||||
console.log(`Sprint ${sprint.name}: ${sprintTasks.length} tasks`, sprintTasks.map(t => t.title))
|
||||
return (
|
||||
<div key={sprint.id} id={`sprint-${sprint.id}`}>
|
||||
<SectionDropZone key={sprint.id} id={`sprint-${sprint.id}`}>
|
||||
<TaskSection
|
||||
title={sprint.name}
|
||||
tasks={sprintTasks}
|
||||
@ -332,7 +357,7 @@ export function BacklogView() {
|
||||
status: sprint.status,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SectionDropZone>
|
||||
)
|
||||
})}
|
||||
|
||||
@ -391,7 +416,7 @@ export function BacklogView() {
|
||||
)}
|
||||
|
||||
{/* Backlog Section */}
|
||||
<div id="backlog">
|
||||
<SectionDropZone id="backlog">
|
||||
<TaskSection
|
||||
title="Backlog"
|
||||
tasks={backlogTasks}
|
||||
@ -399,7 +424,7 @@ export function BacklogView() {
|
||||
onToggle={() => toggleSection("backlog")}
|
||||
onTaskClick={(task) => selectTask(task.id)}
|
||||
/>
|
||||
</div>
|
||||
</SectionDropZone>
|
||||
</div>
|
||||
|
||||
<DragOverlay>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user