/* ============================================================================= * B08_Quantity_UI_Page.ts * 로그인 후 08: 5차 워크플로우 (수량 산출) — 위 탭 화면 (PLAN 61) * * 탭 = 구조물 집계표(61-1) · 자재 · 토공 · 수리집수면적표 (PLAN 95-1 · 160-1) · 거리이정표(244-1). 위 탭 단추 = 공용 * 기본 크기(md) · 보조 탭 단추 = sm(PLAN 69-1). 구조물도는 집계표 행을 눌러 여는 모달(95-3). * 자재 아래 보조 탭 = 총괄자재수량집계표 · 콘크리트소요자재집계표 · 자재수량집계표(121-1 `…_UI_Material`). * 옛 수량 산출 코드(`old_code/B08_Quantity/`)는 가져오지 않음 — 새로 씀. * 좌측 패널 = 메인 탭과 같은 컨테이너 셋 + [저장] 줄(`B08_Quantity_UI_Options` · PLAN 122). * ========================================================================== */ import "@ui/ui_template_general_layout.css"; import "./B08_Quantity_UI_Page.css"; import { createWorkflowLayout } from "@ui/ui_template_workflow_layout"; import { createButton } from "@ui/ui_template_elements"; import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale"; import { CURRENT_PROJECT_ID_KEY } from "@config/config_frontend"; import { workflowSteps } from "../A00_Common/b_page_scaffold"; import { goToWorkflowStage, WORKFLOW_STEP_ROUTES } from "../A00_Common/b_workflow_nav"; import { renderStructureSummary } from "./B08_Quantity_UI_Summary"; import { renderMaterialSheet } from "./B08_Quantity_UI_Material"; import { renderHydro } from "./B08_Quantity_UI_Hydro"; import { renderEarthwork } from "./B08_Quantity_UI_Earthwork"; import { createOptionsPanel } from "./B08_Quantity_UI_Options"; import { renderMilestone } from "./B08_Quantity_UI_Milestone"; function L(key: keyof typeof ui_locales): string { return ui_locales[key][currentLanguageIndex]; } const TABS = [ { id: "summary", label: "B08_Quantity_Tab_StructureSummary" }, { id: "material", label: "B08_Quantity_Tab_Materials" }, { id: "earthwork", label: "B08_Quantity_Tab_Earthwork" }, { id: "hydrology", label: "B08_Quantity_Tab_Hydrology" }, { id: "milestone", label: "B08_Quantity_Tab_Milestone" }, ] as const; type TabId = (typeof TABS)[number]["id"]; /** 자재 탭 보조 탭 — 실무 엑셀 차례. */ const SHEETS = [ { id: "material", label: "B08_Quantity_Tab_MaterialTotal" }, { id: "concrete", label: "B08_Quantity_Tab_ConcreteMaterial" }, { id: "quantity", label: "B08_Quantity_Tab_MaterialQuantity" }, ] as const; type SheetId = (typeof SHEETS)[number]["id"]; function pendingNote(): HTMLElement { const pending = document.createElement("p"); pending.className = "ui-general-block__empty"; pending.textContent = L("B08_Quantity_Tab_Pending"); return pending; } /** 자재 탭 — 보조 탭 줄 + 몸통(보조 탭마다 새 몸통 · 늦은 응답이 다른 보조 탭을 덮지 않게). */ function renderMaterials(host: HTMLElement, projectId: string | null): void { const tabs = document.createElement("div"); tabs.className = "b08-quantity__tabs ui-subtabs b08-structure__tabs"; tabs.setAttribute("role", "tablist"); const view = document.createElement("div"); const select = (picked: SheetId): void => { tabs.replaceChildren( ...SHEETS.map(({ id, label }) => { const button = createButton({ label: L(label), variant: id === picked ? "filled" : "ghost", size: "sm", onClick: () => id !== picked && select(id), }); button.setAttribute("role", "tab"); button.setAttribute("aria-selected", String(id === picked)); button.dataset.b08StructureSheet = id; return button; }), ); const body = document.createElement("div"); view.replaceChildren(body); if (!projectId) body.append(pendingNote()); else void renderMaterialSheet(body, projectId, picked); }; host.replaceChildren(tabs, view); select("material"); } /** 탭마다 새 몸통 — 늦게 온 응답이 다른 탭 화면을 덮지 않게(떨어진 몸통에만 그림). */ function renderTab(host: HTMLElement, tab: TabId, projectId: string | null): void { const view = document.createElement("div"); host.replaceChildren(view); if (tab === "material") return renderMaterials(view, projectId); if (tab === "summary" && projectId) { // 보조 탭 줄 자리(170-4) — 보이지 않는 sm 단추 한 줄 · 자재 탭과 같은 높이라 탭 바꿀 때 안 튐 const blank = document.createElement("div"); blank.className = "b08-quantity__tabs ui-subtabs b08-quantity__tabs--blank"; blank.setAttribute("aria-hidden", "true"); const ghost = createButton({ label: L(SHEETS[0].label), variant: "ghost", size: "sm" }); ghost.tabIndex = -1; blank.append(ghost); const body = document.createElement("div"); view.append(blank, body); return void renderStructureSummary(body, projectId); } if (tab === "hydrology" && projectId) return void renderHydro(view, projectId); if (tab === "earthwork" && projectId) return void renderEarthwork(view, projectId); if (tab === "milestone" && projectId) return renderMilestone(view, projectId); view.append(pendingNote()); } export function renderB08Quantity(root: HTMLElement): void { const projectId = localStorage.getItem(CURRENT_PROJECT_ID_KEY); const tabBar = document.createElement("div"); tabBar.className = "b08-quantity__tabs"; tabBar.setAttribute("role", "tablist"); const panel = document.createElement("div"); panel.className = "b08-quantity__panel"; panel.setAttribute("role", "tabpanel"); let current: TabId = "summary"; const select = (tab: TabId): void => { current = tab; tabBar.replaceChildren( ...TABS.map(({ id, label }) => { const button = createButton({ label: L(label), variant: id === tab ? "filled" : "ghost", onClick: () => id !== tab && select(id), }); button.setAttribute("role", "tab"); button.setAttribute("aria-selected", String(id === tab)); button.dataset.b08Tab = id; return button; }), ); renderTab(panel, tab, projectId); }; const body = document.createElement("div"); body.className = "b08-quantity"; body.append(tabBar, panel); select("summary"); const layout = createWorkflowLayout({ title: L("B08_Quantity_Title"), steps: workflowSteps(), activeStep: 5, mainContent: body, // [저장] 뒤 열린 탭을 서버 정본으로 다시 그림 leftPanel: createOptionsPanel(projectId, () => select(current)), routes: WORKFLOW_STEP_ROUTES, onStepClick: (stepIndex: number) => { if (projectId) goToWorkflowStage(projectId, WORKFLOW_STEP_ROUTES[stepIndex]); }, }); root.append(layout.root); }