feat(B08): 확정 값 넷을 화면 칸으로 세움 — 값과 근거를 보이고 바꿀 수 있게

사용자 지시(2026-09-09): 「값을 코드에 박고 끝내지 말 것 · 대신 페이지에 남길 것」

- 층따기 길이(확정 2차 ①) — 비면 층따기 줄이 막힘. 칸이 없어 값이 안 들어가던 자리
- 기초잡석 두께(확정 3차 ②, 0.2m) — 근거: 폭은 버림 폭과 같음(KCS 34 50 05),
  두께는 품셈 12-25 가 안 정해 사용자 확정값
- 사토장까지 거리(확정 3차 ③ 미정) — 비어 있고, 비면 사토 운반 줄이 막힘
- 구조물터파기 용수(확정 3차 ④) — 기본 「육상」이 **통상값이고 사용자 확정 아님**을
  칸 밑 근거 줄에 적음. 품셈 9-13 의 18구분이 이 값으로 갈림
- 칸마다 근거 한 줄(`b08-quantity__hint`)을 붙임 — 왜 그 값인지 화면에서 보이게

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-09 06:28:28 +09:00
co-authored by Claude Opus 5
parent a57e1bbe6d
commit ab51fa38cf
4 changed files with 119 additions and 0 deletions
@@ -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 {
@@ -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); }
`;
/** 스타일을 한 번만 넣는다 — 페이지를 다시 그려도 중복되지 않는다. */
+83
View File
@@ -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<string, keyof typeof ui_locales> = {
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<string, SupplyChoice>;
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<void> {
rock_methods: { ...((stored.rock_methods ?? {}) as Record<string, string>) },
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<string, SupplyChoice>) },
dirty: false,
};
+26
View File
@@ -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)"],