/* ============================================================================= * B08_Quantity_UI_Summary.ts * 1탭 구조물 집계표(PLAN 61-1) — 줄 = 측점 · 열 = 저장된 구조물 조합 · 맨 아래 계 줄. * 표는 공용 많은 자료 표(`ui_template_data_table` · PLAN 70-2) — 머리 · 측점 열 고정. * * 구조물 = GET …/route/structures · 배관 = GET …/drainage/pipe-points (읽기만) * 머리 두 줄 = 조합 구분(경로 첫 칸) · 조합 이름(나머지 경로) — 사본 manifest 경로(집계 응답 `linked`) * 조합 · 측점 = 프로젝트 안 사본이 있는 것만(`linkedOnly` · PLAN 143-1) — 미연결 시설은 그 측점 * 줄만 측점 칸 왼쪽 노랑 램프(값 빈칸 · 서버 `unlinked` · PLAN 154-1 · 198-1) * · 램프 호버(공용 `ui_template_hover_info`) = 그 측점 빈 조합 칸(160-2 · 화면만) * · 시설이 모두 조합에 이은 측점 줄 = 같은 자리 초록 램프(호버 「모두 연결됨」 · 247-1) * 횡단배수 열 · 칸 = GET …/quantity/summary(서버 단독 · PLAN 98 · 120) — 머리 구조물 · 형식 · 규격 * 3단 + 단위 줄 · 「관 공」 맨 윗 머리 = 관에 이은 조합 경로(있으면) · 서버가 펼친 종류(`types`)는 * 조합 열에서 뺌 · 다른 구조물 열은 조합 그대로 * 열 폭 = 맨 아래 단 글(+ 칸 값)만 잼 · 위 단 머리는 줄바꿈(`B08_Quantity_UI_Page.css` · 120-2) * 줄을 누르면 그 측점의 구조물도 모달(PLAN 95-3 · `B08_Quantity_UI_Drawing` 은 누를 때 `import()`) * 미연결 시설 = 모달 맨 위 「조합 연결」 → 바로 저장 → 탭 몸통을 새로 그림(154-2) * 커플링 열 = 좌측 패널 1본 길이로 바로 다시 셈(관마다 길이 `pipes` · `recountCouplings` · PLAN 122) * 자료 읽기 · 표 짜기 = `B08_Quantity_Summary_Table`(화면 없음 · 인쇄 미리보기도 씀 · 302-3) * ========================================================================== */ import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale"; import { createCard, showToast } from "@ui/ui_template_elements"; import { createDataTable } from "@ui/ui_template_data_table"; import { attachHoverInfo } from "@ui/ui_template_hover_info"; import { combosAt, unlinkedAt } from "./B08_Quantity_Summary_Data"; import { LAMP_CLASS, loadSummary, summaryTable, type SummaryView, } from "./B08_Quantity_Summary_Table"; import type { SheetsRequest } from "./B08_Quantity_UI_Drawing"; import { footNote } from "./B08_Quantity_UI_Foot"; import { onOptionsChange } from "./B08_Quantity_UI_Options"; 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; } function buildTable( view: SummaryView, onRowClick: (station: number, label: string) => void, ): HTMLElement { const { options, labels, missing } = summaryTable(view); const table = createDataTable({ ...options, onRowClick: (i) => onRowClick(view.stations[i], labels[i]), fillViewport: true, }); table.root.dataset.b08Summary = "table"; // 줄을 다시 그려도(보이는 줄만) 떠 있는 root 하나에 걸어 둠 — 줄 번호 = 줄 `data-i` attachHoverInfo( table.root, (cell) => { const why = missing[Number(cell.closest("tr")?.dataset.i)]; return why ? `${L("B08_Quantity_Summary_LampTip")} — ${why}` : L("B08_Quantity_Summary_LampOk"); }, `td.${LAMP_CLASS}`, ); return table.root; } /** 행 누름 — 카드 · CAD 묶음은 이때 처음 받음. */ async function openSheets(request: SheetsRequest): Promise { try { const { openStructureSheets } = await import("./B08_Quantity_UI_Drawing"); openStructureSheets(request); } catch (error) { showToast(`${L("B08_Quantity_Drawing_Failed")} — ${String(error)}`, "error"); } } /** 탭 몸통을 그림 — 불러오는 동안 한 줄 · 실패하면 까닭 한 줄. */ export async function renderStructureSummary(host: HTMLElement, projectId: string): Promise { host.replaceChildren(note(L("B08_Quantity_Summary_Loading"))); try { const view = await loadSummary(projectId); const { stations, things, linked, describe, saved } = view; const summary = view.live(); const parts: HTMLElement[] = []; if (view.error) parts.push(note(`${L("B08_Quantity_Summary_Failed")} — ${view.error}`)); if (summary?.notes.length) parts.push(footNote(summary.notes.join(" · "))); // 236-1 한 줄 if (!view.columns.length && !summary?.columns.length) parts.push(note(L("B08_Quantity_Summary_NoCombo"))); const draw = (): HTMLElement => stations.length ? buildTable(view, (station, label) => openSheets({ projectId, station: label, combos: combosAt(things, station), linked, describe, facilities: unlinkedAt(saved?.facilities ?? [], station), // 새 몸통에 다시 그림 — 옛 몸통은 떨어져 옵션 듣기가 스스로 빠짐(겹침 없음) onLinked: () => { const fresh = document.createElement("div"); host.replaceWith(fresh); void renderStructureSummary(fresh, projectId); }, }), ) : note(L("B08_Quantity_Summary_NoStation")); let table = draw(); host.replaceChildren(createCard({ body: [table, ...parts] })); // 공용 카드 안(토공 표와 같은 꼴 · 143-3) · 안내 · 경고 = 표 아래(202-1) onOptionsChange(host, () => { const next = draw(); table.replaceWith(next); table = next; }); } catch (error) { const reason = error instanceof Error ? error.message : String(error); host.replaceChildren(note(`${L("B08_Quantity_Summary_Failed")} — ${reason}`)); } }