From 155d1eefbec547536efdb7a7cc8f3069248fd502 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 29 Sep 2026 08:40:09 +0900 Subject: [PATCH] =?UTF-8?q?feat(M02):=20=EC=A2=8C=EC=B8=A1=20=ED=8C=A8?= =?UTF-8?q?=EB=84=90=20=EC=9E=AC=EA=B5=AC=EC=84=B1=20=E2=80=94=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=EB=AC=BC=20=EC=A7=91=EA=B3=84=20=EC=96=91=EC=8B=9D=20?= =?UTF-8?q?=EC=95=84=EB=9E=98=20=ED=95=98=EC=9C=84=20=EB=84=B7=20=C2=B7=20?= =?UTF-8?q?=EC=B2=AB=20=ED=99=94=EB=A9=B4=20=EA=B5=AC=EC=A1=B0=EB=AC=BC?= =?UTF-8?q?=EC=A7=91=EA=B3=84=ED=91=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 「표 양식」 → 「구조물 집계 양식」 · 구조물집계표 줄 하나만 · 새로 · 본떠 만들기 · 지우기 단추 삭제 - 구조물 도면 · 면적산출 · 재료산출 · 자재 조합 컨테이너를 그 아래 하위로(들여 씀 · 같이 접힘) · 도면 양식은 별도 최상위 - 들어오면 구조물집계표를 엶 · 양식 지움 · 자재 조합 상세 닫음 뒤에도 빈 화면 대신 구조물집계표 - 표 새 양식 틀 · 표 복사 머리 이름 바꿈(이제 안 쓰는 길) 걷어냄 · 시험 고침 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019Kyw9qnkJ94pC1A4PfpDtA --- .../M02_MasterTemplete_UI_Page.ts | 13 +++- .../M02_MasterTemplete_UI_Side.ts | 64 +++++++++---------- .../M02_MasterTemplete_UI_Style.css | 5 ++ resources/tester/test_m02_structure_panel.py | 18 +++++- ui_template/ui_template_locale_m2.ts | 2 +- 5 files changed, 62 insertions(+), 40 deletions(-) diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts index 51033c77..0f24cc71 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts @@ -10,7 +10,8 @@ import { el } from "@ui/ui_template_elements"; import { t as L } from "@ui/ui_template_locale"; import { createWorkflowOverlays } from "@ui/ui_template_overlay"; import { fetchSessionUser } from "../A06_Login/A06_Login_Api_Fetch"; -import { createMain } from "./M02_MasterTemplete_UI_Main"; +import { STRUCTURE_TABLE } from "./M02_MasterTemplete_Structure"; +import { createMain, type Selection } from "./M02_MasterTemplete_UI_Main"; import { buildSide } from "./M02_MasterTemplete_UI_Side"; import "./M02_MasterTemplete_UI_Style.css"; @@ -22,9 +23,16 @@ export async function renderM02MasterTemplate(root: HTMLElement): Promise () => void side.refresh(), () => side.paint(), ); + // 첫 화면 · 닫은 뒤(양식 지움 · 자재 조합 상세 닫음) = 구조물집계표 — 빈 화면 없음 + const home: Selection = { + layer: "system", + projectId: null, + kind: "table", + name: STRUCTURE_TABLE, + }; const side = buildSide({ isAdmin, - onOpen: (sel) => void main.open(sel), + onOpen: (sel) => void main.open(sel ?? home), getOpen: main.current, confirmLeave: main.confirmLeave, showView: main.showView, @@ -46,4 +54,5 @@ export async function renderM02MasterTemplate(root: HTMLElement): Promise ); root.innerHTML = ""; root.append(layout); + void main.open(home); } diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts index 8461ec8f..18121691 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts @@ -1,9 +1,10 @@ /* ============================================================================= * M02_MasterTemplete_UI_Side.ts - * 마스터 템플릿 좌측 패널 — 시스템 양식 목록(표/도면/구조물 도면/면적산출/재료산출) · 새로 · 본떠 · 지우기. - * 서버 길은 계약 `6_계약.md`. 구조물 도면 · 면적산출 · 재료산출 줄은 집계표 구조물 열(꼴=수)마다 하나씩 — + * 마스터 템플릿 좌측 패널 — 「구조물 집계 양식」(구조물집계표 줄 하나 + 하위 구조물 도면 · 면적산출 · + * 재료산출 · 자재 조합) · 「도면 양식」(새로 · 본떠 · 지우기). 서버 길은 계약 `6_계약.md`. + * 구조물 도면 · 면적산출 · 재료산출 줄은 집계표 구조물 열(꼴=수)마다 하나씩 — * 서버가 미리 만들어 둠(도번 SD · 면적번 SA · 재료번 SM + 같은 8자리) · - * 줄 글은 번호가 맨 앞(「SD01010100 돌기슭막이 …」). 맨 아래 「자재 조합」 컨테이너는 `M02_MasterTemplete_Matset.ts`. + * 줄 글은 번호가 맨 앞(「SD01010100 돌기슭막이 …」). 「자재 조합」 컨테이너는 `M02_MasterTemplete_Matset.ts`. * ========================================================================== */ import { @@ -48,27 +49,14 @@ const COLUMN_KINDS: { kind: ColumnKind; key: keyof StructureNumbers; label: () = { kind: "area", key: "면적번", label: () => L("M02_KindArea") }, { kind: "material", key: "재료번", label: () => L("M02_KindMaterial") }, ]; -/** 새 양식의 첫 문서 — 표는 `3_표양식.md` 4장 틀 · 도면은 빈 웹캐드 문서 */ -function stubDoc(kind: Kind, name: string): unknown { - return kind === "table" - ? { - 양식: name, - 종류: "표", - 판: 1, - 층: ["종류", "공법", "규격"], - 열: [{ id: "sta", 머리: ["측점", null, null], 단위: null, 꼴: "글", 고정: true }], - 줄: [], - 합계줄: [], - 보기: {}, - } - : { - format: 6, - entities: [], - // 기본 도면층 하나 — 없으면 B07 CAD 가 못 엶("Cannot read properties of - // undefined (reading 'length')", 2026-09-27 실측) - layers: [{ id: "기본", name: "기본", isVisible: true, isLocked: false }], - }; -} +/** 새 도면 양식의 첫 문서 — 빈 웹캐드 문서 */ +const STUB_DRAWING = { + format: 6, + entities: [], + // 기본 도면층 하나 — 없으면 B07 CAD 가 못 엶("Cannot read properties of + // undefined (reading 'length')", 2026-09-27 실측) + layers: [{ id: "기본", name: "기본", isVisible: true, isLocked: false }], +}; export interface SideOptions { isAdmin: boolean; @@ -116,7 +104,7 @@ export function buildSide(opt: SideOptions): SideHandle { const columnBodies = COLUMN_KINDS.map(() => el("div", { className: "m02-side__items ui-sidebar-list" }), ); - /** 새로 · 본떠 만들기 · 지우기 — 표 · 도면 컨테이너 안에 하나씩(그 종류로 동작) · 관리자만 */ + /** 새로 · 본떠 만들기 · 지우기 — 도면 양식 컨테이너 안(구조물 집계 양식은 집계표 하나라 없음) · 관리자만 */ const editButtons = new Map(); const actionRow = (kind: Kind): HTMLElement[] => { if (!canEdit(layer, opt.isAdmin)) return []; @@ -141,13 +129,20 @@ export function buildSide(opt: SideOptions): SideHandle { matsetSection.querySelector(".ui-collapsible__title")?.addEventListener("click", () => { if (matsetSection.classList.contains("is-collapsed")) void matset.refresh(); }); + // 구조물 집계 양식 — 집계표 줄 아래 하위 컨테이너 넷(들여 · 같이 접힘) · 처음부터 펼침(집계표가 열려 있음) + const summary = section( + L("M02_KindTable"), + bodies.get("table")!, + ...COLUMN_KINDS.map((k, i) => section(k.label(), columnBodies[i])), + matsetSection, + ); + summary.classList.remove("is-collapsed"); + summary.classList.add("m02-side__summary"); const listHost = el("div", { className: "m02-side__lists ui-sidebar-panel", children: [ - section(L("M02_KindTable"), bodies.get("table")!, ...actionRow("table")), + summary, section(L("M02_KindDrawing"), bodies.get("drawing")!, ...actionRow("drawing")), - ...COLUMN_KINDS.map((k, i) => section(k.label(), columnBodies[i])), - matsetSection, ], }); attachCollapsible(listHost); @@ -169,7 +164,10 @@ export function buildSide(opt: SideOptions): SideHandle { function paintList(): void { for (const [kind, body] of bodies) { - const mine = items.filter((i) => i.종류 === kind); + // 표는 구조물집계표 한 줄만(다른 표 양식은 안 보임) + const mine = items.filter( + (i) => i.종류 === kind && (kind !== "table" || i.이름 === STRUCTURE_TABLE), + ); body.replaceChildren( ...(mine.length ? mine.map((info) => { @@ -265,7 +263,7 @@ export function buildSide(opt: SideOptions): SideHandle { await Promise.all([refreshStructure(), matset.refresh()]); } - /* --- 시스템 층 새로 · 본떠 · 지우기(표 · 도면만 — 구조물 도면은 집계표 열이 정함) --- */ + /* --- 시스템 층 새로 · 본떠 · 지우기(도면 양식만 — 구조물 도면은 집계표 열이 정함) --- */ const nameDialog = (title: string, onOk: (name: string) => Promise): void => { const name = createInputField({ label: L("M02_Name"), placeholder: L("M02_Name") }); openModal({ @@ -307,7 +305,7 @@ export function buildSide(opt: SideOptions): SideHandle { /** 이 컨테이너 종류의 새 양식 — 종류 고르기 없이 이름만 */ function newTemplate(kind: Kind): void { nameDialog(L("M02_NewTitle"), async (name) => { - await saveTemplate("system", kind, name, null, "", stubDoc(kind, name)); + await saveTemplate("system", kind, name, null, "", STUB_DRAWING); await openCreated(kind, name); }); } @@ -316,9 +314,7 @@ export function buildSide(opt: SideOptions): SideHandle { if (!from) return; nameDialog(L("M02_CopyTitle"), async (name) => { const got = await readTemplate("system", from.종류, from.이름, null); - // 표 문서 머리 이름(`양식`)도 새 이름으로 — 안 바꾸면 복사본이 원본 이름을 달고 있음 - const doc = from.종류 === "table" ? { ...(got.문서 as object), 양식: name } : got.문서; - await saveTemplate("system", from.종류, name, null, "", doc); + await saveTemplate("system", from.종류, name, null, "", got.문서); await openCreated(from.종류, name); }); } diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css b/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css index edd91047..ac566224 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css @@ -10,6 +10,11 @@ font-size: var(--text-body-sm); } +/* 구조물 집계 양식 하위 컨테이너(구조물 도면 · 면적산출 · 재료산출 · 자재 조합) — 들여 씀 */ +.m02-side__summary > .ui-sidebar-card { + margin-left: var(--spacing-8); +} + .m02-side__row { display: flex; flex-wrap: wrap; diff --git a/resources/tester/test_m02_structure_panel.py b/resources/tester/test_m02_structure_panel.py index 80733940..4e291b60 100644 --- a/resources/tester/test_m02_structure_panel.py +++ b/resources/tester/test_m02_structure_panel.py @@ -140,12 +140,24 @@ def test_미리보기_CAD_패널은_숨기지_않고_접힌_채로_엶(): assert "isInspectorCollapsed()" in _text(cad / "components/Toolbar.tsx") -def test_좌측_새로_본떠_지우기는_표_도면_컨테이너_안에_하나씩(): +def test_좌측_구조물_집계_양식_아래_하위_넷_새로_본떠_지우기는_도면_양식만(): side = _text(SIDE) - assert 'actionRow("table")' in side and 'actionRow("drawing")' in side + assert 'actionRow("table")' not in side and 'actionRow("drawing")' in side assert "createSelectField" not in side # 새로 = 종류 고르기 없이 이름만 assert "cur?.종류 !== kind" in side # 본떠 · 지우기 = 그 컨테이너에서 고른 것이 있을 때만 - assert "양식: name" in side.split("function copyTemplate")[1] # 복사본 머리 이름 = 새 이름 + assert "i.이름 === STRUCTURE_TABLE" in side # 표는 구조물집계표 한 줄만 + # 구조물 도면 · 면적산출 · 재료산출 · 자재 조합 = 구조물 집계 양식 컨테이너 안(같이 접힘) + summary = side.split("const summary = section(")[1].split(");")[0] + assert "COLUMN_KINDS.map" in summary and "matsetSection" in summary + assert 'M02_KindTable: ["구조물 집계 양식"' in _text( + ROOT / "ui_template" / "ui_template_locale_m2.ts" + ) + + +def test_첫_화면과_닫은_뒤는_구조물집계표(): + page = _text(M02 / "M02_MasterTemplete_UI_Page.ts") + assert re.search(r'kind: "table",\s*name: STRUCTURE_TABLE', page) + assert "main.open(sel ?? home)" in page and "void main.open(home);" in page def test_좌측_고름_표시는_열린_것_한_줄뿐(): diff --git a/ui_template/ui_template_locale_m2.ts b/ui_template/ui_template_locale_m2.ts index bb81c0ae..4fc62eab 100644 --- a/ui_template/ui_template_locale_m2.ts +++ b/ui_template/ui_template_locale_m2.ts @@ -6,7 +6,7 @@ export const ui_locales_m2 = { B01_Dashboard_MasterTemplate: ["마스터 템플릿", "Master Templates"], M02_Title: ["마스터 템플릿", "Master Templates"], - M02_KindTable: ["표 양식", "Table templates"], + M02_KindTable: ["구조물 집계 양식", "Structure summary templates"], M02_KindDrawing: ["도면 양식", "Drawing templates"], M02_KindStructure: ["구조물 도면", "Structure drawings"], M02_KindArea: ["면적산출", "Area takeoff"],