import { useState, useCallback, useMemo } from "react"; import { createRoute, useNavigate } from "@tanstack/react-router"; import { Route as appRoute } from "../_app"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { api, useApiQuery, useApiMutation } from "@/lib/api"; import { useRealtime } from "@/hooks/use-realtime"; import { DndContext, DragOverlay, closestCorners, KeyboardSensor, PointerSensor, useSensor, useSensors, type DragEndEvent, type DragStartEvent } from "@dnd-kit/core"; import { SortableContext, verticalListSortingStrategy, useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { Plus, GripVertical, Trash2, Calendar, Clock, ListTodo, Layout as LayoutIcon, Search, Filter, MoreHorizontal } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { EntityDetailPanel } from "@/components/entities/entity-detail-panel"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import type { Task, PaginatedResponse } from "@/lib/types"; import { cn } from "@/lib/utils"; const STATUS_COLUMNS = [ { id: "todo", label: "Todo", color: "bg-slate-500" }, { id: "in_progress", label: "In Progress", color: "bg-blue-500" }, { id: "done", label: "Done", color: "bg-green-500" }, { id: "cancelled", label: "Cancelled", color: "bg-red-500" }, ]; const PRIORITY_COLORS: Record = { urgent: "text-red-500 bg-red-500/10", high: "text-orange-500 bg-orange-500/10", medium: "text-blue-500 bg-blue-500/10", low: "text-slate-500 bg-slate-500/10", }; function SortableTaskCard({ task, onClick }: { task: Task; onClick: () => void }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: task.id }); const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.5 : 1, }; return (

{task.title}

{task.dueDate && ( {new Date(task.dueDate).toLocaleDateString()} )} {task.priority} {task.tags?.slice(0, 2).map((tag) => ( {tag.name} ))}
); } function TaskForm({ task, onClose }: { task?: Task; onClose: () => void }) { const queryClient = useQueryClient(); const [title, setTitle] = useState(task?.title || ""); const [description, setDescription] = useState(task?.description || ""); const [status, setStatus] = useState(task?.status || "todo"); const [priority, setPriority] = useState(task?.priority || "medium"); const [dueDate, setDueDate] = useState(task?.dueDate ? task.dueDate.slice(0, 10) : ""); const createMutation = useMutation({ mutationFn: (data: any) => api.post("/tasks", data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["tasks"] }); onClose(); }, }); const updateMutation = useMutation({ mutationFn: (data: any) => api.patch("/tasks/" + task!.id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["tasks"] }); onClose(); }, }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!title.trim()) return; const data: any = { title: title.trim(), description: description || null, status, priority }; if (dueDate) data.dueDate = new Date(dueDate).toISOString(); if (task) { updateMutation.mutate(data); } else { createMutation.mutate(data); } }; return (
setTitle(e.target.value)} placeholder="Task title" required />