Files
Aislo/B08_Quantity/B08_Quantity_UI_Side_TreeWaste.ts
eomsangdonandClaude Opus 5 35ba45f572 feat(b08): 임목폐기물 뿌리 산정법 고르개 · 지침 π 3.14 · 분리발주 대상 아님 안내
- 환경친화적인 도로건설 지침 부록1 확인: 뿌리분 체적 V = 0.3927D³ × 1,300㎏/㎥ — 고르개로 넣음(기본은 분배비 15/85)
- 뿌리분 직경 D 는 지침 표기 「뿌리분의 직경(DBH, m)」이 모호해 흉고직경과 따로 받음
- 지상부 식을 지침 원문대로 π 3.14 로 셈 — 실정보고 예 130.40㎥ 와 바로 맞음
- 분리발주 칸 밑·사유에 「임목폐기물은 분리발주 대상 아님(환경부 회신)」

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
2026-09-14 01:09:23 +09:00

132 lines
4.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* B08_Quantity_UI_Side_TreeWaste.ts
* 산출 조건 「임목폐기물」 구획 — 현장 조사값 넷 · 수동 처리단가 · 분리발주(2026-09-14 판정).
*
* 페이지 본문(`B08_Quantity_UI_Page.ts`)이 700줄을 넘어 새 칸을 이 파일로 뺌(CLAUDE.md 4장).
* ⚠ 톤은 서버가 셈 — 여기는 값만 캐시에 쌓고 [저장]에서 정본으로 감(5장).
* ========================================================================== */
import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
function L(key: keyof typeof ui_locales): string {
return ui_locales[key][currentLanguageIndex];
}
export interface TreeWasteDraft {
tree_waste: Record<string, number | null>;
tree_waste_unit_price_krw_per_ton: number | null;
waste_separate_order: boolean;
/** 뿌리 산정법 — `""` 분배비 15/85(기본) · "root_ball" 뿌리분 체적 × 1,300. */
tree_waste_root_method: string;
dirty: boolean;
}
/** 페이지의 칸 만들기 도우미 — 같은 모양을 쓰려고 받아 씀. */
export interface SideFieldHelpers {
field(label: string, value: string): HTMLElement;
optionalNumberField(
label: string,
value: number | null,
step: string,
onInput: (value: number | null) => void,
): HTMLElement;
selectField(
label: string,
value: string,
options: { value: string; label: string }[],
onChange: (value: string) => void,
): HTMLElement;
hintRow(text: string): HTMLElement;
}
/** 조사값 칸 — 서버 `TREE_WASTE_INPUTS` 와 같은 키. */
const INPUTS: [string, keyof typeof ui_locales, string][] = [
["stems_per_1000m2", "B08_Quantity_TreeWaste_Stems", "1"],
["dbh_cm", "B08_Quantity_TreeWaste_Dbh", "0.1"],
["height_m", "B08_Quantity_TreeWaste_Height", "0.1"],
["unit_weight_kg_m3", "B08_Quantity_TreeWaste_UnitWeight", "10"],
];
/** 수동 단가 칸 — 값이 있으면 빨간 테두리(미확정). */
function markManual(row: HTMLElement, value: number | null): void {
const input = row.querySelector("input");
if (input) input.style.outline = value ? "2px solid var(--color-danger, #d9534f)" : "";
}
export function appendTreeWasteFields(
panel: HTMLElement,
draft: TreeWasteDraft,
h: SideFieldHelpers,
): void {
panel.append(h.field(L("B08_Quantity_Side_TreeWaste"), ""));
for (const [key, label, step] of INPUTS) {
panel.append(
h.optionalNumberField(L(label), draft.tree_waste[key] ?? null, step, (value) => {
draft.tree_waste[key] = value;
draft.dirty = true;
}),
);
}
panel.append(h.hintRow(L("B08_Quantity_Side_TreeWaste_Hint")));
// 뿌리 — 지침 부록1 이 두 방법을 둠. 뿌리분 직경은 흉고직경과 갈라 따로 받음(지침 표기가 모호).
panel.append(
h.selectField(
L("B08_Quantity_TreeWaste_RootMethod"),
draft.tree_waste_root_method,
[
{ value: "", label: L("B08_Quantity_TreeWaste_RootRatio") },
{ value: "root_ball", label: L("B08_Quantity_TreeWaste_RootBall") },
],
(value) => {
draft.tree_waste_root_method = value;
draft.dirty = true;
},
),
);
panel.append(
h.optionalNumberField(
L("B08_Quantity_TreeWaste_RootBallDiameter"),
draft.tree_waste["root_ball_diameter_m"] ?? null,
"0.01",
(value) => {
draft.tree_waste["root_ball_diameter_m"] = value;
draft.dirty = true;
},
),
);
panel.append(h.hintRow(L("B08_Quantity_TreeWaste_Root_Hint")));
const price = h.optionalNumberField(
L("B08_Quantity_TreeWaste_Price"),
draft.tree_waste_unit_price_krw_per_ton,
"1000",
(value) => {
draft.tree_waste_unit_price_krw_per_ton = value;
draft.dirty = true;
markManual(price, value);
},
);
markManual(price, draft.tree_waste_unit_price_krw_per_ton);
panel.append(price);
const priceHint = h.hintRow(L("B08_Quantity_TreeWaste_Price_Hint"));
priceHint.classList.add("b08-quantity__hint--warn");
panel.append(priceHint);
panel.append(
h.selectField(
L("B08_Quantity_TreeWaste_Separate"),
draft.waste_separate_order ? "on" : "",
[
{ value: "", label: L("B08_Quantity_TreeWaste_Separate_Off") },
{ value: "on", label: L("B08_Quantity_TreeWaste_Separate_On") },
],
(value) => {
draft.waste_separate_order = value === "on";
draft.dirty = true;
},
),
);
panel.append(h.hintRow(L("B08_Quantity_TreeWaste_Separate_Hint")));
}