diff --git a/frontend/src/components/git/AssignIssueAgentDialog.tsx b/frontend/src/components/git/AssignIssueAgentDialog.tsx index 7720bbf..5bead7b 100644 --- a/frontend/src/components/git/AssignIssueAgentDialog.tsx +++ b/frontend/src/components/git/AssignIssueAgentDialog.tsx @@ -8,6 +8,10 @@ import { useListAgentsApiV1AgentsGet, type listAgentsApiV1AgentsGetResponse, } from "@/api/generated/agents/agents"; +import { + useListBoardsApiV1BoardsGet, + type listBoardsApiV1BoardsGetResponse, +} from "@/api/generated/boards/boards"; import { ApiError } from "@/api/mutator"; import { Dialog, @@ -20,7 +24,11 @@ import { import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import type { ForgejoIssue, AssignIssueAgentResponse } from "@/lib/api-forgejo"; -import { assignIssueToAgent, getLinkedBoardsForRepository } from "@/lib/api-forgejo"; +import { + assignIssueToAgent, + getLinkedBoardsForRepository, + linkBoardForgejoRepository, +} from "@/lib/api-forgejo"; type LinkedBoard = { id: string; name: string }; @@ -74,6 +82,8 @@ export function AssignIssueAgentDialog({ const [linkedBoards, setLinkedBoards] = useState([]); const [boardsLoading, setBoardsLoading] = useState(false); const [linkedBoardsLoaded, setLinkedBoardsLoaded] = useState(false); + const [linkBoardId, setLinkBoardId] = useState(""); + const [isLinkingRepository, setIsLinkingRepository] = useState(false); // Fetch only boards linked to this issue's repository — prevents picking a // board that the backend will reject with "not linked to any board". @@ -110,10 +120,31 @@ export function AssignIssueAgentDialog({ { query: { enabled: open && !!boardId, refetchOnMount: "always" } }, ); + const boardsQuery = useListBoardsApiV1BoardsGet< + listBoardsApiV1BoardsGetResponse, + ApiError + >( + { limit: 200 }, + { + query: { + enabled: + open && + linkedBoardsLoaded && + !boardsLoading && + linkedBoards.length === 0, + refetchOnMount: "always", + }, + }, + ); + const agents = agentsQuery.data?.status === 200 ? (agentsQuery.data.data.items ?? []) : []; + const allBoards = + boardsQuery.data?.status === 200 + ? (boardsQuery.data.data.items ?? []) + : []; useEffect(() => { if (!open) { @@ -124,6 +155,8 @@ export function AssignIssueAgentDialog({ setStartImmediately(true); setError(null); setLinkedBoardsLoaded(false); + setLinkBoardId(""); + setIsLinkingRepository(false); } }, [open]); @@ -164,6 +197,32 @@ export function AssignIssueAgentDialog({ } }; + const handleLinkRepositoryToBoard = async () => { + if (!issue || !linkBoardId) return; + setIsLinkingRepository(true); + setError(null); + + try { + await linkBoardForgejoRepository(linkBoardId, issue.repository_id); + const linkedBoard = allBoards.find((board) => board.id === linkBoardId); + const nextBoard = { + id: linkBoardId, + name: linkedBoard?.name ?? "Selected board", + }; + setLinkedBoards([nextBoard]); + setBoardId(nextBoard.id); + setLinkBoardId(""); + } catch (err) { + setError( + err instanceof Error + ? err.message + : "Failed to link repository to board", + ); + } finally { + setIsLinkingRepository(false); + } + }; + const toggleSwitch = ( value: boolean, setter: (v: boolean) => void, @@ -227,13 +286,50 @@ export function AssignIssueAgentDialog({ use its Git Project repositories panel, or manage tracked repositories first.

- - Manage Git repositories - - +
+ + + {boardsQuery.isError ? ( +

+ Unable to load boards. You can still manage repositories + from Git Projects. +

+ ) : null} +
+
+ + + Manage Git repositories + + +
) : ( <>