From f1e15a7711994cb5fe07f27091e34cbf3cb199d8 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sat, 17 Oct 2026 00:29:31 +0900 Subject: [PATCH] =?UTF-8?q?feat(B06=20=ED=91=9C=EC=A4=80=20=ED=9A=A1?= =?UTF-8?q?=EB=8B=A8):=20364-6=20=EB=8D=A7=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EC=B9=B8=20=EB=81=9D=EC=97=90=20=EC=95=94=20=C2=B7=20=ED=8F=AC?= =?UTF-8?q?=EC=9E=A5=20=EC=8B=A4=EC=A0=9C=20=EA=B0=92=20=EB=B3=B4=EA=B8=B0?= =?UTF-8?q?=20=EC=A0=84=EC=9A=A9=20=EA=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 고치기 전 공통 칸(측구 상단폭 · 횡단경사)이 토사 값과 다른 그룹 값을 라벨 끝 작은 글로(「암 0.69」 · 「암 3 %」) - 칸을 고치면 한 벌로 펼쳐져 글 사라짐 · 글자색 --color-text-muted · 줄 수 그대로 - 로케일 새 키 둘(끝에 더함) · 시험 test_364_6_std_panel_same_value.py 8 · 관련 17 · typecheck Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ --- B06_Section/B06_Section_UI_Standard_Panel.ts | 37 +++++++++++++++++++ B06_Section/B06_Section_UI_Style.css | 9 +++++ resources/tester/helper_364_6_entry.ts | 1 + resources/tester/helper_364_6_std_panel.mjs | 14 +++++++ .../tester/test_364_6_std_panel_same_value.py | 12 ++++++ ui_template/ui_template_locale_b2.ts | 2 + 6 files changed, 75 insertions(+) diff --git a/B06_Section/B06_Section_UI_Standard_Panel.ts b/B06_Section/B06_Section_UI_Standard_Panel.ts index 263c09f9f..6bdb37a6b 100644 --- a/B06_Section/B06_Section_UI_Standard_Panel.ts +++ b/B06_Section/B06_Section_UI_Standard_Panel.ts @@ -245,6 +245,33 @@ function spread(state: StandardCrossSection, spec: NumberFieldSpec, value: numbe } } +const GROUP_LABELS: Partial> = { + rock: "B06_Std_Note_Rock", + paved: "B06_Std_Note_Paved", +}; + +/** + * 공통 칸이 보이는 토사 값과 다른 그룹의 실제 값 — 「암 0.69」(보기 전용 · 364-6). + * 고치면 한 벌로 펼쳐져(`spread`) 빈 글이 됨. + */ +export function commonGroupNote( + state: StandardCrossSection, + label: keyof typeof ui_locales, +): string { + const spec = FIELD_SPECS.find((one) => one.scope === "common" && one.label === label); + if (!spec || !state.soil) return ""; + const shown = spec.get(state.soil); + const unit = label.startsWith("B06_Std_Field_CrossSlope") ? " %" : ""; + const notes: string[] = []; + for (const [key, name] of Object.entries(GROUP_LABELS) as [StandardCrossKey, string][]) { + const group = state[key]; + if (!group || spec.exclude === key) continue; + const value = spec.get(group); + if (value !== shown) notes.push(`${L(name as keyof typeof ui_locales)} ${value}${unit}`); + } + return notes.join(" · "); +} + /** * 표준 횡단면 설정 패널을 만든다. * @param projectId 세션 캐시 스코프. @@ -301,6 +328,13 @@ export function createStandardPanel( const buildField = (spec: NumberFieldSpec, grid: HTMLElement): void => { const source = spec.scope === "common" ? state.soil : state[spec.scope]; if (!source) return; + // 같은 줄 끝 작은 글 — 고치기 전 다른 그룹이 실제로 쓰는 값(364-6) + const note = document.createElement("span"); + note.className = "b06-std__group-note"; + const showNote = (): void => { + note.textContent = spec.scope === "common" ? commonGroupNote(state, spec.label) : ""; + note.hidden = !note.textContent; + }; const field = createInputField({ label: L(spec.label), type: "number", @@ -311,8 +345,11 @@ export function createStandardPanel( if (spec.scope === "common") spread(state, spec, parsed); else spec.set(source, parsed); persist(); + showNote(); }, }); + showNote(); + field.root.querySelector(".ui-field__label")?.append(note); field.input.step = "0.1"; field.input.min = "0"; // 경사 칸은 **각도도 함께 보인다** — 카드의 개별 절토각 칸이 도(°)로 받으므로 두 자리의 diff --git a/B06_Section/B06_Section_UI_Style.css b/B06_Section/B06_Section_UI_Style.css index 6553a39f4..81e578837 100644 --- a/B06_Section/B06_Section_UI_Style.css +++ b/B06_Section/B06_Section_UI_Style.css @@ -295,6 +295,15 @@ color: var(--color-text-secondary); } +/* 공통 칸 라벨 끝 작은 글 — 고치기 전 암 · 포장이 실제로 쓰는 값(364-6 · 보기 전용) */ +.b06-std__group-note { + margin-left: var(--spacing-4); + font-size: 0.9em; + font-weight: normal; + color: var(--color-text-muted); + white-space: nowrap; +} + .b06-std__actions { display: flex; gap: var(--spacing-8); diff --git a/resources/tester/helper_364_6_entry.ts b/resources/tester/helper_364_6_entry.ts index b8781a9c4..d3cb70cbd 100644 --- a/resources/tester/helper_364_6_entry.ts +++ b/resources/tester/helper_364_6_entry.ts @@ -1,6 +1,7 @@ /* 364-6 시험 묶음 진입점 — 표준 횡단 패널 열기 전후 같은 다시 셈인지 잼(vite ssr). */ export { refreshCrossDesigns } from "../../B06_Section/B06_Section_Cross_Refresh"; export { + commonGroupNote, createStandardPanel, effectiveStandardCross, rememberStandardDefaults, diff --git a/resources/tester/helper_364_6_std_panel.mjs b/resources/tester/helper_364_6_std_panel.mjs index 8d6c38094..248a7ea57 100644 --- a/resources/tester/helper_364_6_std_panel.mjs +++ b/resources/tester/helper_364_6_std_panel.mjs @@ -60,7 +60,21 @@ async function scenario(pid, seed) { return { b05, b06, session: m.readState("std-cross", pid) }; } +/** 공통 칸 끝 글(364-6 덧) — 고치기 전 · 공통 칸을 고친 뒤(토사 값을 다른 그룹에 펼침 = spread). */ +const LABELS = [ + "B06_Std_Field_DitchTop", + "B06_Std_Field_CrossSlopeMin", + "B06_Std_Field_CrossSlopeMax", + "B06_Std_Field_CutSlope", +]; +const notesOf = (state) => Object.fromEntries(LABELS.map((l) => [l, m.commonGroupNote(state, l)])); +const edited = JSON.parse(JSON.stringify(standard)); +edited.rock.ditch.top_width_m = edited.soil.ditch.top_width_m; +edited.paved.ditch.top_width_m = edited.soil.ditch.top_width_m; +edited.rock.cross_slope_pct.max = edited.soil.cross_slope_pct.max; + const out = { + notes: { before: notesOf(standard), after: notesOf(edited) }, server: areas(fixture.detail.cross_sections), standard, config: await scenario("p364a", null), diff --git a/resources/tester/test_364_6_std_panel_same_value.py b/resources/tester/test_364_6_std_panel_same_value.py index 0eb24e3c2..c69406c55 100644 --- a/resources/tester/test_364_6_std_panel_same_value.py +++ b/resources/tester/test_364_6_std_panel_same_value.py @@ -82,3 +82,15 @@ def test_B06_칸_열어도_B05_값(case, out): @pytest.mark.parametrize("case", ["config", "stored"]) def test_칸_열기는_표준_횡단을_안_고침(case, out): assert out[case]["session"] == out["standard"] + + +def test_고치기_전_공통_칸_끝에_암_값(out): + before = out["notes"]["before"] + assert before["B06_Std_Field_DitchTop"] == "암 0.69" + assert before["B06_Std_Field_CrossSlopeMax"] == "암 3 %" + assert before["B06_Std_Field_CrossSlopeMin"] == "" + assert before["B06_Std_Field_CutSlope"] == "" # 암은 자기 칸 · 포장은 같은 값 + + +def test_고친_뒤_끝_글_없음(out): + assert set(out["notes"]["after"].values()) == {""} diff --git a/ui_template/ui_template_locale_b2.ts b/ui_template/ui_template_locale_b2.ts index 677cdb4b9..3470286fa 100644 --- a/ui_template/ui_template_locale_b2.ts +++ b/ui_template/ui_template_locale_b2.ts @@ -1045,4 +1045,6 @@ export const ui_locales_b2 = { "입금 확인이 완료되면 설계문서와 DWG 다운로드가 활성화됩니다.", "Design document and DWG downloads are enabled after deposit confirmation.", ], + B06_Std_Note_Rock: ["암", "Rock"], + B06_Std_Note_Paved: ["포장", "Paved"], } as const satisfies Record;