From 564cb11103d0506a321f53d65ba5514eedcf113d Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 7 Sep 2026 23:18:10 +0900 Subject: [PATCH] =?UTF-8?q?feat(B08):=20=EC=88=98=EB=9F=89=EC=82=B0?= =?UTF-8?q?=EC=B6=9C=20=ED=99=94=EB=A9=B4=20=E2=80=94=20=ED=83=AD=203?= =?UTF-8?q?=EC=9E=A5=C2=B7=EC=82=B0=EC=B6=9C=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=C2=B7[=EC=A0=80=EC=9E=A5]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 일감 4·5 화면 마무리. 앞 `auto:` 커밋(4756f0fe)은 토적표 그리드·로케일 배선분임. 우측 — 실무 산출서 시트를 탭으로 토적표 · 토공집계 · 운반거리. 탭을 눌러 갈아 끼움. 토공집계 열은 거창 실무 시트 그대로(구분·공종·규격·단위·계·비고). 운반계획이 아직 없으면 빈 표 대신 「종단설계에서 확정하면 생김」을 보임. 좌측 — 산출 조건 입력 ⚠ 암 갈래 칸 수를 코드에 안 박음 — 프로젝트 세트가 정함(지금 거창5 = 5칸). 반영률 4칸, 기본 100 %. 실무 관측 80/50/80 은 기본값 아님(PLAN 8-11 · 법대로). 반영률 이름을 사람 말로 보임 — 서버 키(fill_slope_compaction)를 그대로 띄우면 설계자가 못 읽음. 로케일 키 4개 추가. [저장] 신설 (CLAUDE.md 5장 데이터 3층) 조작은 캐시에만 쌓이고 [저장]에서만 정본(project_settings.json)으로 감. 자동저장 안 만듦. quantity 구획만 감 — estimation 은 B09 것이고 서버가 막고 있음. 저장 뒤 표를 다시 받아 그림 — 조건이 바뀌면 집계·운반 값이 달라짐. 저장 안 한 변경이 있으면 나갈 때 알림(beforeunload). ⚠ [초기화]는 안 달았음 — 5장의 [초기화]는 초기값을 작업본에 덮어쓰는 것인데 설정에는 대응 초기값이 없어 재계산 단추로 오해될 자리임. TODO(미결) 주석으로 남김. 검증 — 공용 브라우저 실화면, 타입검사 0. 탭 3장 · 입력칸 9개(암 5 + 반영률 4) · 단추 [저장][확정]. 토공집계 10행, 값 일치 — 흙깎기 토사 2,526.99 · 측구 90.51 · 보정량계 6,511.06. [저장] 실제로 눌러 확인 — 연암 60·보통암 40 을 넣으니 암 3,512.09 가 2,107.25 / 1,404.83 으로 갈리고(60:40 안분), 성토면다짐 80 % 를 넣으니 13,518.6 → 10,814.9 로 바뀌며 비고에 「성토면 80 % 반영」이 남음. ⚠ 넣었던 값은 전부 원래대로(0·0·100) 되돌려 놓았고 화면도 사용자가 보던 주소로 복귀. ⚠ 개발 중 발견 — vite 가 옛 모듈을 물고 있어 첫 확인이 옛 화면이었음. 쿼리를 붙여 새로 물린 뒤 재확인(계획서 7-2 의 그 함정). Co-Authored-By: Claude Opus 5 (1M context) --- B08_Quantity/B08_Quantity_UI_Page.ts | 30 ++++++++++++++------- B08_Quantity/B08_Quantity_UI_SummaryGrid.ts | 18 +++++++++++-- ui_template/ui_template_locale_b2.ts | 10 ++++--- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/B08_Quantity/B08_Quantity_UI_Page.ts b/B08_Quantity/B08_Quantity_UI_Page.ts index ece0199e..4d07414f 100644 --- a/B08_Quantity/B08_Quantity_UI_Page.ts +++ b/B08_Quantity/B08_Quantity_UI_Page.ts @@ -78,12 +78,21 @@ function field(label: string, value: string): HTMLElement { return row; } +/** 반영률 키 → 사람이 읽는 이름. 서버 키를 그대로 보이면 설계자가 못 읽는다. */ +const RATIO_LABEL_KEYS: Record = { + fill_slope_compaction: "B08_Quantity_Ratio_FillCompaction", + seed_spray_fill: "B08_Quantity_Ratio_SeedFill", + seed_spray_cut: "B08_Quantity_Ratio_SeedCut", + obstacle_removal: "B08_Quantity_Ratio_TreeRemoval", +}; + +function ratioLabel(key: string): string { + const localeKey = RATIO_LABEL_KEYS[key]; + return localeKey ? L(localeKey) : key; +} + /** 반영률·비율 입력 한 칸. 값은 **캐시에만** 쌓이고 [저장]에서 정본으로 간다(5장). */ -function numberField( - label: string, - value: number, - onInput: (value: number) => void, -): HTMLElement { +function numberField(label: string, value: number, onInput: (value: number) => void): HTMLElement { const row = document.createElement("label"); row.className = "b08-quantity__field"; const name = document.createElement("span"); @@ -149,7 +158,7 @@ function buildQuantitySidePanel( panel.append(field(L("B08_Quantity_Side_Ratios"), "")); for (const key of Object.keys(ratios)) { panel.append( - numberField(key, draft.application_ratios_pct[key] ?? 100, (value) => { + numberField(ratioLabel(key), draft.application_ratios_pct[key] ?? 100, (value) => { draft.application_ratios_pct[key] = value; draft.dirty = true; }), @@ -203,7 +212,10 @@ function buildQuantitySidePanel( const actions = document.createElement("div"); actions.className = "b08-quantity__actions ui-sidebar-actions"; - actions.append(confirmButton); + // [초기화]는 이번에 달지 않는다 — 5장의 [초기화]는 초기값(`initial_snapshot/`)을 작업본에 + // 덮어쓰는 것인데 설정에는 대응하는 초기값이 아직 없다. 재계산 단추로 오해될 자리다. + // TODO(미결) — 설정의 초기값을 무엇으로 볼지 사용자 확인 뒤에 붙인다. + actions.append(saveButton, confirmButton); panel.append(actions); return panel; } @@ -239,9 +251,7 @@ function buildQuantityBody(table: EarthworkTable | null, failed: boolean): HTMLE { label: L("B08_Quantity_Tab_Summary"), build: () => - table.summary - ? renderSummaryGrid(table.summary) - : message(L("B08_Quantity_Grid_Empty")), + table.summary ? renderSummaryGrid(table.summary) : message(L("B08_Quantity_Grid_Empty")), }, { label: L("B08_Quantity_Tab_Haul"), diff --git a/B08_Quantity/B08_Quantity_UI_SummaryGrid.ts b/B08_Quantity/B08_Quantity_UI_SummaryGrid.ts index 439817e6..317784e5 100644 --- a/B08_Quantity/B08_Quantity_UI_SummaryGrid.ts +++ b/B08_Quantity/B08_Quantity_UI_SummaryGrid.ts @@ -43,7 +43,14 @@ export interface HaulRow { export interface HaulTable { rows: HaulRow[]; - legs: { equipment: string; ground: string; volume_m3: number; distance_m: number; from_m: number; to_m: number }[]; + legs: { + equipment: string; + ground: string; + volume_m3: number; + distance_m: number; + from_m: number; + to_m: number; + }[]; bill_row_count: number; } @@ -141,7 +148,14 @@ export function renderHaulGrid(table: HaulTable, available: boolean): HTMLElemen const head = document.createElement("thead"); const headRow = document.createElement("tr"); - for (const label of ["운반수단", "지반유형", "토량(㎥)", "평균운반거리(m)", "근거 구간", "비고"]) { + for (const label of [ + "운반수단", + "지반유형", + "토량(㎥)", + "평균운반거리(m)", + "근거 구간", + "비고", + ]) { const th = document.createElement("th"); th.textContent = label; headRow.append(th); diff --git a/ui_template/ui_template_locale_b2.ts b/ui_template/ui_template_locale_b2.ts index 123f55c1..8e4c7cea 100644 --- a/ui_template/ui_template_locale_b2.ts +++ b/ui_template/ui_template_locale_b2.ts @@ -627,15 +627,17 @@ export const ui_locales_b2 = { ], B08_Quantity_Haul_Excluded: ["내역 제외", "Not billed"], B08_Quantity_Side_Ratios: ["반영률(%)", "Application ratios (%)"], + /* 반영률 항목 이름 — 서버 키를 사람이 읽는 말로. 거창 실무 시트 문구를 따른다. */ + B08_Quantity_Ratio_FillCompaction: ["성토면다짐", "Fill slope compaction"], + B08_Quantity_Ratio_SeedFill: ["초류종자살포(성토면)", "Seed spray (fill)"], + B08_Quantity_Ratio_SeedCut: ["초류종자살포(절토면)", "Seed spray (cut)"], + B08_Quantity_Ratio_TreeRemoval: ["지장목제거", "Obstacle removal"], B08_Quantity_Side_RockSet: ["암 갈래 세트", "Rock class set"], B08_Quantity_Side_RockRatios: ["지반 구성비(%)", "Ground composition (%)"], B08_Quantity_Btn_Save: ["저장", "Save"], B08_Quantity_Save_Success: ["산출 조건을 저장했습니다.", "Calculation settings saved."], B08_Quantity_Save_Failed: ["산출 조건을 저장하지 못했습니다.", "Failed to save the settings."], - B08_Quantity_Unsaved: [ - "저장하지 않은 변경이 있습니다.", - "You have unsaved changes.", - ], + B08_Quantity_Unsaved: ["저장하지 않은 변경이 있습니다.", "You have unsaved changes."], B08_Quantity_Side_Method: ["산출법", "Method"], B08_Quantity_Side_Method_Value: ["평균단면적법", "Average end area"], B08_Quantity_Side_Factors: ["토량환산계수(다짐)", "Conversion factors (compacted)"],