feat(M02): 일위대가 라이브러리 화면 — 좌측 「일위대가 조합」 컨테이너 · 뿌리 셋 나무 · 조합 편집 · M01 컨테이너 뺌(PLAN 27-2b · 27-2c 화면)

- 좌측 「일위대가 조합」(구조물 조합 위) → 메인 칸 왼쪽 뿌리 셋 + 조합 나무(폴더 [구분, 상세구분] 깊이 2 · TreePanel 공용) · 오른쪽 목록 · 상세(옛 M01 조합 화면 옮김 · Labor_Edit)
- 나무 오른쪽 클릭: 폴더 더하기(2 단까지) · 조합 더하기(그 폴더 채운 빈 조합) · 이름 · 옮기기 · 지우기 · 조합 이름 바꾸기 서버 길 더함
- 해시 open=일위대가 로 진입 · 다른 뿌리를 누르면 그 뿌리 일위대가로
- 읽기만 뿌리는 저장 · 지우기 · 로직 더하기 없음
- TreePanel 에 maxDepth · item(부르는 말 · 새 항목 화면) 선택지 더함(구조물 라이브러리는 그대로)
- 조합 고르기(면적산출 옆 목록 · 일위대가 고르기)가 이 층이 쓸 수 있는 세 층 조합 · 알약은 /api/m02/combos/key/{키}/inputs
- M01 좌측 · 페이지에서 일위대가 조합 컨테이너 뺌
- 시험 test_m02_labor_tree(새) · test_m02_combo_layers 더함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BU9gbNE3aWFUvE61pS2WGt
This commit is contained in:
2026-09-30 13:35:38 +09:00
co-authored by Claude Opus 5.5
parent ca946ebbf1
commit c8686ee3ad
17 changed files with 572 additions and 268 deletions
@@ -23,8 +23,10 @@ import {
entryQuery,
layerOfRoot,
ownOwner,
type OpenTarget,
type Root,
} from "./M02_MasterTemplete_Layer";
import { buildLaborLibrary, buildLaborSection } from "./M02_MasterTemplete_Labor";
import { STRUCTURE_TABLE } from "./M02_MasterTemplete_Structure";
import { createTreePanel } from "./M02_MasterTemplete_TreePanel";
import { createLibraryRoots } from "./M02_MasterTemplete_TreePanel_Roots";
@@ -55,11 +57,26 @@ export async function renderM02MasterTemplate(root: HTMLElement): Promise<Mounte
settle: () => main.settle(),
onChanged: (colId) => main.openTable(layer, colId, true),
});
const go = async (next: Root, nextOwner: string | null): Promise<void> => {
const go = async (
next: Root,
nextOwner: string | null,
open: OpenTarget = "구조물조합",
): Promise<void> => {
// 같은 주소 안 이동은 머리 가드가 안 물음 — 저장 안 한 고침은 여기서
if (!(await main.confirmLeave())) return;
navigateTo(ROUTES.M02_MASTER_TEMPLATE, entryQuery({ ...entry, root: next, owner: nextOwner }));
navigateTo(
ROUTES.M02_MASTER_TEMPLATE,
entryQuery({ ...entry, open, root: next, owner: nextOwner }),
);
};
// 일위대가 라이브러리(PLAN 27-2b) — 같은 뿌리 셋 · 다른 뿌리를 누르면 그 뿌리의 일위대가로 다시 엶
const laborRoots = (panel: HTMLElement): HTMLElement =>
createLibraryRoots({
user,
active: { root: entry.root, owner },
panel,
go: (next, nextOwner) => void go(next, nextOwner, "일위대가"),
});
const roots = createLibraryRoots({
user,
active: { root: entry.root, owner },
@@ -97,6 +114,14 @@ export async function renderM02MasterTemplate(root: HTMLElement): Promise<Mounte
getOpen: main.current,
confirmLeave: main.confirmLeave,
showView: main.showView,
labor: ({ onOpen }) =>
buildLaborSection({
show: main.showView,
confirmLeave: main.confirmLeave,
onOpen,
make: () =>
buildLaborLibrary({ scope: { layer, owner }, canEdit: isAdmin, roots: laborRoots }),
}),
});
const layout = el("div", { className: "ui-workflow-layout m02-master" });
@@ -116,9 +141,9 @@ export async function renderM02MasterTemplate(root: HTMLElement): Promise<Mounte
root.innerHTML = "";
root.append(layout);
void main.open(home);
// 진입 해시 `open` — 옵션 · 도면 컨테이너는 마스터에만(일위대가는 27-2 컨테이너가 생기면) ·
// 옵션 화면이 뒤에 열리면 편집 칸 차례(`seq`)가 늦게 온 집계표를 버림
if (entry.open === "옵션" || entry.open === "도면") side.reveal(entry.open);
// 진입 해시 `open` — 옵션 · 도면 컨테이너는 마스터에만 · 일위대가는 세 뿌리 ·
// 옵션 · 일위대가 화면이 뒤에 열리면 편집 칸 차례(`seq`)가 늦게 온 집계표를 버림
if (entry.open !== "구조물조합") side.reveal(entry.open);
void tree.refresh();
// 이 화면을 떠나면 층 길을 시스템 · 자기 층으로 되돌림(다른 화면의 항목 고르기가 남의 층을 안 부르게)
return { covers: () => false, show: () => {}, dispose: () => setActiveLayer("system") };