/* ============================================================================= * A00_Common/design/design_save_api.ts * [저장] · [확정] 한 본문(PLAN 472-6 6-A) — 차례 조각 꼴 · 서버 한 창구 `POST /design/save`. * * 차례마다 조각 하나 = 본문(없으면 null · 서버가 그 차례를 건너뜀) + 저장 뒤 정리(초안 비움 · 화면 맞춤). * 서버는 전부 저장하거나 아무것도 안 바꿈 — 실패면 차례 이름 · 까닭(`restored` 거짓 = 복구 확인 필요). * ========================================================================== */ import { API_ANALYSIS_TIMEOUT_MS, API_BASE_URL } from "@config/config_frontend"; /** 서버 본문 칸 차례 그대로(`B06_Section_Router_DesignSave`). */ export const SAVE_PART_KEYS = [ "settings", "pipes", "structures", "plusStations", "alignment", "culvertOptions", "uphill", "cross", ] as const; export type SavePartKey = (typeof SAVE_PART_KEYS)[number]; /** 차례 조각 — `after` 는 서버가 다 저장한 뒤에만(그 차례 응답을 받음). */ export interface SavePart { body: unknown | null; after?: (response: Record | undefined) => Promise | void; } const BODY_KEY: Record = { settings: "settings", pipes: "pipes", structures: "structures", plusStations: "plus_stations", alignment: "alignment", culvertOptions: "culvert_options", uphill: "uphill", cross: "cross", }; export interface DesignSaveResult { waited: boolean; steps: Partial>>; } /** 실패 — 차례 이름(`step`) · 되돌림 됨(`restored`). */ export class DesignSaveError extends Error { constructor( message: string, readonly step: SavePartKey | null, readonly restored: boolean, ) { super(message); } } /** 조각 본문을 모아 한 번에 보냄. */ export async function postDesignSave( projectId: string, routeId: number, confirm: boolean, parts: Partial>, ): Promise { const body: Record = { route_id: routeId, confirm }; for (const key of SAVE_PART_KEYS) { const part = parts[key]; if (part && part.body !== null && part.body !== undefined) body[BODY_KEY[key]] = part.body; } const controller = new AbortController(); // [확정]은 낡은 배수유역을 다시 셈 — 옛 차례들의 시간 한도 합 만큼 const timer = window.setTimeout(() => controller.abort(), API_ANALYSIS_TIMEOUT_MS * 3); try { const response = await fetch(`${API_BASE_URL}/projects/${projectId}/design/save`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), signal: controller.signal, }); const payload = await response.json().catch(() => ({})); if (!response.ok) throw new DesignSaveError( payload?.message ?? `HTTP ${response.status}`, (payload?.step as SavePartKey) ?? null, payload?.restored !== false, ); return { waited: payload?.waited === true, steps: payload?.steps ?? {} }; } catch (error) { if (error instanceof DOMException && error.name === "AbortError") throw new DesignSaveError( "저장 응답을 시간 안에 못 받았습니다 — 새로고침해 저장됐는지 확인해 주세요.", null, false, ); throw error; } finally { window.clearTimeout(timer); } }