/* ============================================================================= * B08_Quantity_UI_Drawing.ts * 구조물 집계표 행 → 구조물도 모달(PLAN 95-3) — 그 측점에 놓인 조합 전부를 세로로 쌓음. * 조합 한 개 = M02 항목 카드(`M02_MasterTemplete_ItemCards`) 그대로 — 도면(도각 포함 · 보기 전용 CAD · * 여러 장이면 장 탭) + 면적산출. * * 원천 = 그 구조물의 프로젝트 복사본(같은 항목일 때) · 없으면 M02 원본 층 — 읽기만 * 조합 목록 = 집계표와 같음(`B08_Quantity_Summary_Data` `combosAt`) * 카드 · CAD · 스프레드시트는 모달을 열 때만 `import()` · 닫을 때 카드를 모두 정리 * ========================================================================== */ import "./B08_Quantity_UI_Drawing.css"; import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale"; import { openModal } from "@ui/ui_template_modal"; import { layerOf, type PickerRow } from "@ui/ui_template_item_picker_data"; import { PART_OF, projectItems, sameItem, structureFolder, type ProjectItemInfo, } from "@ui/ui_template_item_picker_project"; import { readTemplate } from "../M02_MasterTemplete/M02_MasterTemplete_Api_Fetch"; import type { ItemCardsHandle } from "../M02_MasterTemplete/M02_MasterTemplete_ItemCards"; import type { ItemSource } from "../M02_MasterTemplete/M02_MasterTemplete_ItemSource"; import { summaryTooltip, type SummaryItem } from "../common_util/common_util_summary_item"; import type { ComboLabel, StationCombo } from "./B08_Quantity_Summary_Data"; function L(key: keyof typeof ui_locales): string { return ui_locales[key][currentLanguageIndex]; } function note(text: string): HTMLElement { const node = document.createElement("p"); node.className = "ui-general-block__empty"; node.textContent = text; return node; } export interface SheetsRequest { projectId: string; /** 모달 제목에 붙는 측점 글 */ station: string; combos: StationCombo[]; /** `comboId` → M02 줄 */ rows: Map; describe: (item: SummaryItem) => ComboLabel | null; } /** 그 구조물의 복사본 목록 — 프로젝트 복사본을 못 읽으면 원본만(모달은 그대로 열림). */ const loadCopies = (projectId: string): Promise => projectItems(projectId) .list() .catch(() => []); function sourceOf( request: SheetsRequest, combo: StationCombo, row: PickerRow, copies: ProjectItemInfo[], ): { source: ItemSource; copy: boolean } { const own = combo.structureId ? copies.find((info) => info.폴더 === structureFolder(combo.structureId!)) : undefined; if (own && sameItem(own, row)) { const items = projectItems(request.projectId); return { copy: true, source: { layer: "project", projectId: request.projectId, read: async (part) => { const doc = await items.read(own.폴더, PART_OF[part]); if (doc == null) throw new Error(L("B08_Quantity_Sheets_NoCopy")); return { 문서: doc }; }, }, }; } const layer = layerOf(row); return { copy: false, source: { layer, projectId: null, read: (part, column) => readTemplate(layer, part, column, null), }, }; } /** 조합 한 칸 — 제목줄(이름 · 원천) + 안내 + 카드. */ function comboSection( request: SheetsRequest, combo: StationCombo, copies: ProjectItemInfo[], createItemCards: typeof import("../M02_MasterTemplete/M02_MasterTemplete_ItemCards").createItemCards, handles: ItemCardsHandle[], ): HTMLElement { const label = request.describe(combo.item); const name = label ? [label.group, label.name].filter(Boolean).join(" ") : combo.item.name || combo.item.key; const row = request.rows.get(combo.id) ?? null; const section = document.createElement("section"); section.className = "b08-sheets__combo"; section.dataset.b08Combo = combo.id; const title = document.createElement("h4"); title.className = "b08-sheets__title"; title.textContent = name; title.title = summaryTooltip(combo.item); section.append(title); if (!row?.열) { section.append(note(L("B08_Quantity_Drawing_Missing"))); return section; } const picked = sourceOf(request, combo, row, copies); const from = document.createElement("span"); from.className = "b08-sheets__from"; from.textContent = L( picked.copy ? "B08_Quantity_Sheets_FromCopy" : "B08_Quantity_Sheets_FromMaster", ); title.append(from); if (!row.도번) section.append(note(L("B08_Quantity_Drawing_NoDrawing"))); const cards = createItemCards({ source: () => picked.source, editable: () => false, onEdit: () => undefined, warnOnFail: () => false, // 못 읽으면 카드에 「없음」 }); handles.push(cards); section.append(cards.root); void cards.load({ id: row.열, label: name }); return section; } /** 모달을 엶 — 조합이 없으면 안내 한 줄 · 불러오다 실패하면 까닭 한 줄. */ export function openStructureSheets(request: SheetsRequest): void { const handles: ItemCardsHandle[] = []; let closed = false; openModal({ title: `${request.station} ${L("B08_Quantity_Sheets_Title")}`, closeLabel: L("Common_Btn_Close"), dialogClass: "b08-sheets", beforeClose: () => { closed = true; // 아직 불러오는 중이면 뒤늦게 카드를 만들지 않음 handles.forEach((cards) => cards.destroy()); return true; }, mount: (body) => { if (!request.combos.length) return void body.append(note(L("B08_Quantity_Summary_NoCombo"))); body.append(note(L("B08_Quantity_Drawing_Loading"))); void (async () => { try { const [{ createItemCards }, copies] = await Promise.all([ import("../M02_MasterTemplete/M02_MasterTemplete_ItemCards"), loadCopies(request.projectId), ]); if (closed) return; body.replaceChildren( ...request.combos.map((combo) => comboSection(request, combo, copies, createItemCards, handles), ), ); } catch (error) { if (closed) return; const reason = error instanceof Error ? error.message : String(error); body.replaceChildren(note(`${L("B08_Quantity_Drawing_Failed")} — ${reason}`)); } })(); }, }); }