223 lines
6.9 KiB
TypeScript
223 lines
6.9 KiB
TypeScript
/* =============================================================================
|
|
* B08_wf5_Quantity_UI_Page.ts
|
|
* 로그인 후 08: 5차 워크플로우 (수량 산출 UI 메인)
|
|
*
|
|
* 엑셀 내역서(2025년 산불진화임도...) 연동을 위한 전체 145개 템플릿 UI.
|
|
* 입력데이터(B07 또는 이전 산출값)는 비어있는(null) 상태로 조율되며, 수식 기반 템플릿 항목을 표시합니다.
|
|
* 오직 B08_wf5_Quantity 폴더 내의 파일만 다룹니다.
|
|
* ========================================================================== */
|
|
|
|
import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
|
|
import { renderPendingWorkflow, workflowSteps } from "../A00_Common/b_page_scaffold";
|
|
import {
|
|
INITIAL_QUANTITY_TEMPLATES,
|
|
QUANTITY_CATEGORY_LABELS,
|
|
QuantityCategory,
|
|
QuantityItemTemplate,
|
|
} from "./B08_wf5_Quantity_Template";
|
|
import "./B08_wf5_Quantity_UI_Style.css";
|
|
|
|
/** locale 헬퍼 */
|
|
function L(key: keyof typeof ui_locales): string {
|
|
return ui_locales[key][currentLanguageIndex];
|
|
}
|
|
|
|
/** 현재 활성화된 공종 탭 */
|
|
let currentCategory: QuantityCategory = "earthwork";
|
|
|
|
/**
|
|
* B08 수량 산출 메인 렌더링 함수
|
|
*/
|
|
export async function renderB08Quantity(root: HTMLElement): Promise<void> {
|
|
// 1. 공통 Workflow Shell 렌더링
|
|
await renderPendingWorkflow(root, {
|
|
title: L("B08_Quantity_Title"),
|
|
steps: workflowSteps(),
|
|
activeStep: 5,
|
|
});
|
|
|
|
// 2. 본문 컨테이너 슬롯 확보 (Workflow Shell 내부 본문 영역)
|
|
const bodyContainer = root.querySelector(".workflow-body") || root;
|
|
bodyContainer.innerHTML = ""; // 기존 준비중 메시지 대체
|
|
|
|
const container = document.createElement("div");
|
|
container.className = "b08-quantity-container";
|
|
bodyContainer.appendChild(container);
|
|
|
|
// 3. UI 컴포넌트 조합
|
|
renderSummaryGrid(container);
|
|
renderTabBar(container);
|
|
renderTableArea(container);
|
|
renderActionBar(container);
|
|
}
|
|
|
|
/**
|
|
* 1. 상단 요약 카드 그리드 렌더링 (입력 데이터 펜딩 상태)
|
|
*/
|
|
function renderSummaryGrid(parent: HTMLElement): void {
|
|
const grid = document.createElement("div");
|
|
grid.className = "b08-summary-grid";
|
|
|
|
const summaryItems = [
|
|
{ title: "전체 엑셀 템플릿", val: "총 145개 항목", desc: "6개 세부 공종 100% 매핑 완료" },
|
|
{ title: "단가산출 / 일위대가", val: "65개 표준공종", desc: "토공 40종 + 일위대가 25종" },
|
|
{ title: "자재 & 노무비", val: "51개 자재/인력", desc: "자재 37종 + 노무인부 14종" },
|
|
{ title: "중기 & 일식/폐기물", val: "29개 중기/일식", desc: "중기시간 24종 + 폐기물 5종" },
|
|
];
|
|
|
|
grid.innerHTML = summaryItems
|
|
.map(
|
|
(item) => `
|
|
<div class="b08-card">
|
|
<div class="b08-card-header">${item.title}</div>
|
|
<div class="b08-card-value empty-state">${item.val}</div>
|
|
<div class="b08-card-sub">${item.desc}</div>
|
|
</div>
|
|
`,
|
|
)
|
|
.join("");
|
|
|
|
parent.appendChild(grid);
|
|
}
|
|
|
|
/**
|
|
* 2. 공종 탭 바 렌더링
|
|
*/
|
|
function renderTabBar(parent: HTMLElement): void {
|
|
const tabBar = document.createElement("div");
|
|
tabBar.className = "b08-tab-bar";
|
|
|
|
const categories: QuantityCategory[] = [
|
|
"earthwork",
|
|
"structure",
|
|
"material",
|
|
"equipment",
|
|
"waste",
|
|
"labor",
|
|
];
|
|
|
|
categories.forEach((cat) => {
|
|
const btn = document.createElement("button");
|
|
btn.className = `b08-tab-btn ${cat === currentCategory ? "active" : ""}`;
|
|
btn.textContent = QUANTITY_CATEGORY_LABELS[cat].ko;
|
|
btn.addEventListener("click", () => {
|
|
currentCategory = cat;
|
|
// 탭 업데이트
|
|
tabBar.querySelectorAll(".b08-tab-btn").forEach((b) => b.classList.remove("active"));
|
|
btn.classList.add("active");
|
|
// 테이블 재렌더링
|
|
const tableArea = parent.querySelector("#b08-table-area");
|
|
if (tableArea) {
|
|
tableArea.innerHTML = "";
|
|
renderTableContent(tableArea as HTMLElement);
|
|
}
|
|
});
|
|
tabBar.appendChild(btn);
|
|
});
|
|
|
|
parent.appendChild(tabBar);
|
|
}
|
|
|
|
/**
|
|
* 3. 수량 집계 데이터 테이블 영역 렌더링
|
|
*/
|
|
function renderTableArea(parent: HTMLElement): void {
|
|
const tableArea = document.createElement("div");
|
|
tableArea.id = "b08-table-area";
|
|
tableArea.className = "b08-table-wrapper";
|
|
parent.appendChild(tableArea);
|
|
|
|
renderTableContent(tableArea);
|
|
}
|
|
|
|
/**
|
|
* 테이블 내용 렌더링 (현재 카테고리 기준)
|
|
*/
|
|
function renderTableContent(parent: HTMLElement): void {
|
|
const items = INITIAL_QUANTITY_TEMPLATES.filter((item) => item.category === currentCategory);
|
|
|
|
const table = document.createElement("table");
|
|
table.className = "b08-table";
|
|
|
|
table.innerHTML = `
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 60px;">호표</th>
|
|
<th>명칭</th>
|
|
<th>규격</th>
|
|
<th style="width: 60px;">단위</th>
|
|
<th>설계 산출 수량 (raw_qty)</th>
|
|
<th style="width: 80px;">할증률</th>
|
|
<th>최종 산출 수량</th>
|
|
<th>엑셀 수식 및 산출 근거 (Formula)</th>
|
|
<th>연동 시트 / 코드</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${items.map((item) => renderTableRow(item)).join("")}
|
|
</tbody>
|
|
`;
|
|
|
|
parent.appendChild(table);
|
|
}
|
|
|
|
/**
|
|
* 테이블 행 HTML 생성
|
|
*/
|
|
function renderTableRow(item: QuantityItemTemplate): string {
|
|
const rawQtyDisplay =
|
|
item.raw_qty !== null
|
|
? item.raw_qty.toLocaleString()
|
|
: `<span class="b08-empty-val">[입력 대기]</span>`;
|
|
|
|
const calcQtyDisplay =
|
|
item.calc_qty !== null
|
|
? item.calc_qty.toLocaleString()
|
|
: `<span class="b08-empty-val">[수식 자동계산]</span>`;
|
|
|
|
const codeBadge = item.code_ref
|
|
? `<span class="b08-badge b08-badge-code">${item.code_ref}</span>`
|
|
: "";
|
|
|
|
return `
|
|
<tr>
|
|
<td>${item.item_no}</td>
|
|
<td><strong>${item.name}</strong></td>
|
|
<td>${item.spec}</td>
|
|
<td>${item.unit}</td>
|
|
<td>${rawQtyDisplay}</td>
|
|
<td>${item.allowance_rate}</td>
|
|
<td>${calcQtyDisplay}</td>
|
|
<td><span class="b08-formula-cell">${item.formula_desc}</span></td>
|
|
<td>
|
|
<span class="b08-badge b08-badge-sheet">${item.excel_ref_sheet}</span>
|
|
${codeBadge}
|
|
</td>
|
|
</tr>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* 4. 하단 컨트롤/액션 바 렌더링
|
|
*/
|
|
function renderActionBar(parent: HTMLElement): void {
|
|
const actionBar = document.createElement("div");
|
|
actionBar.className = "b08-action-bar";
|
|
|
|
actionBar.innerHTML = `
|
|
<div class="b08-info-text">
|
|
💡 <strong>엑셀 내역서 145개 전체 템플릿 매핑 완료</strong>: 입력 수량값은 차후 이전 워크플로우(B07 등) 및 수식 엔진에 의해 자동 기입됩니다.
|
|
</div>
|
|
<button class="b08-btn-confirm">
|
|
수량 산출 확정 및 B09 견적서 생성 >
|
|
</button>
|
|
`;
|
|
|
|
const confirmBtn = actionBar.querySelector(".b08-btn-confirm");
|
|
confirmBtn?.addEventListener("click", () => {
|
|
alert("B08 수량 템플릿 항목(145종)이 확정되었습니다. B09 견적 파이프라인으로 전달됩니다.");
|
|
});
|
|
|
|
parent.appendChild(actionBar);
|
|
}
|