/* ============================================================================= * M02_MasterTemplete_Labor_View.ts * 일위대가 보기 갈래(39-3 · 고치기 없음) — 그 열 일위대가의 담은 로직 표 + 자재목록 표(기준 · 반영). * 면적산출 상세 오른쪽 패널(sub3) · 구조물 조합 일위대가 카드(sub1)가 씀. 표 모양은 상세와 한 벌(`Labor_Rows`). * · `show(층, 열, 수량?)` = 문서 · 로직 입력 목록 · calc 를 받아 그림(최신 응답만) * · `setQty({알약 키: 칸 값})` = 면적 칸 값이 바뀌면 서버 없이 짝 셈(`Labor_Calc.settle`)으로 바로 곱함 * (그 열 면적 알약 값 전부를 줌 — 빠진 키는 「수량 없음」) * · `onRow` = 줄 누름(알약 키 · 이름 · 단위 — 칸 지정 모달 등) · 없으면 누름 없음 * ========================================================================== */ import { el } from "@ui/ui_template_elements"; import { readTemplate, type ItemLogic, type Layer } from "./M02_MasterTemplete_Api_Fetch"; import { areaLinks, calcLabor, fetchLogicInputs, normalizeLabor, type LaborCalc, type LaborDoc, } from "./M02_MasterTemplete_Labor_Api"; import { settle } from "./M02_MasterTemplete_Labor_Calc"; import { logicTable, matTable } from "./M02_MasterTemplete_Labor_Rows"; import { tc } from "./M02_MasterTemplete_Labor_Text"; import "./M02_MasterTemplete_Labor.css"; export interface LaborViewRow { kind: "logic" | "material"; /** 로직 키 · 자재 코드 */ key: string; /** 면적산출 알약 키(로직 「이름|단위」 · 자재 「자재|MT키」) — calc 전이면 null */ pill: string | null; name: string; unit: string; } export interface LaborViewHandle { root: HTMLElement; /** 그 열 일위대가를 읽어 그림 — `수량` = 저장 안 한 면적 칸 값(없으면 저장된 면적산출) */ show: (layer: Layer, column: string | null, 수량?: Record) => Promise; /** 면적 칸 값이 바뀜(알약 값 전부) — 받은 기준에 바로 곱함(서버 안 부름) */ setQty: (수량: Record) => void; /** 지금 문서(없으면 null) */ doc: () => LaborDoc | null; destroy: () => void; } export function createLaborView(onRow?: (row: LaborViewRow) => void): LaborViewHandle { const root = el("div", { className: "m02-labview" }); const logics = new Map(); let doc: LaborDoc | null = null; let calc: LaborCalc | null = null; let fail: string | null = null; let cells = new Map(); let seq = 0; const paint = (): void => { if (!doc) return; const d = doc; const pick = (row: LaborViewRow): (() => void) | undefined => onRow ? () => onRow(row) : undefined; const logicOpen = (i: number): void => { const row = d.담은로직[i]; const line = calc?.줄.find((l) => l.로직 === row.로직); pick({ kind: "logic", key: row.로직, pill: line?.알약 ?? null, name: logics.get(row.로직)?.이름 ?? row.로직, unit: line?.분모 ?? "", })?.(); }; const matOpen = (i: number): void => { const mat = d.자재[i]; const line = calc?.자재.find((m) => m.코드 === mat.코드); pick({ kind: "material", key: mat.코드, pill: line?.알약 ?? null, name: mat.이름 ?? mat.코드, unit: mat.단위 ?? "", })?.(); }; root.replaceChildren( el("h4", { className: "m02-labview__title", text: tc("Logics_Title") }), logicTable( { rows: d.담은로직, logics, calc, fail, cells }, onRow ? { open: logicOpen } : undefined, ), el("h4", { className: "m02-labview__title", text: tc("Labor_Mats") }), matTable({ mats: d.자재, calc, fail, cells }, onRow ? { open: matOpen } : undefined), ); }; const show = async ( layer: Layer, column: string | null, 수량?: Record, ): Promise => { const mine = ++seq; doc = null; calc = null; fail = null; if (!column) return void root.replaceChildren(); root.replaceChildren(el("p", { className: "m02-labrow__muted", text: tc("Loading") })); try { const got = await readTemplate(layer, "labor", column, null); if (mine !== seq) return; doc = normalizeLabor(got.문서, column); const want = doc.담은로직.map((r) => r.로직).filter((k) => !logics.has(k)); const [inputs, area] = await Promise.all([ fetchLogicInputs(want), readTemplate(layer, "area", column, null).then( (a) => a.문서, () => null, ), ]); if (mine !== seq) return; for (const l of inputs.로직) logics.set(l.로직, l); cells = new Map([...areaLinks(area)].map(([k, v]) => [k, v.칸])); paint(); // 금액 「…」 calc = await calcLabor(layer, column, null, 수량); } catch (error) { if (mine !== seq) return; if (!doc) { root.replaceChildren( el("p", { className: "m02-labrow__empty", text: error instanceof Error ? error.message : tc("Load_Failed"), }), ); return; } fail = error instanceof Error ? error.message : tc("Labor_Calc_Fail"); } if (mine === seq) paint(); }; return { root, show, setQty: (수량) => { if (!doc || !calc) return; calc = settle(calc, doc, 수량); paint(); }, doc: () => doc, destroy: () => { seq += 1; root.remove(); }, }; }