From 566e0c885e67a26354bc816faa2851723acefdcc Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 16 Sep 2026 14:03:03 +0900 Subject: [PATCH] =?UTF-8?q?feat(z01):=20=EC=A2=8C=EC=B8=A1=EC=9D=84=20?= =?UTF-8?q?=EB=91=98=EB=A1=9C=20=EA=B0=80=EB=A6=84=20=E2=80=94=20=E2=91=A0?= =?UTF-8?q?=EA=B8=B0=EC=B4=88=EB=8B=A8=EA=B0=80=20=EB=8B=A4=EC=84=AF(?= =?UTF-8?q?=EC=96=BC=EB=A7=88=EC=9D=B8=EA=B0=80=20=C2=B7=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=A5=EC=9D=B4=20=EC=A0=95=ED=95=A8)=20=E2=91=A1=ED=92=88?= =?UTF-8?q?=EC=85=88=20=EA=B8=B0=EC=A4=80=20=EC=95=84=ED=99=89(=EC=96=BC?= =?UTF-8?q?=EB=A7=88=EB=82=98=20=EB=93=9C=EB=82=98=20=C2=B7=20=ED=92=88?= =?UTF-8?q?=EC=85=88=EC=9D=B4=20=ED=95=B4=EB=A7=88=EB=8B=A4=20=ED=95=9C=20?= =?UTF-8?q?=EB=B2=88=20=C2=B7=20=ED=86=A0=EB=9F=89=ED=99=98=EC=82=B0=C2=B7?= =?UTF-8?q?=EC=9E=90=EC=9E=AC=ED=95=A0=EC=A6=9D=C2=B7=EA=B1=B0=ED=91=B8?= =?UTF-8?q?=EC=A7=91=C2=B7=EC=B2=A0=EA=B7=BC=C2=B7=EB=AA=A9=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=C2=B7=EB=8F=8C=EC=8C=93=EA=B8=B0=20=EA=B2=BD=EC=82=AC?= =?UTF-8?q?=C2=B7=EB=92=B7=EA=B8=B8=EC=9D=B4=C2=B7=EB=8F=8C=EC=A2=85?= =?UTF-8?q?=EB=A5=98=C2=B7=EA=B0=88=EB=9E=98)=20=C2=B7=20=EC=B6=95=20?= =?UTF-8?q?=EC=B9=B8=20=EC=9E=A0=EA=B8=88=C2=B7=EA=B5=AC=EA=B0=84=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9D=80=20=EC=84=9C=EB=B2=84=EA=B0=80=20?= =?UTF-8?q?=EC=A4=80=20=EB=8C=80=EB=A1=9C=20=EA=B7=B8=EB=8C=80=EB=A1=9C(?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=E2=91=A0=E2=91=A1=20=EC=A7=80?= =?UTF-8?q?=EC=8B=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq --- Z01_MasterData/Z01_MasterData_Api_Fetch.ts | 21 ++++++++- Z01_MasterData/Z01_MasterData_UI_Side.ts | 51 ++++++++++++++-------- ui_template/ui_template_locale_b3.ts | 11 +++++ 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/Z01_MasterData/Z01_MasterData_Api_Fetch.ts b/Z01_MasterData/Z01_MasterData_Api_Fetch.ts index 7d881ee6..7a76b9e7 100644 --- a/Z01_MasterData/Z01_MasterData_Api_Fetch.ts +++ b/Z01_MasterData/Z01_MasterData_Api_Fetch.ts @@ -47,9 +47,26 @@ export interface RowsQuery { sort?: SortOrder | null; } -/** 기초단가 다섯 — 고친 값은 원본이 아니라 덮개 파일에 쌓임(품셈이 갱신돼도 안 날아감). */ +/** + * ① 기초단가 다섯 — 얼마인가. 시장이 정하고 주간~반기로 바뀜. + * 고친 값은 원본이 아니라 덮개 파일에 쌓임(품셈이 갱신돼도 안 날아감). + */ export const BASE_PRICE_KINDS = ["labor", "machine", "material", "oil", "rate"] as const; -export type BasePriceKind = (typeof BASE_PRICE_KINDS)[number]; + +/** ② 품셈 기준 아홉 — 얼마나 드나. 품셈이 정하고 해마다 한 번 개정(2026-09-16 사용자 ①②). */ +export const SPEC_KINDS = [ + "coef", + "material_surcharge", + "formwork_reuse", + "rebar_complexity", + "timber_structure_class", + "masonry_slope", + "masonry_back_length", + "stone_kind", + "masonry_class", +] as const; + +export type BasePriceKind = (typeof BASE_PRICE_KINDS)[number] | (typeof SPEC_KINDS)[number]; async function request(path: string, init: RequestInit = {}): Promise { const response = await fetch(`${API_BASE_URL}${path}`, { diff --git a/Z01_MasterData/Z01_MasterData_UI_Side.ts b/Z01_MasterData/Z01_MasterData_UI_Side.ts index c50d6fed..a0fb7fee 100644 --- a/Z01_MasterData/Z01_MasterData_UI_Side.ts +++ b/Z01_MasterData/Z01_MasterData_UI_Side.ts @@ -15,9 +15,11 @@ import { fetchMasterRows, fetchOverrides, type MasterGroup, + type BasePriceKind, type MasterRows, type RowsQuery, saveBasePrice, + SPEC_KINDS, VALUES_TABLE_ID, } from "./Z01_MasterData_Api_Fetch"; import { valueWithUnit, type OverrideState } from "./Z01_MasterData_UI_Cells"; @@ -31,25 +33,32 @@ export function buildSidePanel(groups: MasterGroup[], view: RowsView): HTMLEleme }; const baseTitle = L("Z01_MasterData_BasePrices"); - const baseGrid = el("div", { className: "z01-master__base-grid" }); - for (const kind of BASE_PRICE_KINDS) { - const label = L(`Z01_MasterData_Base_${kind}` as const); - const button = el("button", { - className: "z01-master__base", - attrs: { type: "button" }, - text: label, - }); - button.addEventListener("click", () => { - activate(button); - view.show({ - title: `${baseTitle} › ${label}`, - key: `base-prices/${kind}`, - load: (query) => fetchBasePrices(kind, query), - save: (rowId, values) => saveBasePrice(kind, rowId, values), + const specTitle = L("Z01_MasterData_SpecValues"); + /** 한 상자 몫 단추들 — ①얼마인가 ②얼마나 드나 로 갈라 세움(성격도 갱신 주기도 다름). */ + const kindGrid = (boxTitle: string, kinds: readonly BasePriceKind[]): HTMLElement => { + const grid = el("div", { className: "z01-master__base-grid" }); + for (const kind of kinds) { + const label = L(`Z01_MasterData_Base_${kind}` as const); + const button = el("button", { + className: "z01-master__base", + attrs: { type: "button" }, + text: label, }); - }); - baseGrid.append(button); - } + button.addEventListener("click", () => { + activate(button); + view.show({ + title: `${boxTitle} › ${label}`, + key: `base-prices/${kind}`, + load: (query) => fetchBasePrices(kind, query), + save: (rowId, values) => saveBasePrice(kind, rowId, values), + }); + }); + grid.append(button); + } + return grid; + }; + const baseGrid = kindGrid(baseTitle, BASE_PRICE_KINDS); + const specGrid = kindGrid(specTitle, SPEC_KINDS); const overridesButton = el("button", { className: "z01-master__base z01-master__base--wide", attrs: { type: "button" }, @@ -113,7 +122,11 @@ export function buildSidePanel(groups: MasterGroup[], view: RowsView): HTMLEleme tree.append(groupBox); } - panel.append(section(baseTitle, baseGrid), section(L("Z01_MasterData_Tree"), tree)); + panel.append( + section(baseTitle, baseGrid), + section(specTitle, specGrid), + section(L("Z01_MasterData_Tree"), tree), + ); attachCollapsible(panel); return panel; } diff --git a/ui_template/ui_template_locale_b3.ts b/ui_template/ui_template_locale_b3.ts index 662fc14c..866bf245 100644 --- a/ui_template/ui_template_locale_b3.ts +++ b/ui_template/ui_template_locale_b3.ts @@ -147,6 +147,17 @@ export const ui_locales_b3 = { Z01_MasterData_Base_material: ["자재단가", "Material"], Z01_MasterData_Base_oil: ["유가", "Fuel"], Z01_MasterData_Base_rate: ["요율", "Rates"], + /* ② 품셈 기준 아홉 — 얼마나 드나(품셈이 정하고 해마다 한 번 개정) */ + Z01_MasterData_SpecValues: ["품셈 기준", "Standard rates"], + Z01_MasterData_Base_coef: ["토량환산", "Soil conversion"], + Z01_MasterData_Base_material_surcharge: ["자재할증", "Material surcharge"], + Z01_MasterData_Base_formwork_reuse: ["거푸집", "Formwork reuse"], + Z01_MasterData_Base_rebar_complexity: ["철근", "Rebar"], + Z01_MasterData_Base_timber_structure_class: ["목구조", "Timber structure"], + Z01_MasterData_Base_masonry_slope: ["돌쌓기 경사", "Masonry slope"], + Z01_MasterData_Base_masonry_back_length: ["뒷길이", "Back length"], + Z01_MasterData_Base_stone_kind: ["돌종류", "Stone kind"], + Z01_MasterData_Base_masonry_class: ["갈래", "Masonry class"], Z01_MasterData_Overrides: ["고친 것 모아 보기", "Edited values"], Z01_MasterData_Edit_Can: [ "고칠 수 있는 칸 — 눌러 고치고 Enter 로 저장 (Esc 취소)",