/* ============================================================================= * B09_Estimation_UI_Check.ts * B09 좌측 「데이터 점검」 묶음(PLAN 203-1) — [업데이트] · 연결 점검(옛 「연결 점검」 탭 내용). * * [업데이트] = 단가를 지금 M01 값으로 새로 굳힘 · 서버가 낡았다고 보면(`payload.stale` 까닭 한 줄) * 단추에 점 배지(`is-pending`) · 굳힌 시각 · 까닭 = 공용 호버 * 연결 점검 = 「연결 없음」 줄 목록(누르면 그 탭 그 줄로 · 까닭 = 공용 호버) + 서버 알림 · M01 에 없는 키 * ========================================================================== */ import { attachHoverInfo } from "@ui/ui_template_hover_info"; import type { Row } from "./B09_Estimation_Calc_Base"; import { tabName, type Link, type TabId } from "./B09_Estimation_UI_Tabs"; export interface CheckItem { link: Link; tab: string; name: string; why: string; } /** 빠진 연결 줄 · 알림 글(서버 알림 + M01 에 없는 키) */ export function checkItems(sheets: Row, payload: Row): { items: CheckItem[]; notes: string[] } { const gaps: Row[] = sheets.gaps || []; const items = gaps.map((g) => ({ link: { tab: g.tab as TabId, key: g.key }, tab: tabName(g.tab), name: String(g.name ?? ""), why: String(g.why ?? ""), })); const notes: string[] = [...(payload.notes || [])]; const missing: string[] = payload.inputs?.missing || []; if (missing.length) notes.push(`M01 에 없는 키 ${missing.length} — ${missing.join(" · ")}`); return { items, notes }; } /** [업데이트] 호버 글 — 굳힌 시각 + 낡은 까닭 */ export function refreshTip(payload: Row | null): string { const made = payload?.book?.made; const when = made ? `단가 굳힘 ${String(made).replace("T", " ")}` : "단가 아직 안 굳힘"; const stale = payload?.stale; return `${when} · 누르면 지금 M01 값으로 새로 굳힘${stale ? ` · 업데이트 필요 — ${stale}` : ""}`; } export interface CheckPanel { /** 묶음 안 연결 점검 자리 */ list: HTMLElement; draw: (payload: Row, sheets: Row) => void; } export function createCheckPanel(refresh: HTMLElement, goto: (link: Link) => void): CheckPanel { let payload: Row | null = null; let items: CheckItem[] = []; attachHoverInfo(refresh, () => refreshTip(payload)); const list = document.createElement("div"); list.className = "b09-check"; attachHoverInfo(list, (row) => items[Number(row.dataset.i)]?.why, "[data-i]"); const draw = (next: Row, sheets: Row): void => { payload = next; refresh.classList.toggle("is-pending", Boolean(next.stale)); const made = checkItems(sheets, next); items = made.items; const head = document.createElement("p"); head.className = "b09-check__head"; head.textContent = `연결 없음 ${items.length}곳`; const rows = document.createElement("div"); rows.className = "ui-sidebar-list"; rows.append( ...items.map((item, i) => { const row = document.createElement("button"); row.type = "button"; row.className = "ui-sidebar-row"; row.dataset.i = String(i); const name = document.createElement("span"); name.textContent = item.name; const tab = document.createElement("span"); tab.className = "b09-check__tab"; tab.textContent = item.tab; row.append(name, tab); row.addEventListener("click", () => goto(item.link)); return row; }), ); const notes = made.notes.map((text) => { const p = document.createElement("p"); p.className = "b09-check__note"; p.textContent = text; return p; }); list.replaceChildren(head, ...(items.length ? [rows] : []), ...notes); }; return { list, draw }; }