/* B07 상세 설계 도면 목록·단건 API 클라이언트. */ import { API_BASE_URL, API_TIMEOUT_MS } from "@config/config_frontend"; export interface DesignDrawingItem { id: string; // blank: 아직 내용을 만들지 않은 도면 — 도각만 실려 온다. kind: "cover" | "longitudinal" | "cross" | "mass_haul" | "watershed" | "blank"; label: string; chainage_m: number | null; confirmed: boolean; } export interface CadDrawing { entities: Record[]; /** 횡단도 측점별 실좌표(m) → 종이(mm) 변환값. 구조물을 같은 자리에 얹는 데 쓴다. */ cross_placements?: { chainage_m: number; ox: number; oy: number; dy: number; mm_per_m: number; x0: number; x1: number; }[]; layers: { id: string; name: string; isVisible: boolean; isLocked: boolean; }[]; } export interface DesignDrawingListResponse { status: string; project_id: string; route_id: number; drawings: DesignDrawingItem[]; } /** 수량 산출표 값 (미산정 항목은 null). 백엔드 `_quantity_table`의 키와 대응. */ export type QuantityTable = Record; /** 측구 형식별 규격 (B06 엔진 ditch_spec 신구조와 1:1). */ export type DitchSpec = | { type: "none" } | { type: "standard"; top_width_m: number; bottom_width_m: number; depth_m: number; } | { type: "l_type"; width_m: number; depth_m: number }; /** B06에서 지정한 설계(지반정보·계획정보). 횡단도에만 존재. status로 잠정/확정 구분. */ export interface CrossDesignInfo { ground_type: "soil" | "ripping_rock" | "blasting_rock"; geometry_preset: "soil" | "rock"; section_mode: "left_cut" | "right_cut" | "both_cut" | "both_fill"; ditch_side: "left" | "right"; ditch_type?: "standard" | "l_type" | null; ditch_enabled?: boolean; cut_slope_ratio: number; fill_slope_ratio: number; roadbed_width_m: number; carriageway_width_m?: number; cross_slope_pct?: number; paved?: boolean; ditch: DitchSpec; road_edges?: Record<"left" | "right", { offset_m: number; elevation_m: number }>; design_elevation_m: number; cut_area_m2: number; fill_area_m2: number; status?: "provisional" | "confirmed"; } export interface DesignDrawingResponse { status: string; project_id: string; route_id: number; id: string; // blank: 아직 내용을 만들지 않은 도면 — 도각만 실려 온다. kind: "cover" | "longitudinal" | "cross" | "mass_haul" | "watershed" | "blank"; label: string; drawing: CadDrawing; confirmed: boolean; quantity_table?: QuantityTable | null; design?: CrossDesignInfo | null; } export interface DesignDrawingConfirmResponse { status: string; project_id: string; id: string; confirmed: boolean; all_confirmed: boolean; design?: CrossDesignInfo | null; } async function requestJson(path: string, init: RequestInit = {}): Promise { const controller = new AbortController(); const timeoutId = window.setTimeout(() => controller.abort(), API_TIMEOUT_MS); try { const response = await fetch(`${API_BASE_URL}${path}`, { ...init, credentials: "include", headers: { "Content-Type": "application/json" }, signal: controller.signal, }); const payload = (await response.json()) as T & { message?: string }; if (!response.ok) throw new Error(payload.message ?? `HTTP ${response.status}`); return payload; } finally { window.clearTimeout(timeoutId); } } export function fetchDesignDrawingList(projectId: string): Promise { return requestJson(`/projects/${projectId}/design-drawings`); } export function fetchDesignDrawing( projectId: string, drawingId: string, ): Promise { return requestJson(`/projects/${projectId}/design-drawings/${encodeURIComponent(drawingId)}`); } export function confirmDesignDrawing( projectId: string, drawingId: string, drawing: CadDrawing, quantityTable?: QuantityTable | null, ): Promise { return requestJson( `/projects/${projectId}/design-drawings/${encodeURIComponent(drawingId)}/confirm`, { method: "PUT", body: JSON.stringify({ drawing, quantity_table: quantityTable ?? null }), }, ); } export function invalidateDesignDrawing(projectId: string, drawingId: string): Promise { return requestJson( `/projects/${projectId}/design-drawings/${encodeURIComponent(drawingId)}/invalidate`, { method: "POST" }, ); } /** 도각 편집 화면이 싣는 도각 한 장 (실치수 1:1, 잠금 없는 도면층). */ export interface FrameTemplateResponse { status: string; project_id: string; drawing: CadDrawing; /** 회사가 고친 도각을 쓰고 있으면 true, 프로그램 기본 도각이면 false. */ customized: boolean; } export function fetchFrameTemplate(projectId: string): Promise { return requestJson(`/projects/${projectId}/frame-template`); } export function saveFrameTemplate(projectId: string, drawing: CadDrawing): Promise { return requestJson(`/projects/${projectId}/frame-template`, { method: "PUT", body: JSON.stringify({ drawing }), }); } /** 회사 도각을 지우고 프로그램 기본 도각으로 되돌린다. */ export function resetFrameTemplate(projectId: string): Promise { return requestJson(`/projects/${projectId}/frame-template`, { method: "DELETE", }); }