diff --git a/app/(app)/dashboard/KanbanBoard.tsx b/app/(app)/dashboard/KanbanBoard.tsx
index 08be5e8..baed71d 100644
--- a/app/(app)/dashboard/KanbanBoard.tsx
+++ b/app/(app)/dashboard/KanbanBoard.tsx
@@ -1,6 +1,6 @@
/**
* @file dashboard/KanbanBoard.tsx
- * @description Client component wrapping the kanban columns grid, tracking individual task update states, and handling asynchronous status mutations via API.
+ * @description Client component wrapping the kanban columns grid, tracking individual task update/deletion states, and handling asynchronous mutations via API.
*/
"use client";
@@ -13,7 +13,7 @@ import KanbanBoardHeader from "./components/KanbanBoardHeader";
/**
* Renders the responsive grid container of kanban columns, coordinating state tracking
- * for active task updates and triggering status mutation API requests.
+ * for active task updates/deletions and triggering mutation API requests.
*
* @param {Object} props - The component props.
* @param {Task[]} props.tasks - The array of task items displayed across the board.
@@ -61,10 +61,39 @@ export default function KanbanBoard({ tasks }: { tasks: Task[] }) {
});
};
+ /**
+ * Deletes a specific task by sending a DELETE request to the API,
+ * managing loading states, and refreshing the router upon success.
+ *
+ * @param {string} taskId - The unique identifier of the task to delete.
+ */
+ const deleteTask = (taskId: string) => {
+ setUpdatingTaskIds((prev) => new Set(prev).add(taskId));
+
+ startTransition(async () => {
+ try {
+ const response = await fetch(`/api/tasks/${taskId}`, {
+ method: "DELETE",
+ });
+
+ if (!response.ok) throw new Error("Failed to delete task");
+ await router.refresh();
+ } catch (error) {
+ console.error("Error during task deletion:", error);
+ } finally {
+ setUpdatingTaskIds((prev) => {
+ const next = new Set(prev);
+ next.delete(taskId);
+ return next;
+ });
+ }
+ });
+ };
+
return (
{/* Workspace Header */}
-
+
{/* Kanban Columns Grid */}
@@ -80,6 +109,7 @@ export default function KanbanBoard({ tasks }: { tasks: Task[] }) {
color={col.color}
updatingTaskIds={updatingTaskIds}
onTaskMove={updateTaskStatus}
+ onTaskDelete={deleteTask}
/>
);
})}
diff --git a/app/(app)/dashboard/KanbanCard.tsx b/app/(app)/dashboard/KanbanCard.tsx
index 716a755..6c286db 100644
--- a/app/(app)/dashboard/KanbanCard.tsx
+++ b/app/(app)/dashboard/KanbanCard.tsx
@@ -23,6 +23,7 @@ export default function KanbanCard({
task,
isUpdating = false,
onStatusChange,
+ onDelete,
}: KanbanCardProps) {
/**
* Initiates the drag action on a task card if not currently updating, storing its ID and status payload.
@@ -88,7 +89,11 @@ export default function KanbanCard({
creator={task.creator}
assignees={task.assignees}
/>
-
+ onDelete?.(task.id)}
+ />
diff --git a/app/(app)/dashboard/KanbanColumn.tsx b/app/(app)/dashboard/KanbanColumn.tsx
index 2190a9a..a36f816 100644
--- a/app/(app)/dashboard/KanbanColumn.tsx
+++ b/app/(app)/dashboard/KanbanColumn.tsx
@@ -97,6 +97,7 @@ export default function KanbanColumn(props: KanbanColumnProps) {
task={task}
isUpdating={props.updatingTaskIds?.has(task.id)}
onStatusChange={props.onTaskMove}
+ onDelete={props.onTaskDelete}
/>
))
)}
diff --git a/app/(app)/dashboard/components/KanbanBoardHeader.tsx b/app/(app)/dashboard/components/KanbanBoardHeader.tsx
index 7906d9d..918f7bd 100644
--- a/app/(app)/dashboard/components/KanbanBoardHeader.tsx
+++ b/app/(app)/dashboard/components/KanbanBoardHeader.tsx
@@ -1,16 +1,45 @@
/**
* @file dashboard/components/KanbanBoardHeader.tsx
- * @description Component rendering the top title header and action buttons for the dashboard view.
+ * @description Component rendering the top title header with an integrated delete drop zone that appears during drag-and-drop interactions.
*/
-import { Plus, Sparkles } from "lucide-react";
+"use client";
+
+import { useState, useEffect } from "react";
+import { Plus, Sparkles, Trash2 } from "lucide-react";
/**
- * Renders the dashboard header section including title, subtitle, workspace indicator, and action triggers.
+ * Renders the dashboard header featuring a workspace title, subtitle, new task action button,
+ * and a dynamic drop zone for deleting tasks during drag operations.
*
- * @returns {JSX.Element} The rendered dashboard header component.
+ * @param {Object} props - The component props.
+ * @param {(taskId: string) => void} props.onTaskDelete - Callback function invoked when a task is dropped into the delete zone.
+ * @returns {JSX.Element} The rendered kanban board header component.
*/
-export default function KanbanBoardHeader() {
+export default function KanbanBoardHeader({
+ onTaskDelete,
+}: {
+ onTaskDelete: (taskId: string) => void;
+}) {
+ const [isDragging, setIsDragging] = useState(false);
+ const [isOver, setIsOver] = useState(false);
+
+ useEffect(() => {
+ const handleDragStart = () => setIsDragging(true);
+ const handleDragEnd = () => {
+ setIsDragging(false);
+ setIsOver(false);
+ };
+
+ window.addEventListener("dragstart", handleDragStart);
+ window.addEventListener("dragend", handleDragEnd);
+
+ return () => {
+ window.removeEventListener("dragstart", handleDragStart);
+ window.removeEventListener("dragend", handleDragEnd);
+ };
+ }, []);
+
return (
@@ -25,10 +54,38 @@ export default function KanbanBoardHeader() {
-
+ {isDragging ? (
+
{
+ e.preventDefault();
+ e.dataTransfer.dropEffect = "move";
+ setIsOver(true);
+ }}
+ onDragLeave={() => setIsOver(false)}
+ onDrop={(e) => {
+ e.preventDefault();
+ setIsOver(false);
+ setIsDragging(false);
+ const taskId = e.dataTransfer.getData("text/plain");
+ if (taskId) {
+ onTaskDelete(taskId);
+ }
+ }}
+ className={`flex items-center gap-2 px-4 py-2.5 rounded-xl font-medium text-sm border-2 border-dashed cursor-pointer select-none transition-all duration-300 ease-out transform ${
+ isOver
+ ? "bg-destructive border-destructive scale-110 shadow-lg animate-pulse"
+ : "bg-destructive/10 border-destructive/40 text-destructive scale-100 opacity-90 hover:opacity-100 animate-in fade-in zoom-in-95 duration-200"
+ }`}
+ >
+
+ Drop to Delete
+
+ ) : (
+
+ )}
);
diff --git a/app/(app)/dashboard/components/KanbanCardActions.tsx b/app/(app)/dashboard/components/KanbanCardActions.tsx
index 667077d..43b26bf 100644
--- a/app/(app)/dashboard/components/KanbanCardActions.tsx
+++ b/app/(app)/dashboard/components/KanbanCardActions.tsx
@@ -1,29 +1,32 @@
/**
* @file dashboard/components/KanbanCardActions.tsx
- * @description Client component rendering the mobile status transition dropdown for a kanban card.
+ * @description Client component rendering the mobile status transition and deletion dropdown for a kanban card.
*/
"use client";
import { useState, useEffect, useRef } from "react";
-import { MoreHorizontal, CornerDownRight } from "lucide-react";
+import { MoreHorizontal, CornerDownRight, Trash2 } from "lucide-react";
import { KANBAN_COLUMNS, TaskStatus } from "@/types/tasks";
/**
* Renders a mobile-only action menu component allowing users to move a task
- * between different kanban columns via a dropdown interface.
+ * between different kanban columns or delete it entirely via a dropdown interface.
*
* @param {Object} props - The component props.
* @param {TaskStatus} props.currentStatus - The current status category of the task.
* @param {(newStatus: TaskStatus) => void} props.onMove - Callback function triggered when a new status column is selected.
+ * @param {() => void} props.onDelete - Callback function triggered when the delete action is selected.
* @returns {JSX.Element} The rendered mobile card actions component.
*/
export default function KanbanCardActions({
currentStatus,
onMove,
+ onDelete,
}: {
currentStatus: TaskStatus;
onMove: (newStatus: TaskStatus) => void;
+ onDelete: () => void;
}) {
const [showMobileActions, setShowMobileActions] = useState(false);
const dropdownRef = useRef(null);
@@ -59,6 +62,17 @@ export default function KanbanCardActions({
setShowMobileActions(false);
};
+ /**
+ * Handles clicking the delete action item to trigger task deletion.
+ *
+ * @param {React.MouseEvent} e - The mouse event object.
+ */
+ const handleDeleteClick = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ onDelete();
+ setShowMobileActions(false);
+ };
+
return (
);
diff --git a/types/tasks.ts b/types/tasks.ts
index 750237d..5c396b4 100644
--- a/types/tasks.ts
+++ b/types/tasks.ts
@@ -46,12 +46,14 @@ export interface KanbanColumnProps extends KanbanColumnConfig {
tasks: Task[];
updatingTaskIds?: Set;
onTaskMove?: (taskId: string, targetStatus: TaskStatus) => void;
+ onTaskDelete?: (taskId: string) => void;
}
export interface KanbanCardProps {
task: Task;
isUpdating?: boolean;
onStatusChange?: (taskId: string, newStatus: TaskStatus) => void;
+ onDelete?: (taskId: string) => void;
}
// ==========================================