/* ============================================================================= * B07_DesignDetail_UI_Settings_Scale.ts * ⚙ 모달 X · Y 축척 칸(485-2 · 요구 280-5) — 종단 · 횡단 · 토적 · 전개도만. * * 칸 = 분모 정수 · 범위 밖 = 칸 붉게 + [축척 저장] 꺼짐 · 기본이 자동인 칸(토적 X · 전개도) = 「자동」 * [축척 저장] = 그 종류 저장본이 있으면 n 장 물음(취소 = 그대로) → PUT → 화면 다시 세움(도면 다시 그림) * [기본값] = 칸만 기본으로 채움 — 저장은 [축척 저장] * ========================================================================== */ import { createCheckbox } from "@ui/ui_template_checkbox"; import { createButton, createInputField, hideLoadingOverlay, showConfirmDialog, showLoadingOverlay, showToast, } from "@ui/ui_template_elements"; import { type DrawingScaleAxes, type DrawingScaleKind, type DrawingScales, fetchDrawingScales, saveDrawingScales, } from "./B07_DesignDetail_Api_Fetch"; import type { DrawingSettingsDeps } from "./B07_DesignDetail_UI_Settings"; import { readScaleAxis, scaleToSend } from "./logic/B07_DesignDetail_Scale_Check"; const LABEL: Record = { longitudinal: "종단도", cross: "횡단도", mass_haul: "토적도", development: "전개도", }; const AXES = [ ["x", "X(가로) 1/"], ["y", "Y(세로) 1/"], ] as const; const isScaleKind = (kind: string): kind is DrawingScaleKind => kind in LABEL; /** ⚙ 모달 몸에 축척 칸을 붙임 — 축척 없는 종류는 아무것도 안 함. */ export function mountDrawingScale( body: HTMLElement, kind: string, deps: DrawingSettingsDeps, close: () => void, ): void { if (!isScaleKind(kind) || !deps.plan) return; const { projectId, onClosed } = deps.plan; const section = document.createElement("div"); section.className = "b07-scale"; section.textContent = "축척 받는 중…"; body.append(section); void fetchDrawingScales(projectId) .then((data) => { section.textContent = ""; buildScaleFields(section, kind, data, async (next) => { const saved = data.saved[kind] ?? 0; const ask = `${LABEL[kind]} 축척을 바꾸면 저장본 ${saved}장이 풀립니다(원본에서 다시 그림). 바꿀까요?`; if (saved > 0 && !(await showConfirmDialog(ask, "바꿈"))) return; showLoadingOverlay(); try { await saveDrawingScales(projectId, { [kind]: next }); close(); showToast(`${LABEL[kind]} 축척을 바꿨습니다.`, "success"); onClosed(true, deps.current()?.id); // 목록 · 도면을 서버에서 다시 받아 다시 그림 } catch (error) { showToast( error instanceof Error ? error.message : "축척을 저장하지 못했습니다.", "error", ); } finally { hideLoadingOverlay(); } }); }) .catch(() => { section.textContent = "축척을 받지 못했습니다."; }); } function buildScaleFields( section: HTMLElement, kind: DrawingScaleKind, data: DrawingScales, save: (next: DrawingScaleAxes) => Promise, ): void { const fields = AXES.map(([axis, label]) => { const fallback = data.defaults[kind][axis]; const range = data.ranges[kind][axis]; const [min, max] = range; const field = createInputField({ label, type: "number", compact: true, min, max }); field.root.classList.add("b07-scale__field"); const auto = fallback === null ? createCheckbox("자동") : undefined; if (auto) field.root.append(auto.root); const set = (value: number | null): void => { field.input.value = value === null ? "" : String(value); if (auto) auto.input.checked = value === null; field.input.disabled = value === null && !!auto; }; set(data.scales[kind][axis]); const read = () => readScaleAxis(field.input.value, !!auto?.input.checked, range); return { axis, field, auto, fallback, set, read }; }); const saveButton = createButton({ label: "축척 저장", onClick: () => { const [x, y] = fields.map((f) => f.read()) as [number | null, number | null]; const now = data.scales[kind]; if (x === now.x && y === now.y) { showToast("바뀐 축척이 없습니다.", "info"); return; } void save({ x: scaleToSend(x, fields[0].fallback), y: scaleToSend(y, fields[1].fallback) }); }, }); const check = (): void => { let bad = false; for (const f of fields) { const result = f.read(); f.field.setError(typeof result === "string" ? result : undefined); bad ||= typeof result === "string"; } saveButton.disabled = bad; }; for (const f of fields) { f.field.input.addEventListener("input", check); f.auto?.input.addEventListener("change", () => { if (f.auto!.input.checked) f.set(null); else f.field.input.disabled = false; // 빈 칸 — 숫자를 적어야 저장 check(); }); } const reset = createButton({ label: "기본값", variant: "ghost", onClick: () => { fields.forEach((f) => f.set(f.fallback)); check(); }, }); reset.title = "칸을 기본 축척으로 채움 — [축척 저장]으로 씀"; const title = document.createElement("p"); title.className = "b07-settings__note"; title.textContent = kind === "cross" ? "축척 — 횡단도 장(표준 횡단면도 뺌)" : `축척 — ${LABEL[kind]}`; const row = document.createElement("div"); row.className = "b07-scale__row"; row.append(...fields.map((f) => f.field.root)); const buttons = document.createElement("div"); buttons.className = "b07-scale__actions"; buttons.append(reset, saveButton); section.append(title, row, buttons); }