- 상세 화면(view=project) — 머리 · [⋯] · [작업 열기 — 지금 단계 ›] · 단계 진행(누름 = 그 단계) · 노선 지도(배경 타일 + 선 · 시점 · 종점) · 핵심 숫자 · 기본 정보 · 연결된 것 · 산출물 · 최근 변경(있는 칸만) - 목록 · 상세 숫자 · 썸네일 = 547-1 카드 요약(project_summaries) 한 벌 - 색 = 셸 변수(--b01-*) 이름에 맞춤 - 시험 test_548_project_overview — 칸반 나눔 · 산출물 · 내 것 · 단계 수 짝 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
85 lines
2.7 KiB
TypeScript
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)}`);
|
|
}
|