Files
Aislo/B05_Profile/B05_Profile_UI_RouteEdit_Apply.ts
T

82 lines
3.4 KiB
TypeScript

/* =============================================================================
* B05_Profile_UI_RouteEdit_Apply.ts
* 계획노선 편집 모달의 **[확인]·[예상노선으로]** — 무거운 재계산과 대기 표시.
*
* 누르면 서버가 배수유역부터 종·횡단·유토곡선까지 전 단계를 다시 돈다(약 90초). 중간 취소는
* 만들지 않기로 했으므로(계획서 0-2, 2026-09-09) **얼마나 지났는지**를 초로 보여 사람이
* 멈춘 것인지 도는 것인지 알 수 있게 한다.
* ========================================================================== */
import { clearDrafts, clearResults } from "../A00_Common/b_page_state";
import { showToast } from "@ui/ui_template_elements";
import { replanRoute, resetRoutePlan } from "./B05_Profile_Api_Replan";
type Vertex = [number, number];
export interface RouteApplyParams {
overlay: HTMLElement;
/** 화면 전체를 덮는 대기 막. 안에 `<span>` 한 개가 글을 받는다. */
busy: HTMLElement;
projectId: string;
/** 지금 편집값 — 누른 순간에 읽는다. */
nodes: () => { planned: Vertex[]; curveOn: boolean[]; curveRadius: Array<number | null> };
/** 성공하면 모달을 닫고 화면을 다시 읽는다. */
close: () => void;
onApplied: () => void | Promise<void>;
}
/** [확인]·[예상노선으로]를 붙인다. 리스너는 모달과 수명이 같다. */
export function bindRouteApply(params: RouteApplyParams): void {
const { overlay, busy, projectId } = params;
async function runHeavy(label: string, task: () => Promise<unknown>): Promise<void> {
busy.hidden = false;
// ⚠ 「몇 분」은 옛 값이었다 — 0-11 로 **약 90초**가 됐다(2026-09-09 실측 네 번:
// 87.3 · 90.0 · 93.9 · 95.4초).
const message = busy.querySelector("span")!;
const started = Date.now();
const tick = (): void => {
const seconds = Math.round((Date.now() - started) / 1000);
message.textContent = `${label} — 배수유역부터 다시 계산 중입니다. 1분 반쯤 걸립니다 (${seconds}초 지남).`;
};
tick();
const timer = window.setInterval(tick, 1000);
try {
await task();
// 노선이 바뀌면 세션 초안·조회 캐시는 옛 노선 것이라 남기지 않는다(PLAN 0-7 확정 5).
clearDrafts(projectId);
clearResults(projectId);
showToast("노선을 다시 계산했습니다.", "success");
params.close();
await params.onApplied();
} catch (error) {
busy.hidden = true;
showToast(error instanceof Error ? error.message : "노선 재계산에 실패했습니다.", "error");
} finally {
window.clearInterval(timer); // 성공·실패·닫힘 어느 쪽이든 멈춘다
}
}
overlay.querySelector('[data-act="apply"]')!.addEventListener("click", () => {
const { planned, curveOn, curveRadius } = params.nodes();
if (planned.length < 2) {
showToast("노선은 노드가 2개 이상이어야 합니다.", "error");
return;
}
void runHeavy("계획노선 반영", () =>
replanRoute(
projectId,
planned.map(([x, y], index) => ({
x,
y,
curve: curveOn[index] !== false,
radius_m: curveRadius[index] ?? null,
})),
),
);
});
overlay.querySelector('[data-act="reset"]')!.addEventListener("click", () => {
void runHeavy("예상노선으로 되돌리기", () => resetRoutePlan(projectId));
});
}