/* ============================================================================= * B08_Quantity_UI_Hydro.ts * 4탭 수리집수면적표 — 배수지점 측점마다 보조 탭 · 한 장 = 홍수량 계산서(PLAN 62-2 · 69-4~6). * * 측점 목록은 저장된 관 지점(pipe_points) · B05 연결값(A · L · H · 관경 · 내공 · 월류 폭 · 96)이 빈칸을 채움 * 값 차례 = 기본 < 장 기본 < 연결 < 저장 < 초안 · 연결값과 같은 칸은 null 로 저장(연동 유지) * 박스암거 = 사각 단면 장(73-11) · 세월교 · 물넘이포장 = 월류 높이 장(73-12) · 그 밖 = 관 장 * 처음 값 = 표준 값 · 측점 고유 값은 빈칸 · 지역 · R24 = 공사지 자료(서버) * 조작 = 캐시(sessionStorage) · [저장] = B08 자기 파일(PUT …/quantity/hydro) · 뒤 캐시 비움 * 계산 = 서버 단독(POST …/quantity/hydro/calc · 최신 응답만 채택) * 자료 읽기 · 측점 입력 겹치기 = `B08_Quantity_Hydro_Table`(화면 없음 · 인쇄 미리보기도 씀 · 302-3) * ========================================================================== */ import "./B08_Quantity_UI_Hydro.css"; import { createButton, hideLoadingOverlay, showLoadingOverlay, showToast, } from "@ui/ui_template_elements"; import { createText } from "@ui/ui_template_text"; import { clearState, writeState } from "../A00_Common/b_page_state"; import { createToolbar } from "@ui/ui_template_toolbar"; import { fillTabs } from "../A00_Common/b_page_tabs"; import { stationLabel } from "../common_util/common_util_station"; import { hydroCalc, hydroInputs, keyOf, loadHydro, unlinkedInputs as unlinked, type Drafts, type HydroPayload, type HydroStation, } from "./B08_Quantity_Hydro_Table"; import { request } from "./B08_Quantity_Material_Tables"; import { buildHydroSheet, type HydroInputs, type HydroResult } from "./B08_Quantity_UI_Hydro_Sheet"; export { request }; function note(text: string): HTMLElement { return createText(text, "muted", 14); } /** `onSave` = 페이지 이동 전 저장 등록(481-2) — 고친 측점 없으면 요청 없이 참. */ export async function renderHydro( host: HTMLElement, projectId: string, onSave?: (save: () => Promise) => void, ): Promise { host.replaceChildren(note("홍수량 계산서를 불러오는 중…")); let sheet: HydroPayload; let interval = 20; let drafts: Drafts = {}; try { ({ sheet, interval, drafts } = await loadHydro(projectId)); } catch (error) { host.replaceChildren(note(`홍수량 계산서를 불러오지 못했습니다 — ${String(error)}`)); return; } if (!sheet.stations.length) { host.replaceChildren( note("저장된 배수지점(관 지점)이 없습니다 — 배수유역 · 종단에서 관을 저장하세요."), ); return; } const inputsOf = (station: HydroStation): HydroInputs => hydroInputs(sheet, station, drafts); const tabs = document.createElement("div"); tabs.className = "b08-quantity__tabs b08-hydro__tabs"; const save = createButton({ label: "저장", variant: "filled", onClick: () => void saveAll(), }); save.dataset.b08HydroSave = ""; const toolbar = createToolbar({ items: [tabs, save], size: 32 }); toolbar.classList.add("b08-hydro__toolbar"); const page = document.createElement("div"); host.replaceChildren(toolbar, page); let seq = 0; let openIndex = 0; const open = (index: number): void => { openIndex = index; const station = sheet.stations[index]; fillTabs(tabs, { tabs: sheet.stations.map((s, i) => { const label = stationLabel(s.chainage_m, interval); return { key: String(i), label: s.kind ? `${label} ${s.kind}` : label }; }), value: String(index), kind: "pill", data: "b08HydroStation", title: (key) => sheet.stations[Number(key)].kind || sheet.stations[Number(key)].facility, onChange: (key) => open(Number(key)), }); const draw = (inputs: HydroInputs): void => { const view = buildHydroSheet( stationLabel(station.chainage_m, interval), inputs, sheet, (next) => { drafts[keyOf(station)] = unlinked(station, next); writeState("b08-hydro", drafts, projectId); // 단면 가정 · 관 종류는 그림 · 식이 바뀜 — 장을 다시 그림 · 그 밖은 값만 if (next.section !== inputs.section || next.pipe_kind !== inputs.pipe_kind) draw(next); else void calc(next, view.show); }, station.kind, { values: station.linked ?? {}, b05: station.b05 }, ); page.replaceChildren(view.root); void calc(inputs, view.show); }; draw(inputsOf(station)); }; const calc = async ( inputs: HydroInputs, show: (r: HydroResult | null, message?: string) => void, ): Promise => { const mine = ++seq; try { const result = await hydroCalc(projectId, inputs); if (mine === seq) show(result); } catch (error) { if (mine === seq) show(null, `계산하지 못했습니다 — ${String(error)}`); } }; /** [저장] — 측점 전부의 지금 입력을 B08 자기 파일로 · 성공하면 캐시를 비우고 저장본으로 다시 엶 */ const saveAll = async (): Promise => { const stations = Object.fromEntries( sheet.stations.map((s) => [keyOf(s), unlinked(s, inputsOf(s))]), ); showLoadingOverlay(); try { const body = await request<{ saved: Drafts }>(`/projects/${projectId}/quantity/hydro`, { method: "PUT", body: JSON.stringify({ stations }), }); sheet.saved = body.saved; drafts = {}; clearState("b08-hydro", projectId); showToast("수리집수면적표를 저장했습니다.", "success"); open(openIndex); return true; } catch (error) { showToast(`저장하지 못했습니다 — ${String(error)}`, "error"); return false; } finally { hideLoadingOverlay(); } }; open(0); onSave?.(async () => !Object.keys(drafts).length || saveAll()); }