import { useMemo, useState } from "react"; import { useParams, useSearchParams } from "react-router-dom"; import { toast } from "sonner"; import { AgentProfile } from "../components/AgentProfile"; import { FilterBar } from "../components/FilterBar"; import { AgentAvatarOverlay } from "../components/FloatingAvatar"; import { KanbanColumn } from "../components/KanbanColumn"; import { TaskChatDrawer } from "../components/TaskChatDrawer"; import { TaskDetail } from "../components/TaskDetail"; import { TaskSchemaModal } from "../components/TaskSchemaModal"; import { Button } from "../components/ui/button"; import { useAgentPresence } from "../hooks/useAgentPresence"; import { useBoard } from "../hooks/useBoard"; import { api } from "../lib/api"; const TASK_STATUSES = ["backlog", "todo", "in_progress", "in_review", "done", "cancelled"] as const; const TASK_STATUS_LABELS: Record = { backlog: "Backlog", todo: "Todo", in_progress: "In Progress", in_review: "In Review", done: "Done", cancelled: "Cancelled", }; export function BoardPage() { const { boardId, projectId: routeProjectId } = useParams<{ boardId?: string; projectId?: string }>(); const [searchParams] = useSearchParams(); const targetId = routeProjectId || boardId; const { board, loading, error, refresh } = useBoard(targetId, !!routeProjectId); const projectId = routeProjectId || searchParams.get("projectId") || (board as any)?.project_id || undefined; const avatars = useAgentPresence(board?.id || targetId); const [selectedTask, setSelectedTask] = useState(null); const [selectedAgent, setSelectedAgent] = useState(null); const [chatTask, setChatTask] = useState(null); const [activeRepository, setActiveRepository] = useState(null); const [activeLabel, setActiveLabel] = useState(null); const [mobileTab, setMobileTab] = useState(0); const [viewMode, _setViewMode] = useState<"board" | "list" | "timeline" | "due">("board"); const [showFilterBar, setShowFilterBar] = useState(false); const repositories = useMemo(() => { if (!board?.tasks) return []; const map = new Map(); for (const task of board.tasks) { if (task.repository_id && task.repository_name) { map.set(task.repository_id, task.repository_name); } } return Array.from(map.entries()) .map(([id, name]) => ({ id, name })) .sort((a, b) => a.name.localeCompare(b.name)); }, [board]); const filteredTasks = useMemo(() => { if (!board?.tasks) return []; return board.tasks.filter((t: any) => { if (activeRepository && t.repository_id !== activeRepository) return false; if (activeLabel && !t.labels?.includes(activeLabel)) return false; return true; }); }, [board, activeRepository, activeLabel]); const handleTaskDrop = async (taskId: string, newStatus: string) => { try { await api.tasks.update(taskId, { status: newStatus }); toast.success(`Đã chuyển Task sang ${TASK_STATUS_LABELS[newStatus] || newStatus}`); await refresh(); } catch (err: any) { console.error("Failed to move task", err); toast.error(err.message || "Không thể di chuyển Task"); } }; const columns = useMemo(() => { return TASK_STATUSES.map((status) => ({ status, name: TASK_STATUS_LABELS[status], tasks: filteredTasks.filter((t: any) => t.status === status), })); }, [filteredTasks]); if (error === "NOT_AUTHENTICATED") { window.location.href = "/auth"; return null; } if (loading) { return (
{[0, 1, 2, 3, 4, 5].map((i) => (
{[0, 1].map((j) => (
))}
))}
); } if (!board) { return (
Board not found
); } return (
{/* Top Board Action & Title Bar */} {!routeProjectId && (

📋 {board.name}

)} {/* Compact Action Bar */}
refresh()} trigger={ } />
{/* Collapsible Filter Bar */} {showFilterBar && ( )} {error && (
{error}
)} {/* View Switcher Output */} {viewMode === "board" && ( <> {/* Mobile tab switcher */}
{columns.map((col, i) => ( ))}
{/* Desktop: 5-column grid */}
{columns.map((col) => ( ))}
{/* Mobile: single column based on tab */}
{columns .filter((_, i) => i === mobileTab) .map((col) => ( ))}
)} {selectedTask && ( setSelectedTask(null)} onRefresh={refresh} onAgentClick={(agentId) => setSelectedAgent(agentId)} /> )} {chatTask && ( { if (!open) setChatTask(null); }} taskId={chatTask.id} task={chatTask} /> )} {selectedAgent && ( setSelectedAgent(null)} onTaskClick={(taskId) => { setSelectedAgent(null); setSelectedTask(taskId); }} /> )}
); }