/* ============================================================================= * B09_Estimation_UI_Tab_PriceCompare.ts * B09 자재단가대비표 탭 — 원천 슬롯 여섯 · 채택 · 최소단가 (실무 `자재단가대비표` · STmate `wM_Boxa` 보기) * * - 칸: 호표 · 명칭 · 규격 · 단위 · 슬롯 1~5(단가 · 페이지) · 적용 단가(6번) · 비고. * - 채택 슬롯 = 굵게·색 · 최소단가 = 「최소」 표시(서버 `min_slot`). 값 없는 슬롯은 빈칸(0 아님). * - 채택 바꾸기(「변동없음 / 1 단가~5 단가 / 최소단가」 일괄)는 2차 — 저장 모양 판정 뒤. * ========================================================================== */ import type { B09Tab, B09TabContext } from "./B09_Estimation_UI_Shell_Types"; import { L, el, hint, numberCell, sheetTable, won } from "./B09_Estimation_UI_Sheet"; import { loadPriceCompare, type PriceCompareDto } from "./B09_Estimation_UI_Store"; function head(slotNames: string[]): HTMLElement { const thead = el("thead"); const top = el("tr"); const bottom = el("tr"); for (const label of [ L("B09_Sheet_Col_Sheet"), L("B09_Sheet_Col_Name"), L("B09_Sheet_Col_Spec"), L("B09_Sheet_Col_Unit"), ]) { const th = el("th", "", label); th.rowSpan = 2; top.append(th); } slotNames.forEach((name, index) => { const th = el("th", "", `${index + 1} ${name}`); th.colSpan = 2; top.append(th); bottom.append( el("th", "", L("B09_Sheet_Col_UnitPrice")), el("th", "", L("B09_Sheet_Col_Page")), ); }); const note = el("th", "", L("B09_Sheet_Col_Note")); note.rowSpan = 2; top.append(note); thead.append(top, bottom); return thead; } function draw(ctx: B09TabContext, data: PriceCompareDto): void { const table = data.material_comparison; const { wrap, tbody } = sheetTable(head(table.slot_names)); table.rows.forEach((row, index) => { const tr = el("tr"); tr.append( el("td", "", String(index + 1)), el("td", "", row.name), el("td", "", row.spec), el("td", "", row.unit), ); row.slots.forEach((slot, slotIndex) => { const price = numberCell(won(slot.price_krw)); if (slot.adopted) price.classList.add("b09s-adopted"); if (row.min_slot === slotIndex + 1) price.append(el("span", "b09s-min", L("B09_Sheet_Min"))); tr.append(price, el("td", "", slot.source_note)); }); tr.append(el("td", "b09s-note", row.note)); tbody.append(tr); }); ctx.body.append(el("div", "b09s-title", L("B09_Sheet_Tab_PriceCompare")), wrap); if (table.rows.length === 0) ctx.body.append(hint(L("B09_Sheet_EmptyGroup"))); for (const note of table.notes) ctx.body.append(hint(note)); } export const priceCompareTab: B09Tab = { key: "price_compare", label: () => L("B09_Sheet_Tab_PriceCompare"), render(ctx) { if (!ctx.projectId) { ctx.body.append(hint(L("B09_Sheet_NoProject"))); return; } ctx.body.append(hint(L("B09_Sheet_Loading"))); loadPriceCompare(ctx.projectId) .then((data) => { ctx.body.replaceChildren(); draw(ctx, data); }) .catch((error: Error) => { ctx.body.replaceChildren(hint(`${L("B09_Sheet_LoadFailed")} ${error.message}`, true)); }); }, };