"use client" import { useState, useCallback, useEffect, useMemo } from "react" import { useReactTable, getCoreRowModel, getPaginationRowModel, flexRender, type ColumnDef, } from "@tanstack/react-table" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { IconPencil, IconTrash, IconLink, } from "@tabler/icons-react" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { TaskFormDialog } from "./task-form-dialog" import { DependencyDialog } from "./dependency-dialog" import { deleteTask } from "@/app/actions/schedule" import type { ScheduleTaskData, TaskDependencyData, } from "@/lib/schedule/types" import { useRouter } from "next/navigation" import { toast } from "sonner" import { format, parseISO } from "date-fns" interface ScheduleListViewProps { projectId: string tasks: ScheduleTaskData[] dependencies: TaskDependencyData[] } function StatusDot({ task }: { task: ScheduleTaskData }) { let color = "bg-gray-400" if (task.status === "COMPLETE") color = "bg-green-500" else if (task.status === "IN_PROGRESS") color = "bg-blue-500" else if (task.status === "BLOCKED") color = "bg-red-500" else if (task.isCriticalPath) color = "bg-orange-500" return } function ProgressRing({ percent, size = 28, }: { percent: number size?: number }) { const stroke = 3 const radius = (size - stroke) / 2 const circumference = 2 * Math.PI * radius const offset = circumference - (percent / 100) * circumference return (
{percent}%
) } function InitialsAvatar({ name }: { name: string }) { const initials = name .split(" ") .map((w) => w[0]) .join("") .slice(0, 2) .toUpperCase() return (
{initials}
{name}
) } function formatDate(dateStr: string): string { try { return format(parseISO(dateStr), "MMM d, yyyy") } catch { return dateStr } } export function ScheduleListView({ projectId, tasks, dependencies, }: ScheduleListViewProps) { const router = useRouter() const [taskFormOpen, setTaskFormOpen] = useState(false) const [editingTask, setEditingTask] = useState(null) const [depDialogOpen, setDepDialogOpen] = useState(false) const [localTasks, setLocalTasks] = useState(tasks) const [rowSelection, setRowSelection] = useState>({}) useEffect(() => { setLocalTasks(tasks) }, [tasks]) const handleDelete = useCallback( async (taskId: string) => { const result = await deleteTask(taskId) if (result.success) { router.refresh() } else { toast.error(result.error) } }, [router] ) const columns: ColumnDef[] = useMemo( () => [ { id: "select", header: ({ table }) => ( table.toggleAllRowsSelected(!!value) } /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} /> ), size: 32, }, { id: "idNum", header: "#", cell: ({ row }) => ( {row.original.sortOrder + 1} ), size: 40, }, { accessorKey: "title", header: "Title", cell: ({ row }) => (
{row.original.title}
), }, { id: "complete", header: "Complete", cell: ({ row }) => ( ), size: 70, }, { accessorKey: "phase", header: "Phase", cell: ({ row }) => ( {row.original.phase} ), }, { id: "duration", header: "Duration", cell: ({ row }) => ( {row.original.workdays} {row.original.workdays === 1 ? "day" : "days"} ), size: 80, }, { accessorKey: "startDate", header: "Start", cell: ({ row }) => ( {formatDate(row.original.startDate)} ), }, { accessorKey: "endDateCalculated", header: "End", cell: ({ row }) => ( {formatDate(row.original.endDateCalculated)} ), }, { id: "assignedTo", header: "Assigned To", cell: ({ row }) => row.original.assignedTo ? ( ) : ( - ), }, { id: "actions", cell: ({ row }) => (
), size: 80, }, ], [handleDelete] ) const table = useReactTable({ data: localTasks, columns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getRowId: (row) => row.id, onRowSelectionChange: setRowSelection, state: { rowSelection }, initialState: { pagination: { pageSize: 25 } }, }) return (
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( {header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext() )} ))} ))} {table.getRowModel().rows.length === 0 ? ( No tasks yet. Click "New Schedule Item" to get started. ) : ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender( cell.column.columnDef.cell, cell.getContext() )} ))} )) )}
{table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1} - {Math.min( (table.getState().pagination.pageIndex + 1) * table.getState().pagination.pageSize, localTasks.length )}{" "} of {localTasks.length} items
) }