/* ============================================================================= * B09_Estimation_UI_Tab_UnitPrice.ts * B09 일위대가 탭 — 목록표 + 일위대가표 (STmate `일위대가목록표` · `wM_Edit_iLWi` 보기 · PLAN 12장 1차) * * - 목록표 = 내역에 쓰인 호표만, **내역에 처음 쓰인 차례**로 「제 N 호표」(번호는 서버가 낼 때마다 매김). * - 줄을 누르면 아래에 그 호표의 일위대가표 — 호표 안 줄(하위 호표·단산·중기)을 누르면 더 들어감. * - 구조물도 호표는 B08 구조물도 탭의 일위대가 표가 본표 — 여기선 금액·안내만. * - 편집(구성행 수정)은 2차 — B09 에 수정 저장 자리가 아직 없음(브레인 판정). * ========================================================================== */ import type { B09Tab, B09TabContext } from "./B09_Estimation_UI_Shell_Types"; import { renderDetail } from "./B09_Estimation_UI_Detail"; import { L, el, hint, numberCell, sheetTable, unconfirmedBadge, won, } from "./B09_Estimation_UI_Sheet"; import { loadBill, type BillDto, type SheetEntryDto } from "./B09_Estimation_UI_Store"; let selected = ""; /** 목록표 머리 — 호표 · 명칭 · 규격 · 단위 · 합계 · 노무비 · 재료비 · 경비 · 비고(실무 목록표). */ export function listHead(first: string): HTMLElement { const thead = el("thead"); const tr = el("tr"); for (const label of [ first, L("B09_Sheet_Col_Name"), L("B09_Sheet_Col_Spec"), L("B09_Sheet_Col_Unit"), L("B09_Sheet_Col_Total"), L("B09_Sheet_Col_Labor"), L("B09_Sheet_Col_Material"), L("B09_Sheet_Col_Expense"), L("B09_Sheet_Col_Note"), ]) { tr.append(el("th", "", label)); } thead.append(tr); return thead; } export function listRow(entry: SheetEntryDto, total: string, isSelected: boolean): HTMLElement { const tr = el("tr", `is-clickable${isSelected ? " is-selected" : ""}`); tr.append( el("td", "", entry.label), el("td", "", entry.name), el("td", "", entry.spec), el("td", "", entry.unit), numberCell(won(total)), numberCell(won(entry.labor_krw)), numberCell(won(entry.material_krw)), numberCell(won(entry.expense_krw)), ); const note = el("td", "b09s-note"); if (entry.unconfirmed) { note.append(unconfirmedBadge(entry.unconfirmed)); tr.classList.add("is-manual"); } tr.append(note); return tr; } function labelFor(bill: BillDto, code: string): string { const entry = bill.unit_price_sheet.entries.find((item) => item.code === code); if (entry) return entry.label; return code.startsWith("X-") ? `${L("B09_Sheet_Machine")} ${code.slice(2)}` : code; } function draw(ctx: B09TabContext, bill: BillDto): void { const entries = bill.unit_price_sheet.entries; const page = el("div", "b09s-split"); const { wrap, tbody } = sheetTable(listHead(L("B09_Sheet_Col_Sheet"))); const detail = el("div", "b09s-split"); const select = (code: string): void => { selected = code; ctx.body.replaceChildren(); draw(ctx, bill); }; for (const entry of entries) { const tr = listRow(entry, entry.total_krw ?? "", entry.code === selected); tr.addEventListener("click", () => select(entry.code)); tbody.append(tr); } page.append(el("div", "b09s-title", L("B09_Sheet_UnitPriceList")), wrap); if (entries.length === 0) page.append(hint(L("B09_Sheet_EmptyList"))); page.append(detail); ctx.body.append(page); if (!selected) { detail.append(hint(L("B09_Sheet_PickSheet"))); return; } const picked = entries.find((entry) => entry.code === selected); if (picked?.source === "structure") { detail.append(el("div", "b09s-title", `${picked.label} ${picked.name} · ${picked.spec}`)); detail.append(hint(L("B09_Sheet_StructureSheet"))); return; } renderDetail(ctx, detail, "unit_price", selected, labelFor(bill, selected)); } export const unitPriceTab: B09Tab = { key: "unit_price", label: () => L("B09_Estimation_Tab_UnitPrice"), render(ctx, arg) { if (!ctx.projectId) { ctx.body.append(hint(L("B09_Sheet_NoProject"))); return; } if (arg) selected = arg; ctx.body.append(hint(L("B09_Sheet_Loading"))); loadBill(ctx.projectId) .then((bill) => { ctx.body.replaceChildren(); draw(ctx, bill); }) .catch((error: Error) => { ctx.body.replaceChildren(hint(`${L("B09_Sheet_LoadFailed")} ${error.message}`, true)); }); }, };