/* ============================================================================= * B08_Quantity_UI_Link.ts * 구조물도 모달 맨 위 「조합 연결」 칸(PLAN 154-2) — 그 측점 미연결 시설의 칸 전부 + 연결 여부. * * [선택] → 공용 고르기 모달(`openItemPicker` · 누를 때 `import()`) → 바로 저장 * = PUT …/items/{사본 id}/link(그 칸 하나만 정본에 · 서버가 사본 맞춤) → 집계표 다시 셈 * B05/B06 세션 초안(구조물 · 관)이 있으면 막고 알림 — 그쪽 [저장]이 목록 통째로 덮어 연결이 사라짐 * ========================================================================== */ import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale"; import { createButton, showToast } from "@ui/ui_template_elements"; import { projectItems } from "@ui/ui_template_item_picker_project"; import { readPendingStructures } from "../B05_Profile/B05_Profile_Api_Structures"; import { readPendingPipes } from "../B05_Profile/B05_Profile_Api_Pipes_Draft"; import type { SummaryItem } from "../common_util/common_util_summary_item"; import type { UnlinkedFacility, UnlinkedSlot } from "./B08_Quantity_Summary_Data"; function L(key: keyof typeof ui_locales): string { return ui_locales[key][currentLanguageIndex]; } /** B05/B06 에 저장 안 한 구조물 · 관 조작이 있나(세션 초안). */ export const hasPendingDesign = (projectId: string): boolean => readPendingStructures(projectId) !== null || readPendingPipes(projectId) !== null; /** 칸 하나 고르고 바로 저장 — 저장했으면 true(취소 · 막힘 · 실패 = false). */ async function pickAndLink( projectId: string, facility: UnlinkedFacility, slot: UnlinkedSlot, ): Promise { if (hasPendingDesign(projectId)) { showToast(L("B08_Quantity_Link_Pending"), "warning", 5000); return false; } const { openItemPicker } = await import("@ui/ui_template_item_picker"); const result = await openItemPicker({ current: slot.item, canCopy: true, projectId, structureId: slot.copy, }); if (result.action === "cancel") return false; const item: SummaryItem | null = result.action === "apply" ? result.item : null; try { const failed = await projectItems(projectId).link(slot.copy, facility.facility, item); if (failed.length) showToast(`${L("B08_Quantity_Link_CopyFailed")} — ${failed.join(", ")}`, "warning"); else showToast(L("B08_Quantity_Link_Saved"), "success"); return true; } catch (error) { showToast(`${L("B08_Quantity_Link_Failed")} — ${String(error)}`, "error"); return false; } } /** 「조합 연결」 칸 — 시설마다 이름 + 칸 줄(이름 · 연결 여부 · [선택]). `onLinked` = 저장 뒤(모달 닫고 다시 셈). */ export function linkSection( projectId: string, facilities: readonly UnlinkedFacility[], onLinked: () => void, ): HTMLElement { const section = document.createElement("section"); section.className = "b08-sheets__combo b08-link"; section.dataset.b08Link = "section"; const title = document.createElement("h4"); title.className = "b08-sheets__title"; title.textContent = L("B08_Quantity_Link_Title"); section.append(title); for (const facility of facilities) { const head = document.createElement("p"); head.className = "b08-link__facility"; head.textContent = facility.name; section.append(head); for (const slot of facility.slots) { const row = document.createElement("div"); row.className = "b08-link__slot"; row.dataset.b08LinkSlot = slot.copy; const name = document.createElement("span"); name.textContent = slot.group ?? L("B08_Quantity_Link_Body"); const state = document.createElement("span"); state.className = slot.linked ? "b08-link__state" : "b08-link__state is-missing"; state.textContent = slot.linked ? `${L("B08_Quantity_Link_Linked")} · ${slot.item?.name ?? ""}` : L("B08_Quantity_Summary_Unlinked"); const button = createButton({ label: L("B08_Quantity_Link_Pick"), variant: "ghost", size: "sm", onClick: () => { button.disabled = true; void pickAndLink(projectId, facility, slot).then((saved) => saved ? onLinked() : (button.disabled = false), ); }, }); row.append(name, state, button); section.append(row); } } return section; }