From b95ad5e838f528de9973c90f83ae5adc687cf993 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 16:53:25 +0900 Subject: [PATCH] =?UTF-8?q?feat(b08):=20=EB=A9=B4=EA=B3=A0=EB=A5=B4?= =?UTF-8?q?=EA=B8=B0=20=EC=84=B1=ED=86=A0=EB=A9=B4=20=ED=9A=8C=EC=83=89=20?= =?UTF-8?q?=EC=A0=9C=EC=95=88=20+=20[=EC=A0=9C=EC=95=88=EA=B0=92=20?= =?UTF-8?q?=EB=84=A3=EA=B8=B0]=20=E2=80=94=20=EC=84=9C=EB=B2=84=20fill=5Fs?= =?UTF-8?q?uggested=20=EA=B7=B8=EB=8C=80=EB=A1=9C=20=C2=B7=20=EB=88=84?= =?UTF-8?q?=EB=A5=B8=20=EB=95=8C=EB=A7=8C=20=EC=B9=B8=EC=97=90=20=EB=93=A4?= =?UTF-8?q?=EC=96=B4=EA=B0=80=EA=B3=A0=20=EB=B9=84=EC=9B=8C=20=EB=91=90?= =?UTF-8?q?=EB=A9=B4=20=E3=80=8C=EC=95=88=20=EC=A0=95=ED=95=A8=E3=80=8D=20?= =?UTF-8?q?=C2=B7=20=EB=A9=94=EC=9D=B8=20976e5fd9=20=EC=A7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 화면: 칸 빈 값 · 회색 「제안(비우면 안 정함): 성토면 · 기계 · 무한궤도 — 영월 실무…」 · 단추 누르면 칸만 바뀌고 저장값은 그대로 빈 값(저장 안 함) Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq --- .../B08_Quantity_UI_Side_FaceDressing.ts | 37 +++++++++++++++++-- .../test_b08_face_dressing_fields_ui.py | 10 +++++ ui_template/ui_template_locale_b2.ts | 2 + 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/B08_Quantity/B08_Quantity_UI_Side_FaceDressing.ts b/B08_Quantity/B08_Quantity_UI_Side_FaceDressing.ts index dfed952a..68487552 100644 --- a/B08_Quantity/B08_Quantity_UI_Side_FaceDressing.ts +++ b/B08_Quantity/B08_Quantity_UI_Side_FaceDressing.ts @@ -5,11 +5,13 @@ * * 페이지 본문(`B08_Quantity_UI_Page.ts`)이 700줄을 넘어 새 칸을 이 파일로 뺌(CLAUDE.md 4장). * ⚠ 선택지는 **서버가 내려준 목록 그대로**(`face_dressing_choices`) — 화면에 다시 적지 않음(두 벌 금지). - * ⚠ 제안값 없음(판정 Ⓒ) — 첫 보기가 「안 정함」이고 비면 내역 줄이 입력 사유로 섬. + * ⚠ 스스로 안 고름(판정 Ⓒ) — 첫 보기가 「안 정함」이고 비면 내역 줄이 입력 사유로 섬. 성토면 제안은 + * 회색 근거 + [제안값 넣기]를 **누른 때만**(메인 976e5fd9 `fill_suggested`). * ⚠ 면적은 화면이 셈하지 않음 — 비우면 서버가 파종 면적(초류종자살포 × 반영률)을 씀. * ========================================================================== */ import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale"; +import { createButton } from "@ui/ui_template_elements"; import type { SideFieldHelpers } from "./B08_Quantity_UI_Side_TreeWaste"; function L(key: keyof typeof ui_locales): string { @@ -29,6 +31,8 @@ export interface FaceDressingDraft { export interface FaceDressingChoices { cut: string[]; fill: string[]; + /** 성토면 제안(서버 한 곳 · 실무 관측) — 회색으로 보이고 [제안값 넣기]를 **누른 때만** 들어감. */ + fill_suggested?: { value: string; basis: string }; } export function appendFaceDressingFields( @@ -76,11 +80,36 @@ export function appendFaceDressingFields( draft.face_dressing_cut_class = v; }), ); - panel.append( - pick("B08_Quantity_FaceDressing_Fill", draft.face_dressing_fill_class, choices.fill, (v) => { + const fillRow = pick( + "B08_Quantity_FaceDressing_Fill", + draft.face_dressing_fill_class, + choices.fill, + (v) => { draft.face_dressing_fill_class = v; - }), + }, ); + panel.append(fillRow); + // 성토면 제안 — 칸은 비워 둔 채 회색 근거 + 단추(층따기와 같은 모양). 비우면 「안 정함」 그대로. + const suggested = choices.fill_suggested; + if (suggested?.value) { + panel.append( + h.hintRow( + `${L("B08_Quantity_FaceDressing_Suggest")} ${suggested.value} — ${suggested.basis}`, + ), + ); + panel.append( + createButton({ + label: L("B08_Quantity_FaceDressing_FillSuggested"), + variant: "ghost", + onClick: () => { + const select = fillRow.querySelector("select"); + if (select) select.value = suggested.value; + draft.face_dressing_fill_class = suggested.value; + draft.dirty = true; + }, + }), + ); + } panel.append( area("B08_Quantity_FaceDressing_FillArea", draft.face_dressing_fill_area_m2, (v) => { draft.face_dressing_fill_area_m2 = v; diff --git a/resources/tester/test_b08_face_dressing_fields_ui.py b/resources/tester/test_b08_face_dressing_fields_ui.py index 9a7e8e71..691fd480 100644 --- a/resources/tester/test_b08_face_dressing_fields_ui.py +++ b/resources/tester/test_b08_face_dressing_fields_ui.py @@ -24,6 +24,16 @@ def test_저장에_네_키를_싣고_불러온다() -> None: assert f"{key}: (stored.{key}" in PAGE, f"불러오기에 {key} 없음" +def test_성토면_제안은_회색_근거와_단추로만_넣는다() -> None: + """메인 976e5fd9 — `fill_suggested` {value, basis}. 비워 두면 「안 정함」 그대로(스스로 안 고름).""" + side = SIDE.read_text(encoding="utf-8") + assert "choices.fill_suggested" in side and "suggested.basis" in side + button = side.split("createButton(")[1] + assert "draft.face_dressing_fill_class = suggested.value" in button # 누른 때만 넣음 + assert side.count("= suggested.value") == 2 # 단추 안의 칸·초안 둘뿐 + assert "BenchCut" not in side # 층따기 문구(「KDS 토사 0.5 m」)를 빌려 쓰지 않음 + + def test_선택지는_서버_목록_그대로() -> None: side = SIDE.read_text(encoding="utf-8") assert "appendFaceDressingFields(" in PAGE and "face_dressing_choices" in PAGE diff --git a/ui_template/ui_template_locale_b2.ts b/ui_template/ui_template_locale_b2.ts index 69cd4fb0..c76794e1 100644 --- a/ui_template/ui_template_locale_b2.ts +++ b/ui_template/ui_template_locale_b2.ts @@ -753,6 +753,8 @@ export const ui_locales_b2 = { B08_Quantity_FaceDressing_FillArea: ["성토면 면적(㎡)", "Fill face area (㎡)"], B08_Quantity_FaceDressing_CutArea: ["절토면 면적(㎡)", "Cut face area (㎡)"], B08_Quantity_FaceDressing_Area_Placeholder: ["파종 면적 그대로", "Same as seeding area"], + B08_Quantity_FaceDressing_Suggest: ["제안(비우면 안 정함):", "Suggested (blank = not set):"], + B08_Quantity_FaceDressing_FillSuggested: ["제안값 넣기", "Fill suggested"], B08_Quantity_Side_FaceDressing_Hint: [ "품셈 9-19-1 원문 표 두 벌(절토면 토질 6 · 성토면 시공·토질) — 제안값 없음, 안 고르면 내역 줄이 입력 사유로 섭니다. 면적은 비우면 초류종자살포 면적(× 반영률) 그대로 · 넣으면 그 값으로 덮어씀", "Standard estimate 9-19-1 tables (cut face soil · fill face method/soil) — no suggested value; unset leaves the bill row with an input reason. Area left blank uses the seeding area (× ratio); a value overrides it",