feat(B05 배수유역): 369-2 [자동 제안] 두 방식 · [배관 배치 초기화] = 세류선만 초안 · 제안 [적용] = 관경 · 시설만
- 배수유역 머리 [자동 제안] → 고름 창(⚙ 창 틀) ① 전체 자동(all) ② 지정 포함 추가(add) · 낡은 동안 끔 - [배관 배치 초기화] — 저장분 즉시 지움 없앰 · 관 목록 없이 셈(세류선 교차만) · 초안 - 셋 다 답이 오면 세션 초안 + 사진첩 한 걸음(400 ms 넘은 답도 제 걸음) - 제안 목록 [적용] — 관 자리 후보는 안 더함(점선 원 · 목록 그대로) · 관경 · 시설 제안 없으면 꺼짐 - computeDetailBasins(points | null, auto) · 글 = 툴팁 두 값 고침 · 새 글 끝에 새 키 - 시험 — 349 흐름에 자동 제안 · 347_2 · 298 · 312 고쳐 씀 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC34fsxxbweEG3RUjs3Mn
This commit is contained in:
@@ -86,7 +86,9 @@ const flush = async () => {
|
||||
/* ---- 모듈 읽기(그 자리 트랜스파일 · 바깥 모듈은 가짜) ---- */
|
||||
const state = new Map();
|
||||
const detailCalls = [];
|
||||
let modeRow = 1; // ⚙ 창에서 고를 줄 — 0 수동 · 1 자동
|
||||
const detailAutos = []; // [자동 제안](369) 방식 — 없으면 null
|
||||
let records = 0; // 사진첩 한 걸음(369)
|
||||
let modeRow = 1; // ⚙ · [자동 제안] 창에서 고를 줄 — ⚙ 0 수동 · 1 자동 / 제안 0 전체 · 1 지정 포함
|
||||
const stubs = {
|
||||
"@config/config_frontend": { API_BASE_URL: "/api" },
|
||||
"@ui/ui_template_progress": { createProgressCircle: () => ({ root: el("progress") }) },
|
||||
@@ -98,14 +100,20 @@ const stubs = {
|
||||
body.children[modeRow].children[0].fire("change");
|
||||
},
|
||||
},
|
||||
"@ui/ui_template_locale": {
|
||||
currentLanguageIndex: 0,
|
||||
ui_locales: new Proxy({}, { get: (_target, key) => [key, key] }),
|
||||
},
|
||||
"./B05_Profile_UI_Profile_History": { pageHistory: () => ({ record: () => (records += 1) }) },
|
||||
"../A00_Common/b_page_state": {
|
||||
readStateRaw: (key) => state.get(key) ?? null,
|
||||
writeStateRaw: (key, value) => state.set(key, value),
|
||||
},
|
||||
"../B04_PreProcess/B04_PreProcess_Api_Fetch": {
|
||||
computeDetailBasins: async (projectId, points) => {
|
||||
computeDetailBasins: async (projectId, points, auto) => {
|
||||
detailCalls.push(points);
|
||||
return { basins: [], pipe_points: points, stale: null };
|
||||
detailAutos.push(auto ?? null);
|
||||
return { basins: [], pipe_points: points ?? [], stale: null };
|
||||
},
|
||||
},
|
||||
"./B05_Profile_UI_Drainage_Parts": { analyzeFailed: (_error, fallback) => fallback },
|
||||
@@ -135,6 +143,8 @@ async function main() {
|
||||
const viewport = el("viewport");
|
||||
const header = el("header");
|
||||
const root = el("root");
|
||||
const propose = el("button");
|
||||
header.querySelector = (selector) => (selector === ".b05-drainage__propose" ? propose : null);
|
||||
root.querySelector = (selector) =>
|
||||
selector === ".b05-drainage__viewport"
|
||||
? viewport
|
||||
@@ -280,7 +290,12 @@ async function main() {
|
||||
out.autoSavedDone = look();
|
||||
|
||||
// 349-0i 되돌리기 — 자동: 끌기 → 낡음 → Ctrl+Z(초안 없어짐) = 3 s 셈 대신 저장분
|
||||
const savedOk = { basins: [], pipe_points: pipes, stale: null };
|
||||
// 저장분 답에도 관경 제안 하나 — [적용] 켜짐은 낡음 · 관경/시설 제안 유무로 갈림(369)
|
||||
const savedOk = {
|
||||
basins: [{ chainage_m: 100, recommended_facility: "pipe", recommended_diameter_mm: 1200 }],
|
||||
pipe_points: pipes,
|
||||
stale: null,
|
||||
};
|
||||
stage(draft(20, 300));
|
||||
advance(1000);
|
||||
stage(null);
|
||||
@@ -308,6 +323,31 @@ async function main() {
|
||||
savedAnswer({ basins: [], pipe_points: pipes, stale: "배수유역 낡음" });
|
||||
await flush();
|
||||
out.undoSavedStale = { ...look(), savedLoads, mode: state.get("drainage-mode") };
|
||||
// 369 [자동 제안] — 낡은 동안 끔 · ① all · ② add · [배관 배치 초기화] = 관 목록 없이(세류선만)
|
||||
out.proposeStale = { off: propose.disabled, title: propose.title };
|
||||
const beforeStale = detailCalls.length;
|
||||
propose.fire("click");
|
||||
await flush();
|
||||
out.proposeStale.calls = detailCalls.length - beforeStale;
|
||||
live.markFresh();
|
||||
out.proposeFresh = { off: propose.disabled, title: propose.title };
|
||||
const n0 = detailCalls.length;
|
||||
const remembered0 = remembered;
|
||||
const records0 = records;
|
||||
modeRow = 0;
|
||||
propose.fire("click");
|
||||
await flush();
|
||||
modeRow = 1;
|
||||
propose.fire("click");
|
||||
await flush();
|
||||
live.resetToStreams();
|
||||
await flush();
|
||||
out.propose = {
|
||||
autos: detailAutos.slice(n0),
|
||||
points: detailCalls.slice(n0).map((points) => (points === null ? null : points.length)),
|
||||
remembered: remembered - remembered0,
|
||||
records: records - records0,
|
||||
};
|
||||
// 입력 지문 — 판 번호 · 관 차례는 안 봄 · 꼭짓점 · 관 자리(cm) · id 는 봄
|
||||
const { inputKey } = load(path.join(B05, "logic", "B05_Profile_Drainage_Live.ts"));
|
||||
const a = { id: "a1", chainage_m: 100 };
|
||||
|
||||
@@ -104,7 +104,7 @@ def test_B06_초안_쓰면_판이_한_번_다시_나눔():
|
||||
|
||||
|
||||
def test_다시_나눔은_세션_초안으로_계산만_저장_안_함():
|
||||
load = _body(PANEL, "const loadSaved = ", "const resetPipes")
|
||||
load = _body(PANEL, "const loadSaved = ", "const analyze = ")
|
||||
assert "readPendingPipes(projectId as string)" in load
|
||||
assert "computeDetailBasins(projectId as string, pending)" in load
|
||||
analyze = _body(PANEL, "const analyze = ", "analyzeButton.addEventListener")
|
||||
|
||||
@@ -161,7 +161,7 @@ def _body(rel: str, start: str, end: str) -> str:
|
||||
|
||||
def test_화면_초안_빈_목록은_저장분으로_안_넘어감() -> None:
|
||||
load = _body(
|
||||
"B05_Profile/B05_Profile_UI_Drainage_Panel.ts", "const loadSaved = ", "const resetPipes"
|
||||
"B05_Profile/B05_Profile_UI_Drainage_Panel.ts", "const loadSaved = ", "const analyze = "
|
||||
)
|
||||
assert "pending !== null" in load and "pending?.length" not in load
|
||||
# B06 목록도 같은 규칙(`??`)
|
||||
|
||||
@@ -51,8 +51,7 @@ def test_suggestions_collected(r):
|
||||
def test_apply_once_without_touching_input(r):
|
||||
assert r["untouched"]
|
||||
applied = {pipe["chainage_m"]: pipe for pipe in r["applied"]}
|
||||
assert sorted(applied) == [100, 200, 300, 500]
|
||||
assert sorted(applied) == [100, 300, 500] # 관 자리 후보(200)는 안 더함 — [자동 제안](369)
|
||||
assert applied[100]["options"]["pipe_diameter_mm"] == 1200
|
||||
assert applied[200]["source"] == "spacing"
|
||||
assert applied[300]["facility"] == "ford_bridge"
|
||||
assert applied[500]["facility"] == "box_culvert" # 이미 그 종류 — 그대로
|
||||
|
||||
@@ -64,10 +64,25 @@ def test_suggestions_and_apply(r):
|
||||
assert len(r["suggest"]["rows"]) == 2
|
||||
points = {p["chainage_m"]: p for p in r["applied"]["points"]}
|
||||
assert points[100]["options"]["pipe_diameter_mm"] == 1200
|
||||
assert points[200]["source"] == "spacing"
|
||||
assert 200 not in points # 관 자리 후보는 [적용]이 안 더함 — [자동 제안] 몫(369)
|
||||
assert r["applied"]["remembered"] == 1
|
||||
|
||||
|
||||
def test_자동_제안_두_방식_초기화_세류선만_한_걸음(r):
|
||||
assert r["proposeStale"] == {
|
||||
"off": True,
|
||||
"title": "B05_Drainage_Btn_Propose_Stale",
|
||||
"calls": 0, # 낡은 동안 끔 — 눌러도 요청 없음
|
||||
}
|
||||
assert r["proposeFresh"] == {"off": False, "title": "B05_Drainage_Btn_Propose_Tip"}
|
||||
assert r["propose"] == {
|
||||
"autos": ["all", "add", None],
|
||||
"points": [1, 1, None], # ① ② = 지금 관 목록 함께 · 초기화 = 목록 없이(세류선만)
|
||||
"remembered": 3, # 셋 다 초안
|
||||
"records": 3, # 답마다 사진첩 한 걸음
|
||||
}
|
||||
|
||||
|
||||
def test_move_while_busy_cancels(r):
|
||||
moved = r["movedWhileBusy"]
|
||||
assert moved["aborted"] and moved["veil"] and not moved["busy"]
|
||||
|
||||
Reference in New Issue
Block a user