Files
Aislo/B01_Dashboard/B01_Dashboard_Api_Projects.ts
T
eomsangdonandClaude Opus 5.5 070791854c feat(B01): 프로젝트 상세 화면 · 카드 요약 연결 · 지킴 시험 (PLAN 548-2)
- 상세 화면(view=project) — 머리 · [⋯] · [작업 열기 — 지금 단계 ›] · 단계 진행(누름 = 그 단계) ·
  노선 지도(배경 타일 + 선 · 시점 · 종점) · 핵심 숫자 · 기본 정보 · 연결된 것 · 산출물 · 최근 변경(있는 칸만)
- 목록 · 상세 숫자 · 썸네일 = 547-1 카드 요약(project_summaries) 한 벌
- 색 = 셸 변수(--b01-*) 이름에 맞춤
- 시험 test_548_project_overview — 칸반 나눔 · 산출물 · 내 것 · 단계 수 짝

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 18:08:18 +09:00

85 lines
2.7 KiB
TypeScript

/* =============================================================================
* B01_Dashboard_Api_Projects.ts
* 프로젝트 목록 · 상세 API(요구 369~374 · PLAN 548) — 서버 `B01_Dashboard_Router_Overview.py`.
* ========================================================================== */
import { API_BASE_URL } from "@config/config_frontend";
import type { ProjectItem } from "./B01_Dashboard_Api_Fetch";
/** 칸반 칸 — 서버 `board_column` 과 같은 열쇠 */
export type BoardColumn = "start" | "design" | "review" | "confirm" | "done";
export interface OverviewProject extends ProjectItem {
user_id?: number | null;
project_year_end?: number | null;
design_speed_kph?: number | null;
created_at?: string | null;
manager_name?: string | null;
source_project_id?: string | null;
stage_total: number;
board: BoardColumn;
idle_days: number | null;
stalled: boolean;
/** 썸네일 선(0~1 칸 · y 아래로) — 547-1 카드 요약 */
thumb?: number[][] | null;
metrics?: { length_m?: number; stations?: number; structures?: number };
}
/** 지도 선 — 위경도 [lat, lon] */
export interface RouteLine {
points: [number, number][];
}
export interface ProjectLink {
kind: "source" | "derived" | "plan";
project_id: string;
name: string;
project_kind?: string | null;
note?: string | null;
}
export interface ProjectOutput {
key: string;
label: string;
url: string;
}
export interface ProjectChange {
at: string;
who?: string | null;
action: string;
stage_no?: number;
}
export interface ProjectDetail {
project: OverviewProject;
people: { pm?: string | null; field_lead?: string | null; designer?: string | null };
route: RouteLine | null;
numbers: { key: string; value: number; unit?: string }[];
links: ProjectLink[];
outputs: ProjectOutput[];
changes: ProjectChange[];
}
async function getJson<T>(path: string): Promise<T> {
const response = await fetch(`${API_BASE_URL}${path}`, { credentials: "include" });
const data = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(data.detail || `요청 실패: ${response.status}`);
return data as T;
}
export async function fetchOverviewProjects(
scope: "mine" | "company",
companyId?: number | null,
): Promise<OverviewProject[]> {
const company = companyId ? `&company_id=${companyId}` : "";
const data = await getJson<{ projects: OverviewProject[] }>(
`/dashboard/overview/projects?scope=${scope}${company}`,
);
return data.projects ?? [];
}
export function fetchProjectDetail(projectId: string): Promise<ProjectDetail> {
return getJson<ProjectDetail>(`/dashboard/overview/projects/${encodeURIComponent(projectId)}`);
}