/* ============================================================================= * B09_Estimation_UI_Tab_BasisSheet.ts * B09 산출기초 탭 — 줄에 달린 근거를 한 장으로 모은 장(별표2 (5)(가) 열셋째) * * - 옛 `B09_Estimation_UI_BaseData.ts` 의 산출기초를 그대로 옮김(PLAN 12장 옛 탭 옮기기). * - ⚠ 여기서 값을 다시 계산하지 않음 — 모으기만 함. 네 구획: 판 · 고른 값 · 공종별 근거 · 못 채운 자리. * ========================================================================== */ import { API_BASE_URL } from "@config/config_frontend"; import type { ProvenancePayload } from "@ui/ui_template_provenance"; import type { B09Tab } from "./B09_Estimation_UI_Shell_Types"; import { L, hint } from "./B09_Estimation_UI_Sheet"; import { head, infoTable, note } from "./B09_Estimation_UI_Table"; interface BasisSheetDto { provenance?: ProvenancePayload; note: string; summary: string; dataset_versions: Array<{ dataset_id: string; file: string; effective_date: string; sha256: string; }>; chosen_conditions: Array<{ item: string; value: string }>; work_items: Array<{ code: string; name: string; unit: string; notes: string[] }>; gaps: Array<{ kind: string; code: string; reason: string }>; } function draw(body: HTMLElement, data: BasisSheetDto): void { const sheets = data.provenance?.sheets; body.append(head("산출기초")); body.append(note(data.summary)); body.append(note(data.note)); body.append(head(`① 어느 판으로 계산했나 (${data.dataset_versions.length})`)); body.append( infoTable( ["자료", "파일", "기준일", "지문(앞 12)"], data.dataset_versions.map((row) => [ row.dataset_id, row.file, row.effective_date, row.sha256 || "—", ]), [0, 1, 2, 3], ["dataset_id", "file", "effective_date", "sha256"], sheets?.basis_versions, ), ); body.append(head(`② 무엇을 골랐나 (${data.chosen_conditions.length})`)); if (data.chosen_conditions.length === 0) { body.append(note("고른 값이 없습니다 — 전부 확정 기본값으로 돌고 있습니다.")); } else { body.append( infoTable( ["항 목", "고른 값"], data.chosen_conditions.map((row) => [row.item, row.value]), [0, 1], ["item", "value"], sheets?.basis_chosen, ), ); } body.append(head(`③ 공종마다 무엇을 근거로 했나 (${data.work_items.length})`)); body.append( infoTable( ["코드", "공 종", "단위", "근 거"], data.work_items.map((row) => [row.code, row.name, row.unit, row.notes.join(" · ")]), [0, 1, 2, 3], ["code", "name", "unit", "notes"], sheets?.basis_items, ), ); body.append(head(`④ 못 채운 자리 (${data.gaps.length})`)); if (data.gaps.length === 0) { body.append(note("못 채운 자리가 없습니다.")); return; } body.append( infoTable( ["갈 래", "코드", "사 유"], data.gaps.map((row) => [row.kind, row.code, row.reason]), [0, 1, 2], ["kind", "code", "reason"], sheets?.basis_gaps, ), ); body.append(note("⚠ 여기 있는 것은 0 으로 때우지 않고 남겨 둔 자리입니다.")); } export const basisSheetTab: B09Tab = { key: "basis_sheet", label: () => L("B09_Estimation_Tab_BasisSheet"), render(ctx) { if (!ctx.projectId) { ctx.body.append(hint(L("B09_Sheet_NoProject"))); return; } // 고른 값이 바뀌면 근거도 바뀜 — 고를 때마다 새로 받음. ctx.body.append(hint(L("B09_Sheet_Loading"))); fetch(`${API_BASE_URL}/projects/${encodeURIComponent(ctx.projectId)}/estimation/basis-sheet`, { credentials: "include", }) .then((response) => { if (!response.ok) throw new Error(String(response.status)); return response.json() as Promise; }) .then((data) => { ctx.body.replaceChildren(); draw(ctx.body, data); }) .catch((error: Error) => { ctx.body.replaceChildren(hint(`${L("B09_Sheet_LoadFailed")} ${error.message}`, true)); }); }, };