import { Check, Edit, Tag, Trash2 } from "lucide-react"; import React, { useState } from "react"; import { useDrag, useDrop } from "react-dnd"; import { Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../../../ui/primitives"; import { cn } from "../../../ui/primitives/styles"; import { EditableTableCell } from "../components/EditableTableCell"; import { TaskAssignee } from "../components/TaskAssignee"; import { useDeleteTask, useUpdateTask } from "../hooks"; import type { Assignee, Task } from "../types"; import { getOrderColor, getOrderGlow, ItemTypes } from "../utils/task-styles"; interface TableViewProps { tasks: Task[]; projectId: string; onTaskView?: (task: Task) => void; onTaskComplete?: (taskId: string) => void; onTaskDelete?: (task: Task) => void; onTaskReorder: (taskId: string, newOrder: number, status: Task["status"]) => void; onTaskUpdate?: (taskId: string, updates: Partial) => Promise; } interface DraggableRowProps { task: Task; index: number; projectId: string; onTaskView?: (task: Task) => void; onTaskComplete?: (taskId: string) => void; onTaskDelete?: (task: Task) => void; onTaskReorder: (taskId: string, newOrder: number, status: Task["status"]) => void; } const DraggableRow = ({ task, index, projectId, onTaskView, onTaskComplete, onTaskDelete, onTaskReorder, }: DraggableRowProps) => { const updateTaskMutation = useUpdateTask(projectId); const deleteTaskMutation = useDeleteTask(projectId); const [localAssignee, setLocalAssignee] = useState(task.assignee); // Drag and drop handlers const [{ isDragging }, drag] = useDrag({ type: ItemTypes.TASK, item: { id: task.id, index, status: task.status }, collect: (monitor) => ({ isDragging: !!monitor.isDragging(), }), }); const [{ isOver }, drop] = useDrop({ accept: ItemTypes.TASK, hover: (draggedItem: { id: string; index: number; status: Task["status"] }, monitor) => { if (!monitor.isOver({ shallow: true })) return; if (draggedItem.id === task.id) return; if (draggedItem.status !== task.status) return; const draggedIndex = draggedItem.index; const hoveredIndex = index; if (draggedIndex === hoveredIndex) return; // Move the task for visual feedback onTaskReorder(draggedItem.id, hoveredIndex, task.status); // Update the dragged item's index draggedItem.index = hoveredIndex; }, collect: (monitor) => ({ isOver: !!monitor.isOver(), }), }); // Handle field updates using mutations const handleUpdateField = async (field: keyof Task, value: string) => { const updates: Partial = { [field]: value }; await updateTaskMutation.mutateAsync({ taskId: task.id, updates, }); }; const handleAssigneeChange = (newAssignee: Assignee) => { setLocalAssignee(newAssignee); updateTaskMutation.mutate({ taskId: task.id, updates: { assignee: newAssignee }, }); }; const handleDelete = () => { if (onTaskDelete) { onTaskDelete(task); } }; const handleComplete = () => { if (onTaskComplete) { onTaskComplete(task.id); } }; const handleEdit = () => { if (onTaskView) { onTaskView(task); } }; return ( drag(drop(node))} className={cn( "group transition-all duration-200 cursor-move", index % 2 === 0 ? "bg-white/50 dark:bg-black/50" : "bg-gray-50/80 dark:bg-gray-900/30", "hover:bg-gradient-to-r hover:from-cyan-50/70 hover:to-purple-50/70", "dark:hover:from-cyan-900/20 dark:hover:to-purple-900/20", "border-b border-gray-200 dark:border-gray-800", isDragging && "opacity-50 scale-105 shadow-lg", isOver && "bg-cyan-100/50 dark:bg-cyan-900/20 border-cyan-400", )} > {/* Priority/Order Indicator */}
{/* Title */} handleUpdateField("title", value)} placeholder="Enter task title" className="font-medium" isUpdating={updateTaskMutation.isPending} /> {/* Status */} handleUpdateField("status", value)} type="status" isUpdating={updateTaskMutation.isPending} /> {/* Feature */}
{task.feature && } handleUpdateField("feature", value)} placeholder="Add feature" className="text-sm" isUpdating={updateTaskMutation.isPending} />
{/* Assignee */} {/* Actions */}
Edit task Mark as complete Delete task
); }; export const TableView = ({ tasks, projectId, onTaskView, onTaskComplete, onTaskDelete, onTaskReorder, }: TableViewProps) => { // Group tasks by status for better organization const groupedTasks = React.useMemo(() => { const groups: Record = { todo: [], doing: [], review: [], done: [], }; tasks.forEach((task) => { groups[task.status].push(task); }); // Sort each group by task_order Object.keys(groups).forEach((status) => { groups[status as Task["status"]].sort((a, b) => a.task_order - b.task_order); }); return groups; }, [tasks]); const statusOrder: Task["status"][] = ["todo", "doing", "review", "done"]; return (
{statusOrder.map((status) => { const statusTasks = groupedTasks[status]; if (statusTasks.length === 0) return null; return ( {/* Status group header */} {/* Tasks in this status */} {statusTasks.map((task, index) => ( ))} ); })} {tasks.length === 0 && ( )}
Title Status Feature Assignee Actions
{status} ({statusTasks.length})
No tasks yet. Create your first task to get started.
); };