import { apiGet, apiPost, apiPatch, apiDelete } from "@/api/client"; import type { CanonicalMatch } from "@/api/canonicalSystems"; export interface System { id: number; project_id: number; name: string; /** * The global area this System is an instance of, or null. Null is a valid * resting state — a project-specific area should stay unmapped. */ canonical_id: number | null; description: string; color: string | null; status: "active" | "archived"; order_index: number; open_issue_count: number; created_at: string | null; updated_at: string | null; } export async function listSystems(projectId: number): Promise { const data = await apiGet<{ systems: System[] }>(`/api/projects/${projectId}/systems`); return data.systems; } /** * A created System, plus the catalog's answer about its name. An `exact` * catalog hit is applied by the server and arrives as a populated * `canonical_id`; an `overlap` is only OFFERED, and comes back here for the * caller to accept or ignore. * * A same-named System in this project is a 409 ApiError carrying * `{duplicate, existing_id}` — the same gate the MCP door enforces (#2482). */ export interface CreatedSystem extends System { canonical_suggestion?: CanonicalMatch; } export async function createSystem( projectId: number, data: { name: string; description?: string; color?: string; canonical_id?: number }, ): Promise { return apiPost(`/api/projects/${projectId}/systems`, data); } export async function updateSystem( projectId: number, systemId: number, data: Partial<{ name: string; description: string; color: string | null; status: "active" | "archived"; order_index: number; }>, ): Promise { return apiPatch(`/api/projects/${projectId}/systems/${systemId}`, data); } export async function deleteSystem(projectId: number, systemId: number): Promise { return apiDelete(`/api/projects/${projectId}/systems/${systemId}`); } // Lightweight issue shape returned by the project-issues list endpoint. export interface TaskLike { id: number; title: string; status: string; priority: string; systems?: System[]; updated_at?: string | null; } export async function getProjectIssues( projectId: number, openOnly = true, ): Promise { const data = await apiGet<{ issues: TaskLike[] }>( `/api/projects/${projectId}/issues?open_only=${openOnly}`, ); return data.issues; }