import { useState } from "react"; import { useNavigate } from "react-router-dom"; import { useBoardDetailQuery } from "@/hooks/useBoardsQuery"; import { useBoardOperations } from "@/hooks/useBoardOperations"; import { LoadingState } from "@/components/ui/LoadingState"; import { ErrorState } from "@/components/ui/ErrorState"; import { AlertCircle, Plus } from "lucide-react"; import type { CardResponse } from "@/client/model"; import { BOARD_PAGE } from "@/css/app/board-styles"; import { BoardHeader } from "./BoardHeader"; import { BoardColumn } from "./BoardColumn"; import { BoardModals } from "./BoardModals"; interface KanbanBoardProps { projectName: string; boardId: string; } export function KanbanBoard({ projectName, boardId }: KanbanBoardProps) { const navigate = useNavigate(); const { data: boardDetail, isLoading, refetch } = useBoardDetailQuery(projectName, boardId); const ops = useBoardOperations(projectName); const [isCreateColumnOpen, setIsCreateColumnOpen] = useState(false); const [isCreateCardOpen, setIsCreateCardOpen] = useState(false); const [selectedCard, setSelectedCard] = useState(null); const [newColumnName, setNewColumnName] = useState(""); const [newCardTitle, setNewCardTitle] = useState(""); const [newCardDescription, setNewCardDescription] = useState(""); const [editCardTitle, setEditCardTitle] = useState(""); const [editCardDescription, setEditCardDescription] = useState(""); const handleCreateColumn = async () => { if (!newColumnName.trim()) return; await ops.createColumn.mutateAsync({ boardId, params: { name: newColumnName.trim(), position: boardDetail?.columns.length || 0 }, }); setIsCreateColumnOpen(false); setNewColumnName(""); refetch(); }; const handleCreateCard = async () => { if (!newCardTitle.trim() || !isCreateCardOpen) return; await ops.createCard.mutateAsync({ column_id: isCreateCardOpen, title: newCardTitle.trim(), description: newCardDescription.trim() || undefined, }); setIsCreateCardOpen(false); setNewCardTitle(""); setNewCardDescription(""); refetch(); }; const handleUpdateCard = async () => { if (!selectedCard || !editCardTitle.trim()) return; await ops.updateCard.mutateAsync({ id: selectedCard.id, params: { title: editCardTitle.trim(), description: editCardDescription.trim() || null }, }); setSelectedCard(null); refetch(); }; const handleMoveCard = async (cardId: string, targetColumnId: string) => { await ops.moveCard.mutateAsync({ id: cardId, params: { target_column_id: targetColumnId, position: 0 }, }); refetch(); }; const handleDeleteBoard = async () => { if (confirm("Are you sure you want to delete this board?")) { await ops.deleteBoard.mutateAsync(boardId); navigate(`/${projectName}/board`); } }; const handleDeleteCard = async () => { if (!selectedCard) return; if (confirm("Delete this card?")) { await ops.deleteCard.mutateAsync(selectedCard.id); setSelectedCard(null); refetch(); } }; if (isLoading) return ; if (!boardDetail) return ( navigate(`/${projectName}/board`)} /> ); return ( <>
setIsCreateColumnOpen(true)} />
{boardDetail.columns.map((col) => ( setIsCreateCardOpen(columnId)} onDeleteColumn={(columnId) => { if (confirm("Delete column?")) { ops.deleteColumn.mutate(columnId); refetch(); } }} onCardClick={(card) => { setSelectedCard(card); setEditCardTitle(card.title); setEditCardDescription(card.description || ""); }} onMoveCard={handleMoveCard} /> ))} {boardDetail.columns.length === 0 && (

Empty Board

Add your first column (e.g. To Do, Doing, Done) to start tracking work.

)}
{ setIsCreateColumnOpen(false); setNewColumnName(""); }} isCreateCardOpen={isCreateCardOpen} newCardTitle={newCardTitle} newCardDescription={newCardDescription} isCreatingCard={ops.createCard.isPending} onNewCardTitleChange={setNewCardTitle} onNewCardDescriptionChange={setNewCardDescription} onCreateCard={handleCreateCard} onCloseCard={() => { setIsCreateCardOpen(false); setNewCardTitle(""); setNewCardDescription(""); }} selectedCard={selectedCard} editCardTitle={editCardTitle} editCardDescription={editCardDescription} isUpdatingCard={ops.updateCard.isPending} onEditTitleChange={setEditCardTitle} onEditDescriptionChange={setEditCardDescription} onUpdateCard={handleUpdateCard} onDeleteCard={handleDeleteCard} onCloseDetail={() => setSelectedCard(null)} />
); }