Add ability to delete tasks and improve task card interactions

Refactors the KanbanBoard and TaskCard components to include a task deletion functionality and enhance user interaction with task details through a dropdown menu.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ceced2fc-aa46-458d-ba87-ddd4b7bb1518
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/659922a9-0087-461c-90dd-6d9a58b81d4d/ceced2fc-aa46-458d-ba87-ddd4b7bb1518/P2PZNJ9
This commit is contained in:
paul-nothaft
2025-09-11 22:18:44 +00:00
parent d69c81185b
commit 4b96a8c939
3 changed files with 150 additions and 18 deletions
+2 -2
View File
@@ -19,8 +19,8 @@ localPort = 35345
externalPort = 3002
[[ports]]
localPort = 40125
externalPort = 3001
localPort = 40265
externalPort = 3003
[[ports]]
localPort = 41353
+26 -1
View File
@@ -14,11 +14,12 @@ interface KanbanBoardProps {
onTaskStatusChange?: (taskId: string, newStatus: Task['status']) => void;
onTaskUpdate?: (taskId: string, updates: Partial<Task>) => void;
onTaskClick?: (task: Task) => void;
onTaskDelete?: (taskId: string) => void;
}
type ViewMode = 'traditional' | 'weekly' | 'monthly';
export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate, onTaskClick }: KanbanBoardProps) {
export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate, onTaskClick, onTaskDelete }: KanbanBoardProps) {
const [viewMode, setViewMode] = useState<ViewMode>('traditional');
const [currentDate, setCurrentDate] = useState(new Date());
const [draggedTask, setDraggedTask] = useState<string | null>(null);
@@ -199,6 +200,14 @@ export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate, o
onTaskClick?.(task);
console.log(`Task clicked from board: ${task.title}`);
}}
onDelete={() => {
onTaskDelete?.(task.id);
console.log(`Delete task from board: ${task.title}`);
}}
onStatusChange={(newStatus) => {
onTaskStatusChange?.(task.id, newStatus);
console.log(`Task ${task.title} status changed to ${newStatus}`);
}}
isDragging={draggedTask === task.id}
/>
</div>
@@ -261,6 +270,14 @@ export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate, o
onTaskClick?.(task);
console.log(`Task clicked from board: ${task.title}`);
}}
onDelete={() => {
onTaskDelete?.(task.id);
console.log(`Delete task from board: ${task.title}`);
}}
onStatusChange={(newStatus) => {
onTaskStatusChange?.(task.id, newStatus);
console.log(`Task ${task.title} status changed to ${newStatus}`);
}}
isDragging={draggedTask === task.id}
/>
</div>
@@ -323,6 +340,14 @@ export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate, o
onTaskClick?.(task);
console.log(`Task clicked from board: ${task.title}`);
}}
onDelete={() => {
onTaskDelete?.(task.id);
console.log(`Delete task from board: ${task.title}`);
}}
onStatusChange={(newStatus) => {
onTaskStatusChange?.(task.id, newStatus);
console.log(`Task ${task.title} status changed to ${newStatus}`);
}}
isDragging={draggedTask === task.id}
/>
</div>
+122 -15
View File
@@ -1,7 +1,14 @@
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Clock, Calendar, Play, Pause, MoreHorizontal } from "lucide-react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Clock, Calendar, Play, Pause, MoreHorizontal, Edit, Trash2, Timer, CheckCircle } from "lucide-react";
import { useState } from "react";
import { Task, Label } from '@shared/schema';
import { useQuery } from '@tanstack/react-query';
@@ -11,11 +18,12 @@ interface TaskCardProps {
onPlay?: () => void;
onPause?: () => void;
onEdit?: () => void;
onDelete?: () => void;
onStatusChange?: (status: Task['status']) => void;
isDragging?: boolean;
}
export default function TaskCard({ task, onPlay, onPause, onEdit, onStatusChange, isDragging }: TaskCardProps) {
export default function TaskCard({ task, onPlay, onPause, onEdit, onDelete, onStatusChange, isDragging }: TaskCardProps) {
const [isTimerRunning, setIsTimerRunning] = useState(task.isTracking);
// Fetch labels to get the label color
@@ -122,19 +130,118 @@ export default function TaskCard({ task, onPlay, onPause, onEdit, onStatusChange
</div>
<div className="flex flex-col gap-2">
<Button
size="icon"
variant="ghost"
onClick={(e) => {
e.stopPropagation();
onEdit?.();
console.log(`Edit task: ${task.title}`);
}}
data-testid={`button-edit-${task.id}`}
className="w-6 h-6"
>
<MoreHorizontal className="w-3 h-3" />
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
variant="ghost"
onClick={(e) => e.stopPropagation()}
data-testid={`button-menu-${task.id}`}
className="w-6 h-6"
>
<MoreHorizontal className="w-3 h-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" onClick={(e) => e.stopPropagation()}>
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
onEdit?.();
console.log(`Edit task: ${task.title}`);
}}
data-testid={`menu-edit-${task.id}`}
>
<Edit className="w-4 h-4 mr-2" />
Edit Task
</DropdownMenuItem>
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
handleToggleTimer();
}}
data-testid={`menu-timer-${task.id}`}
>
<Timer className="w-4 h-4 mr-2" />
{isTimerRunning ? 'Stop Timer' : 'Start Timer'}
</DropdownMenuItem>
<DropdownMenuSeparator />
{task.status !== 'done' && (
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
onStatusChange?.('done');
console.log(`Mark task as done: ${task.title}`);
}}
data-testid={`menu-complete-${task.id}`}
>
<CheckCircle className="w-4 h-4 mr-2" />
Mark as Done
</DropdownMenuItem>
)}
{task.status === 'todo' && (
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
onStatusChange?.('inProgress');
console.log(`Start working on: ${task.title}`);
}}
data-testid={`menu-start-${task.id}`}
>
<Play className="w-4 h-4 mr-2" />
Start Working
</DropdownMenuItem>
)}
{task.status === 'inProgress' && (
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
onStatusChange?.('todo');
console.log(`Move back to todo: ${task.title}`);
}}
data-testid={`menu-todo-${task.id}`}
>
<Clock className="w-4 h-4 mr-2" />
Move to Todo
</DropdownMenuItem>
)}
{task.status === 'done' && (
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
onStatusChange?.('inProgress');
console.log(`Reopen task: ${task.title}`);
}}
data-testid={`menu-reopen-${task.id}`}
>
<Play className="w-4 h-4 mr-2" />
Reopen Task
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
{onDelete && (
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
onDelete();
console.log(`Delete task: ${task.title}`);
}}
className="text-destructive"
data-testid={`menu-delete-${task.id}`}
>
<Trash2 className="w-4 h-4 mr-2" />
Delete Task
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
<Button
size="icon"