flowstate/app/(app)/dashboard/column/Column.tsx

117 lines
3.9 KiB
TypeScript

/**
* @file dashboard/column/Column.tsx
* @description Client component rendering a single column container supporting drag-and-drop drop targets, task lists, and dynamic updating/deleting states.
*/
"use client";
import { useState } from "react";
import Card from "../card/Card";
import ColumnHeader from "./ColumnHeader";
import ColumnEmptyState from "./ColumnEmptyState";
import { Task, TaskStatus } from "@/types/task";
/**
* Properties for the Column component.
*
* @interface ColumnProps
* @property {TaskStatus} id - The unique status identifier for the column.
* @property {string} title - The display title of the column.
* @property {string} color - The indicator color styling for the column header.
* @property {number} count - The total count of tasks within this column.
* @property {Task[]} tasks - The array of tasks belonging to this column.
* @property {Set<string>} [updatingTaskIds] - A set of task IDs currently undergoing updates.
* @property {(taskId: string, targetStatus: TaskStatus) => void} [onTaskMove] - Callback triggered when a task is moved to a new status column.
* @property {(taskId: string) => void} [onTaskDelete] - Callback triggered when a task deletion is requested.
*/
export interface ColumnProps {
id: TaskStatus;
title: string;
color: string;
count: number;
tasks: Task[];
updatingTaskIds?: Set<string>;
onTaskMove?: (taskId: string, targetStatus: TaskStatus) => void;
onTaskDelete?: (taskId: string) => void;
}
/**
* Renders an interactive board column supporting drag-over drop target indicators,
* header info, and a mapped list of task cards with status change and deletion callbacks.
*
* @param {ColumnProps} props - The component props containing column metadata, task arrays, and handlers.
* @returns {JSX.Element} The rendered column component.
*/
export default function Column(props: ColumnProps) {
const [isDraggingOver, setIsDraggingOver] = useState(false);
/**
* Handles the drag-over event to allow dropping tasks into the column.
*
* @param {React.DragEvent<HTMLDivElement>} e - The drag event object.
*/
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.dataTransfer.dropEffect = "move";
setIsDraggingOver(true);
};
/**
* Resets the drag-over highlight state when a dragged element leaves the column area.
*/
const handleDragLeave = () => {
setIsDraggingOver(false);
};
/**
* Handles dropping a task card onto the column, extracting task data and triggering the move handler.
*
* @param {React.DragEvent<HTMLDivElement>} e - The drop event object.
*/
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
setIsDraggingOver(false);
const taskId = e.dataTransfer.getData("text/plain");
const sourceStatus = e.dataTransfer.getData("sourceStatus") as TaskStatus;
if (!taskId || sourceStatus === props.id) return;
props.onTaskMove?.(taskId, props.id);
};
return (
<div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={`w-full flex flex-col rounded-2xl p-4 transition-all duration-300 bg-background-muted/40 border ${
isDraggingOver
? "border-primary/80 bg-primary/5 shadow-lg ring-4 ring-primary/10"
: "border-border/60 shadow-sm"
}`}
>
<ColumnHeader
title={props.title}
color={props.color}
count={props.count}
/>
<div className="flex flex-col gap-3">
{props.tasks.length === 0 ? (
<ColumnEmptyState />
) : (
props.tasks.map((task) => (
<Card
key={task.id}
task={task}
isUpdating={props.updatingTaskIds?.has(task.id)}
onStatusChange={props.onTaskMove}
onDelete={props.onTaskDelete}
/>
))
)}
</div>
</div>
);
}