diff --git a/B06_Section/B06_Section_UI_Page.ts b/B06_Section/B06_Section_UI_Page.ts index 190cd214..b1b725f6 100644 --- a/B06_Section/B06_Section_UI_Page.ts +++ b/B06_Section/B06_Section_UI_Page.ts @@ -138,6 +138,7 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise { projectId, stationInterval: () => stationInterval ?? 20, focusChainage: focusStationAt, + queuePipeOptions: (chainageM, patch) => stationControls.queueCulvertOptions(chainageM, patch), }); const dockDivider = document.createElement("hr"); dockDivider.className = "b05-structure__divider"; @@ -483,8 +484,24 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise { const owner = stationControls.structureSpan.ownerOf(target); structuresPanel.showAtChainage(owner?.chainage_m ?? target.chainage_m); structureSelection.syncInletStructure(); + selectDefaultWall(owner ?? target); }); + /** + * 시설을 폼에 올릴 때 **대표 벽**(유출 기준벽, 없으면 유입)을 함께 고른다 — + * 단 수·옵션 행은 고른 벽에 딸린 것이라 벽이 없으면 유입구·유출구 그룹이 빈다 + * (2026-08-29 사용자 보고). 이미 고른 벽이 있으면 건드리지 않는다. + */ + function selectDefaultWall(section: NonNullable["cross_sections"][number]) { + const culvert = section.culvert; + if (!culvert) return; + if (stationControls.revetOffset.selectedFor(section)) return; + const key = culvert.outlet ? "outlet" : culvert.inlet ? "inlet" : null; + if (!key) return; + stationControls.revetOffset.select(section.chainage_m, key); + sectionView.refreshCard(section.chainage_m); + } + // 메인 영역: 종·횡단 도면(sectionView) 또는 안내 메시지를 표시한다. const mainArea = document.createElement("div"); mainArea.className = "b06-profile__main"; diff --git a/B06_Section/B06_Section_UI_Page_Station_Controls.ts b/B06_Section/B06_Section_UI_Page_Station_Controls.ts index 2decf785..fb87de56 100644 --- a/B06_Section/B06_Section_UI_Page_Station_Controls.ts +++ b/B06_Section/B06_Section_UI_Page_Station_Controls.ts @@ -96,6 +96,8 @@ export interface StationControls { extraSpansByChainage: () => Map>; /** 예약된 구간값 저장을 즉시 내보낸다 — 확정·임시저장 직전에 부른다. */ flushCulvertOptions: () => Promise; + /** 관 옵션 조각을 예약한다 — 좌측 폼의 [수정]도 이 경로로 정본에 간다(2026-08-29). */ + queueCulvertOptions: (chainageM: number, patch: Record) => void; load: () => void; /** 전체 반영 — 개별 반폭을 전역값으로 덮는다(없으면 비운다). */ applyGlobalWidth: (requested: number | undefined, chainages: number[]) => void; @@ -810,6 +812,7 @@ export function createStationControls(deps: StationControlDeps): StationControls return result; }, flushCulvertOptions: () => culvertOptions.flush(), + queueCulvertOptions: (chainageM, patch) => culvertOptions.queue(chainageM, patch), load: () => { loadStationWidths(); loadRevetShifts(); diff --git a/B06_Section/B06_Section_UI_Page_Structures_Panel.ts b/B06_Section/B06_Section_UI_Page_Structures_Panel.ts index 36a85d9a..679e1f7d 100644 --- a/B06_Section/B06_Section_UI_Page_Structures_Panel.ts +++ b/B06_Section/B06_Section_UI_Page_Structures_Panel.ts @@ -28,7 +28,8 @@ import { adjustDockRoot, resetAdjustDock, setAdjustSideSlots } from "./B06_Secti import type { SectionDetailResponse } from "./B06_Section_Api_Fetch"; import type { StationControls } from "./B06_Section_UI_Page_Station_Controls"; -const PIPE_GUIDE = "계곡 통과 시설의 추가·이동·삭제는 B05(종단) 화면에서 합니다."; +const PIPE_ADD_GUIDE = "계곡 통과 시설의 추가·삭제는 B05(종단) 화면에서 합니다."; +const PIPE_MOVE_GUIDE = "기준 측점 이동은 배수유역을 다시 나눠야 해 B05(종단) 화면에서 합니다."; export interface B06StructuresPanelDeps { projectId: string | null; @@ -36,6 +37,9 @@ export interface B06StructuresPanelDeps { stationInterval: () => number; /** 목록·폼에서 고른 시설의 측점 카드를 선택·스크롤한다. */ focusChainage: (chainageM: number) => void; + /** 폼 [수정]으로 바뀐 관 옵션을 캐시에 예약한다 — [저장]·[확정]이 정본에 쓴다 + * (2026-08-29: 조정창 구간값과 같은 경로). 없으면 안내만 한다. */ + queuePipeOptions?: (chainageM: number, patch: Record) => void; } export interface B06StructuresPanel { @@ -93,9 +97,30 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc if (structure) deps.focusChainage(structureAnchorM(structure)); }, getInterval: deps.stationInterval, - onPipeAdd: () => showToast(PIPE_GUIDE, "error"), - onPipeUpdate: () => showToast(PIPE_GUIDE, "error"), - onPipeRemove: () => showToast(PIPE_GUIDE, "error"), + onPipeAdd: () => showToast(PIPE_ADD_GUIDE, "error"), + // 값 수정은 B06에서도 받는다 — 조정창 구간값과 같은 저장 경로(캐시 예약 → + // [저장]·[확정])로 보낸다. 기준점 이동만 배수유역 재분할이 걸려 B05 몫이다. + onPipeUpdate: (fromChainageM, toChainageM, attributes) => { + if (Math.abs(fromChainageM - toChainageM) > 0.005) { + showToast(PIPE_MOVE_GUIDE, "error"); + return; + } + const patch = attributes.options; + if (!patch || !Object.keys(patch).length) return; + if (!deps.queuePipeOptions) { + showToast(PIPE_ADD_GUIDE, "error"); + return; + } + deps.queuePipeOptions(fromChainageM, patch as Record); + // 화면 캐시(목록·폼 재로드용)도 같이 맞춘다 — 저장 전에도 값이 유지된다. + const hit = pipeFacilities.find( + (entry) => Math.abs(entry.chainage_m - fromChainageM) < PIPE_MATCH_M, + ); + if (hit) hit.options = { ...(hit.options ?? {}), ...patch }; + section.setPipeFacilities(pipeFacilities); + showToast("수정한 값은 [저장]·[확정] 때 반영됩니다.", "success"); + }, + onPipeRemove: () => showToast(PIPE_ADD_GUIDE, "error"), onPipeSelect: (chainageM) => { if (chainageM !== null) deps.focusChainage(chainageM); },