import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale"; import { hideLoadingOverlay, showLoadingOverlay, showToast } from "@ui/ui_template_elements"; import { fetchSectionDetail, regenerateSections, type SectionDetailResponse, } from "./B06_Section_Api_Fetch"; import { replaceSectionDetail } from "./B06_Section_Section_Store"; export function L(key: keyof typeof ui_locales): string { return ui_locales[key][currentLanguageIndex]; } export function buildGroup(legend: string, collapsed = false): HTMLElement { const group = document.createElement("section"); group.className = `b06-profile__group ui-collapsible ui-sidebar-section${collapsed ? " is-collapsed" : ""}`; const title = document.createElement("h3"); title.className = "b06-profile__group-legend ui-collapsible__title"; title.textContent = legend; group.append(title); return group; } /** 반폭 확대(백엔드 재생성)에 필요한 페이지 상태 — 클로저 대신 함수로 받아 결합을 끊는다. */ export interface SampleWidenerDeps { /** 현재 대상 경로. 없으면 확대 불가. */ target: () => { projectId: string; routeId: number } | null; /** 보유 샘플의 최대 반폭(m). */ sampledHalfWidth: () => number; /** 재생성으로 받은 새 상세를 페이지 상태에 반영하고 다시 그린다. */ applyDetail: (detail: SectionDetailResponse) => void; } /** * 요청 반폭까지 지반 샘플을 넓히는 함수를 만든다 — 보유 샘플로 충분하면 즉시 true * (재계산 없음). 모자라면 그 폭으로 B05부터 재생성(영구 저장)하고 상세를 다시 받는다. * 느려도 허용(2026-08-06 사용자 확정). 전역 [전체 측점 반영]과 카드 줌 버튼의 반폭 * 확대가 같은 경로를 쓴다(2026-08-23 사용자 지시). */ export function createSampleWidener(deps: SampleWidenerDeps): (widthM: number) => Promise { let running = false; return async (widthM) => { const target = deps.target(); if (!target) return false; if (widthM <= deps.sampledHalfWidth() + 1e-6) return true; if (running) return false; // 재생성 중 중복 요청은 무시(버튼 연타). running = true; showLoadingOverlay(); try { await regenerateSections(target.projectId, target.routeId, widthM); // 재생성 응답에는 설계가 없다 — 상세를 다시 받아 기본 설계 프리뷰까지 얹는다. const fresh = await fetchSectionDetail(target.projectId, target.routeId); // 공유 캐시도 새 상세로 바꿔 B05가 옛 값을 못 보게 한다. replaceSectionDetail(target.projectId, target.routeId, fresh); deps.applyDetail(fresh); showToast(L("B06_Profile_Regenerate_Success"), "success"); return true; } catch (error) { const detail = error instanceof Error ? ` ${error.message}` : ""; showToast(`${L("B06_Profile_Regenerate_Failed")}${detail}`, "error"); return false; } finally { running = false; hideLoadingOverlay(); } }; }