/* ============================================================================= * B09_Estimation_UI_Tabs_Bill_Link.ts * B09 설계내역서 연결 고르기(166-2 · 184-3) — 있는 호표(일위대가목록표 dg · 단가산출근거목록표 sg) · * M01 로직(새 산근 = 서버 셈 · [저장] 뒤 단가) · 로직 입력값 고치기 · 연결 풀기. * * 줄에 적는 일 = 부르는 쪽 `apply(ref, from)`(`…_UI_Tabs_Bill_Edit` · 저장 꼴 모름) * 로직 고르기 · 입력값 = M02 창 재사용 * ========================================================================== */ import { showToast } from "@ui/ui_template_elements"; import { createFindList } from "@ui/ui_template_find_list"; import { openModal } from "@ui/ui_template_modal"; import { fetchLogicInputs } from "../M02_MasterTemplete/M02_MasterTemplete_Labor_Api"; import { openLaborInput } from "../M02_MasterTemplete/M02_MasterTemplete_Labor_Input"; import { openLogicPicker } from "../M02_MasterTemplete/M02_MasterTemplete_Labor_Pick"; import type { Row } from "./B09_Estimation_Calc_Base"; import type { TabContext } from "./B09_Estimation_UI_Tabs"; /** 연결 적용 — ref = null 이면 풀기 · from = 고른 호표(명칭 · 규격 · 단위 채움) */ export type ApplyRef = (ref: Row | null, from?: Row) => void; export type Leaf = [string, () => void]; /** 있는 호표 고르기 창 — 일위대가목록표(dg) · 단가산출근거목록표(sg) */ function pickHopyo(ctx: TabContext, apply: ApplyRef, tab: "dg" | "sg"): void { const listing: Row[] = (tab === "dg" ? ctx.sheets.daega : ctx.sheets.sg_list) || []; if (!listing.length) return showToast("고를 호표 없음 — 연결된 구조물 조합 사본이 없음", "error"); const label = (r: Row): string => (tab === "dg" ? `대가${r.no}` : `산근 ${r.no}호표`); openModal({ title: tab === "dg" ? "일위대가 호표 고르기" : "단가산출 호표 고르기", closeLabel: "닫기", mount: (body, close) => { const finder = createFindList({ placeholder: "호표 · 명칭 · 규격 찾기" }); const find = (): void => { const q = finder.search.value.trim().toLowerCase(); const found = listing.filter( (r) => !q || `${label(r)} ${r.name} ${r.spec}`.toLowerCase().includes(q), ); finder.hint.textContent = `${found.length} / ${listing.length}`; finder.setRows( found.map((r) => ({ key: String(r.key), tag: label(r), name: String(r.name || ""), sub: String(r.spec || ""), unit: String(r.unit || ""), onPick: () => { close(); apply({ tab, key: r.key }, r); }, })), ); }; finder.search.addEventListener("input", find); find(); body.append(finder.root); requestAnimationFrame(() => finder.search.focus()); }, }); } /** M01 로직 → 입력값 창 → 연결(새 산근 · 단가는 [저장] 뒤 서버 셈) */ function askInputs(apply: ApplyRef, code: string, name: string, old?: Row): void { fetchLogicInputs([code]).then( (got) => { const logic = got.로직[0] ?? null; openLaborInput({ title: `${code} ${name}`.trim(), logic, values: old?.given || {}, memo: null, rate: old?.rate ?? null, readOnly: false, okLabel: "연결", notice: "새 산근 = 단가산출근거 뒤 번호 · 단가는 [저장] 뒤 서버가 셈", done: (next) => { const unit = String(logic?.결과단위 || "") .replace(/^원\//, "") .replace(/^[\d.]+/, ""); const ref = { tab: "logic", code, name, given: next.입력값, rate: next.켜쌓기율 }; apply({ ...ref, picks: old?.picks ?? null }, { name, unit }); showToast("연결함 — 새 산근 단가는 [저장] 뒤에 채워짐", "success"); }, }); }, (error: unknown) => showToast(`로직 입력을 못 읽음 — ${String(error)}`, "error"), ); } /** 항목 줄 연결 메뉴 — [고르기 셋, 그 밖(로직 입력값 · 연결 풀기)] · ref = 지금 연결 */ export function linkItems(ctx: TabContext, ref: Row | null, apply: ApplyRef): [Leaf[], Leaf[]] { const more: Leaf[] = []; if (ref?.tab === "logic") more.push(["로직 입력값 고치기", () => askInputs(apply, ref.code, ref.name || "", ref)]); if (ref) more.push(["연결 풀기", () => apply(null)]); const picks: Leaf[] = [ ["일위대가 호표", () => pickHopyo(ctx, apply, "dg")], ["단가산출 호표", () => pickHopyo(ctx, apply, "sg")], [ "단가산출 로직(M01 · 새 산근)", () => openLogicPicker((logic) => askInputs(apply, logic.키, logic.이름)), ], ]; return [picks, more]; }