/* ============================================================================= * Z01_MasterData_UI_Side.ts * 좌측 패널 — B03~B09 와 같은 도킹 사이드바 안에 접히는 상자 둘(기초단가 · 마스터 트리) * * 상자 겉모습·접기는 공용(`ui-collapsible ui-sidebar-section`) · 누른 단추 하나만 강조 * (두 상자를 가로질러 한 개) (2026-09-15 브레인 — 좌측 패널 양식 갈음 · 기초단가 칸). * ========================================================================== */ import { attachCollapsible } from "@ui/ui_template_collapsible"; import { el } from "@ui/ui_template_elements"; import { t as L } from "@ui/ui_template_locale"; import { fetchBasePrices, fetchMasterRows, resetBasePrices, saveBasePrices, type MasterGroup, VALUES_TABLE_ID, type WorkItemAxis, } from "./Z01_MasterData_Api_Fetch"; import { groupKinds, type ServedKind } from "./Z01_MasterData_UI_Cells"; import type { RowsView } from "./Z01_MasterData_UI_Rows"; const AXES: WorkItemAxis[] = ["forest", "const"]; export function buildSidePanel( groups: MasterGroup[], kinds: ServedKind[], view: RowsView, openWorkItems: (axis: WorkItemAxis) => void, ): HTMLElement { const panel = el("div", { className: "z01-master__side" }); const activate = (button: HTMLElement): void => { panel.querySelector(".is-active")?.classList.remove("is-active"); button.classList.add("is-active"); }; // 갈래·kind·이름·줄 수 모두 서버 것 — 화면은 목록도 이름도 안 가짐(kind 가 늘면 저절로 섬). const boxes = groupKinds(kinds).map((box) => { const grid = el("div", { className: "z01-master__base-grid" }); for (const served of box.items) { const button = el("button", { className: "z01-master__base", attrs: { type: "button" }, children: [ el("span", { text: served.label }), el("span", { className: "z01-master__key", text: served.rows.toLocaleString("ko-KR"), }), ], }); button.addEventListener("click", () => { activate(button); view.show({ title: `${box.label} › ${served.label}`, key: `base-prices/${served.kind}`, load: (query) => fetchBasePrices(served.kind, query), edit: { scope: `base:${served.kind}`, save: (body) => saveBasePrices(served.kind, body), reset: (rowKeys) => resetBasePrices(served.kind, rowKeys), }, }); }); grid.append(button); } return { label: box.label, grid }; }); // 공종 편집 — 구성 줄을 고치고 「입력 필요」 공종을 메움(PLAN 1-4). const workGrid = el("div", { className: "z01-master__base-grid" }); for (const axis of AXES) { const button = el("button", { className: "z01-master__base", attrs: { type: "button" }, text: L(axis === "forest" ? "Z01_WorkItem_Axis_Forest" : "Z01_WorkItem_Axis_Const"), }); button.addEventListener("click", () => { activate(button); openWorkItems(axis); }); workGrid.append(button); } const tree = el("nav", { className: "z01-master__tree" }); for (const group of groups) { const groupBox = folder("z01-master__group", group.label, String(group.files.length)); groupBox.open = true; // 부산물은 계산이 남긴 기록 — 고칠 정본이 아님(2026-09-15 브레인 갈래 나눔표). if (group.key === "byproduct") { groupBox.append( el("p", { className: "z01-master__note", text: L("Z01_MasterData_Byproduct") }), ); } for (const file of group.files) { const fileBox = folder( "z01-master__file", file.label, file.label === file.key ? "" : file.key, ); // 낱값 마디는 표들 밑에 — 누르면 여느 표와 같은 길로 그림(2026-09-15 브레인). const ordered = [...file.tables].sort( (a, b) => Number(a.id === VALUES_TABLE_ID) - Number(b.id === VALUES_TABLE_ID), ); for (const table of ordered) { const button = el("button", { className: "z01-master__table", attrs: { type: "button", title: table.key }, children: [ el("span", { text: table.label }), el("span", { className: "z01-master__key", text: table.row_count.toLocaleString("ko-KR"), }), ], }); button.addEventListener("click", () => { activate(button); view.show({ title: `${file.label} › ${table.label}`, key: `${file.key} / ${table.key}`, load: (query) => fetchMasterRows(file.id, table.id, query), }); }); fileBox.append(button); } groupBox.append(fileBox); } tree.append(groupBox); } panel.append( ...boxes.map((box) => section(box.label, box.grid)), section(L("Z01_WorkItem_Title"), workGrid), section(L("Z01_MasterData_Tree"), tree), ); attachCollapsible(panel); return panel; } function section(title: string, body: HTMLElement): HTMLElement { return el("section", { className: "z01-master__section ui-collapsible ui-sidebar-section", children: [ el("p", { className: "z01-master__section-title ui-collapsible__title", text: title }), body, ], }); } /** 트리 접는 칸 — 브라우저 기본
로 둠. */ function folder(className: string, label: string, aside: string): HTMLDetailsElement { const summary = el("summary", { children: [ el("span", { text: label }), el("span", { className: "z01-master__key", text: aside }), ], }); return el("details", { className, children: [summary] }); }