"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 (
)
}
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
)
}