- 계산 묶음 drainage 단계(pipelineStage · 붙이기 = 묶음 쪽) — 노선 초안 · 관이 바뀌면 낡음 · 자동 = 3 s 조용한 뒤 「지금 노선으로 셈」 · 수동 = 칸만 흐림 + 가운데 [업데이트] · 구석 「계산 중」 · 다시 조작하면 요청 끊고 처음으로 · 덮개 없음 - ⚙ 설정 창(배수유역 칸 머리 · openModal) 수동(기본) / 자동 · 계정 화면 설정 drainage-mode(b_page_state 등록표 한 줄 · build:corridor 다시 지음) - 늦은 답 버림 = 차례 번호 · 서버 superseded · 세부유역 요청 감싸기와 같은 차례(B05_Profile_UI_Drainage_Run 으로 나눔) - 제안 = 관경(Ø→Ø) · 시설(BOX암거 · 세월교) · 관 자리 후보(지도 점선 원) · [적용] 한 번에 관 초안 · 자동 바꿈 없음 - [확정] savePipes(confirm) = PUT ?recompute=true · [저장] = 낡음 달고 저장 · 패널 691줄 - 순수 규칙 logic/B05_Profile_Drainage_Live · _Suggest · 시험 test_347_2_drainage_live_ui · typecheck 통과 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWh5wNNycMkbwMpACz1rte
73 lines
3.0 KiB
TypeScript
73 lines
3.0 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile_UI_Drainage_Run.ts
|
|
* 세부유역 요청 한 번 감싸기 — 버튼 잠금 · 진행 문구 · 오류 표기 · 최신 응답만 채택(차례 번호).
|
|
*
|
|
* 패널 본체(B05_Profile_UI_Drainage_Panel)가 700줄이라 나눔(347-2). 「지금 노선으로 셈」 답도
|
|
* 같은 차례 번호를 타서, 늦게 온 세부유역 답이 새 답을 덮지 않음 — `quiet` 이면 버튼 · 가운데 진행
|
|
* 원 · 상태 글을 안 건드림(칸 구석 「계산 중」은 부르는 쪽 · 조작을 막지 않음).
|
|
* ========================================================================== */
|
|
|
|
import type { DetailBasinResponse } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
|
|
import { analyzeFailed } from "./B05_Profile_UI_Drainage_Parts";
|
|
|
|
type StatusKey =
|
|
| "B05_Drainage_Status_Analyzing"
|
|
| "B05_Drainage_Status_NoBasin"
|
|
| "B05_Drainage_Status_AnalyzeFailed";
|
|
|
|
export interface DetailRunnerDeps {
|
|
projectId: () => string | null;
|
|
analyzeButton: HTMLButtonElement;
|
|
status: HTMLElement;
|
|
showProgress: (ratio: number | null, label: string | null) => void;
|
|
apply: (response: DetailBasinResponse) => void;
|
|
basinCount: () => number;
|
|
scheduleDraw: () => void;
|
|
label: (key: StatusKey) => string;
|
|
}
|
|
|
|
export interface DetailRunner {
|
|
projectId: () => string | null;
|
|
/** 요청 하나 — 답을 화면에 올렸으면 true(뒤에 보낸 요청이 있으면 false). */
|
|
run(request: () => Promise<DetailBasinResponse>, quiet?: boolean): Promise<boolean>;
|
|
}
|
|
|
|
export function createDetailRunner(deps: DetailRunnerDeps): DetailRunner {
|
|
const { analyzeButton, status, showProgress, label } = deps;
|
|
let requestSeq = 0; // 세부유역 요청 — 최신 응답만 채택
|
|
return {
|
|
projectId: deps.projectId,
|
|
async run(request, quiet = false) {
|
|
if (!deps.projectId()) return false;
|
|
if (!quiet) {
|
|
analyzeButton.disabled = true;
|
|
status.hidden = false;
|
|
status.textContent = label("B05_Drainage_Status_Analyzing");
|
|
showProgress(null, label("B05_Drainage_Status_Analyzing"));
|
|
}
|
|
const seq = ++requestSeq;
|
|
try {
|
|
const response = await request();
|
|
if (seq !== requestSeq) return false; // 뒤에 보낸 요청이 있음 — 그 응답을 기다림
|
|
deps.apply(response);
|
|
status.hidden = deps.basinCount() > 0;
|
|
if (!deps.basinCount())
|
|
status.textContent = response.stale ?? label("B05_Drainage_Status_NoBasin");
|
|
deps.scheduleDraw();
|
|
return true;
|
|
} catch (error) {
|
|
if (seq !== requestSeq) return false;
|
|
if (error instanceof DOMException && error.name === "AbortError") return false;
|
|
status.hidden = false;
|
|
status.textContent = analyzeFailed(error, label("B05_Drainage_Status_AnalyzeFailed"));
|
|
return false;
|
|
} finally {
|
|
if (seq === requestSeq) {
|
|
analyzeButton.disabled = false;
|
|
showProgress(null, null);
|
|
}
|
|
}
|
|
},
|
|
};
|
|
}
|