diff --git a/B08_Quantity/B08_Quantity_UI_EarthworkGrid.ts b/B08_Quantity/B08_Quantity_UI_EarthworkGrid.ts index be3184cc..49a8b023 100644 --- a/B08_Quantity/B08_Quantity_UI_EarthworkGrid.ts +++ b/B08_Quantity/B08_Quantity_UI_EarthworkGrid.ts @@ -67,6 +67,14 @@ export interface QuantitySettings { concrete_placing_method?: string | null; /** 표토 두께(m). `null`·없음이면 **안 정한 것**이라 표토제거 줄이 「근거 없음」으로 선다. */ topsoil_thickness_m?: number | null; + /** 층따기 길이(m) — 면적 × 이 값 = ㎥ (확정 2차 ①). 비면 층따기 줄이 막힌다. */ + bench_cut_depth_m?: number | null; + /** 기초잡석 두께(m) — 확정 3차 ② 0.2. 폭은 버림 폭과 같다(KCS 34 50 05). */ + rubble_base_thickness_m?: number | null; + /** 사토장까지 거리(m) — 현장값. 비면 사토 운반 줄이 막힌다. */ + spoil_site_distance_m?: number | null; + /** 구조물터파기 용수 — `"육상"`·`"용수"`. ⚠ 「육상」은 통상값이지 사용자 확정이 아니다. */ + structure_trench_water?: string | null; } export interface EarthworkTable { diff --git a/B08_Quantity/B08_Quantity_UI_EarthworkGrid_Style.ts b/B08_Quantity/B08_Quantity_UI_EarthworkGrid_Style.ts index fb8fe14d..69be9c9f 100644 --- a/B08_Quantity/B08_Quantity_UI_EarthworkGrid_Style.ts +++ b/B08_Quantity/B08_Quantity_UI_EarthworkGrid_Style.ts @@ -184,6 +184,8 @@ const CSS = ` .b08-quantity__message { margin: 0; padding: 16px; font-size: 13px; color: var(--color-text-secondary); } .b08-quantity__field { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; padding: 2px 0; } .b08-quantity__field-value { color: var(--color-text-secondary); font-variant-numeric: tabular-nums; } +/* 칸 밑 근거 한 줄 — 왜 그 값인지 화면에서 보이게 한다(2026-09-09 사용자 지시). */ +.b08-quantity__hint { margin: 0 0 6px; font-size: 11px; line-height: 1.4; color: var(--color-text-secondary); } `; /** 스타일을 한 번만 넣는다 — 페이지를 다시 그려도 중복되지 않는다. */ diff --git a/B08_Quantity/B08_Quantity_UI_Page.ts b/B08_Quantity/B08_Quantity_UI_Page.ts index affaff1c..a86d81f4 100644 --- a/B08_Quantity/B08_Quantity_UI_Page.ts +++ b/B08_Quantity/B08_Quantity_UI_Page.ts @@ -86,6 +86,11 @@ async function saveQuantitySettings(projectId: string, draft: DraftSettings): Pr concrete_placing_method: draft.concrete_placing_method, // ⚠ `null` 도 그대로 보낸다 — 「안 정함」으로 되돌릴 길이 있어야 한다(시공법과 같은 규칙). topsoil_thickness_m: draft.topsoil_thickness_m, + // 확정 2차 ① · 3차 ②③④ — 값이 화면 칸에서 오고, 비면 그 줄이 막힌다. + bench_cut_depth_m: draft.bench_cut_depth_m, + rubble_base_thickness_m: draft.rubble_base_thickness_m, + spoil_site_distance_m: draft.spoil_site_distance_m, + structure_trench_water: draft.structure_trench_water, }), }, ); @@ -105,6 +110,14 @@ function field(label: string, value: string): HTMLElement { return row; } +/** 칸 밑에 붙는 **근거 한 줄** — 왜 그 값인지 화면에서 보이게 한다(2026-09-09 사용자 지시). */ +function hintRow(text: string): HTMLElement { + const row = document.createElement("p"); + row.className = "b08-quantity__hint"; + row.textContent = text; + return row; +} + /** 반영률 키 → 사람이 읽는 이름. 서버 키를 그대로 보이면 설계자가 못 읽는다. */ const RATIO_LABEL_KEYS: Record = { fill_slope_compaction: "B08_Quantity_Ratio_FillCompaction", @@ -228,6 +241,14 @@ interface DraftSettings { concrete_placing_method: string; // 표토 두께(m) — `null` 은 「안 정함」. 정해야 표토제거 줄이 선다(품셈 9-15 [주]② 의 T). topsoil_thickness_m: number | null; + // 층따기 길이(m) — 면적 × 이 값 = ㎥ (확정 2차 ①). `null` 이면 층따기 줄이 막힌다. + bench_cut_depth_m: number | null; + // 기초잡석 두께(m) — 확정 3차 ② 0.2. 폭은 버림 폭과 같다(KCS 34 50 05). + rubble_base_thickness_m: number | null; + // 사토장까지 거리(m) — 현장값. `null` 이면 사토 운반 줄이 막힌다(확정 3차 ③ 는 미정). + spoil_site_distance_m: number | null; + // 구조물터파기 용수 — "육상"·"용수". ⚠ 「육상」은 **통상값**이지 사용자 확정이 아니다. + structure_trench_water: string; // 자재별 관급/사급 — 표 안에서 줄마다 고른 값. material_supply: Record; dirty: boolean; @@ -408,6 +429,64 @@ function buildQuantitySidePanel( ), ); + // ── 구조물·사토 — 확정 2차 ① · 3차 ②③④ 의 값들 ────────────────────── + // ⚠ 사용자 지시(2026-09-09): **값을 코드에 박고 끝내지 말고 화면에 칸으로 세우고 + // 지금 값과 근거를 보이고 바꿀 수 있게 할 것.** 정한 값이 화면에 안 보이면 다음 사람이 + // 왜 그 값인지 모른다. + panel.append(field(L("B08_Quantity_Side_Structure"), "")); + panel.append( + optionalNumberField( + L("B08_Quantity_Side_BenchCut_Label"), + draft.bench_cut_depth_m, + "0.01", + (value) => { + draft.bench_cut_depth_m = value; + draft.dirty = true; + }, + ), + ); + panel.append(hintRow(L("B08_Quantity_Side_BenchCut_Hint"))); + panel.append( + optionalNumberField( + L("B08_Quantity_Side_Rubble_Label"), + draft.rubble_base_thickness_m, + "0.05", + (value) => { + draft.rubble_base_thickness_m = value; + draft.dirty = true; + }, + ), + ); + panel.append(hintRow(L("B08_Quantity_Side_Rubble_Hint"))); + panel.append( + optionalNumberField( + L("B08_Quantity_Side_SpoilDistance_Label"), + draft.spoil_site_distance_m, + "10", + (value) => { + draft.spoil_site_distance_m = value; + draft.dirty = true; + }, + ), + ); + panel.append(hintRow(L("B08_Quantity_Side_SpoilDistance_Hint"))); + panel.append( + selectField( + L("B08_Quantity_Side_Water_Label"), + draft.structure_trench_water, + [ + { value: "", label: L("B08_Quantity_Water_Unset") }, + { value: "육상", label: L("B08_Quantity_Water_Dry") }, + { value: "용수", label: L("B08_Quantity_Water_Wet") }, + ], + (value) => { + draft.structure_trench_water = value; + draft.dirty = true; + }, + ), + ); + panel.append(hintRow(L("B08_Quantity_Side_Water_Hint"))); + // ── 콘크리트 타설 방식 — ⚠ **금액에 바로 걸리는 값**이라 잠정임을 조용히 두지 않는다 ── panel.append(field(L("B08_Quantity_Side_Placing"), "")); panel.append( @@ -659,6 +738,10 @@ export async function renderB08Quantity(root: HTMLElement): Promise { rock_methods: { ...((stored.rock_methods ?? {}) as Record) }, concrete_placing_method: (stored.concrete_placing_method as string) ?? "", topsoil_thickness_m: (stored.topsoil_thickness_m as number | null) ?? null, + bench_cut_depth_m: (stored.bench_cut_depth_m as number | null) ?? null, + rubble_base_thickness_m: (stored.rubble_base_thickness_m as number | null) ?? null, + spoil_site_distance_m: (stored.spoil_site_distance_m as number | null) ?? null, + structure_trench_water: (stored.structure_trench_water as string) ?? "", material_supply: { ...((stored.material_supply ?? {}) as Record) }, dirty: false, }; diff --git a/ui_template/ui_template_locale_b2.ts b/ui_template/ui_template_locale_b2.ts index aac5b82c..d7ea2902 100644 --- a/ui_template/ui_template_locale_b2.ts +++ b/ui_template/ui_template_locale_b2.ts @@ -648,6 +648,32 @@ export const ui_locales_b2 = { B08_Quantity_Side_Topsoil: ["표토제거", "Topsoil Removal"], B08_Quantity_Side_Topsoil_Label: ["표토 두께(m)", "Topsoil thickness (m)"], B08_Quantity_Unset_Placeholder: ["안 정함", "Not set"], + // ── 확정 3차(2026-09-09) 로 정한 값들 — **화면에 칸으로 세우고 근거를 보인다.** + // 사용자 지시: 「값을 코드에 박고 끝내지 말 것 · 대신 페이지에 남길 것」. + B08_Quantity_Side_Structure: ["구조물·사토", "Structures & Spoil"], + B08_Quantity_Side_BenchCut_Label: ["층따기 길이(m)", "Bench cut length (m)"], + B08_Quantity_Side_BenchCut_Hint: [ + "면적 × 이 값 = ㎥ — 비우면 층따기 줄이 막힙니다(확정 2차 ①)", + "Area × this = ㎥ — blank keeps the bench-cut row blocked", + ], + B08_Quantity_Side_Rubble_Label: ["기초잡석 두께(m)", "Rubble base thickness (m)"], + B08_Quantity_Side_Rubble_Hint: [ + "폭은 버림 폭과 같음(KCS 34 50 05) · 두께 0.2m 는 사용자 확정 3차 ② — 품셈 12-25 는 두께를 정하지 않음", + "Width equals the blinding width (KCS 34 50 05); 0.2 m thickness is a user decision — the standard gives none", + ], + B08_Quantity_Side_SpoilDistance_Label: ["사토장까지 거리(m)", "Distance to spoil site (m)"], + B08_Quantity_Side_SpoilDistance_Hint: [ + "비어 있으면 사토 운반 줄이 막힙니다 — 품셈이 정하는 값이 아니라 현장값입니다", + "Blank keeps the spoil haul row blocked — this is a site value, not from the standard", + ], + B08_Quantity_Side_Water_Label: ["구조물터파기 용수", "Structure trench water"], + B08_Quantity_Water_Dry: ["육상(용수 없음)", "Dry"], + B08_Quantity_Water_Wet: ["용수", "Wet"], + B08_Quantity_Water_Unset: ["안 정함(줄이 막힘)", "Not set (row blocked)"], + B08_Quantity_Side_Water_Hint: [ + "⚠ 「육상」은 통상값이고 사용자 확정이 아닙니다(확정 3차 ④) — 품셈 9-13 의 18구분이 이 값으로 갈립니다", + "⚠ “Dry” is a customary default, not a user decision — it selects one of the 18 sub-items", + ], B08_Quantity_Side_Placing: ["콘크리트 타설", "Concrete Placing"], B08_Quantity_Side_Placing_Label: ["타설 방식", "Method"], B08_Quantity_Placing_Unset: ["안 정함(기본값 사용)", "Not set (default)"],