/* ============================================================================= * B05_Profile_UI_Drainage_Facility_Merge.ts * 계곡 통과 시설 [저장] — **폼이 아는 칸만 갈아 끼우고, 폼이 모르는 칸은 건드리지 않음** * (2026-09-14 브레인 판정). * * ⚠ 앞서 저장이 옵션을 **통째로** 갈아 끼웠음 — 구조물 집계표·구조물도로 적은 칸(관 기슭막이 * 기초 `revet_foundation` · 독립 기슭막이 `foundation`·뒷길이·돌 종류 …)이 B05 에서 그 시설을 * 한 번 더 저장하면 **조용히 지워졌음**. 「작업본 쓰기는 [저장]에서만」인데 그 [저장]이 옆 칸까지 * 지우던 자리. * ⚠ 통째로 갈아 끼우는 것은 **시설 종류 자체를 바꿀 때만** — 그때는 옛 칸이 뜻을 잃음. * ⚠ 폼이 아는 칸을 폼이 비워 보내면(값을 지움·유입구를 집수정 → 기슭막이로 바꿔 집수정 칸이 빠짐) * 그 칸은 지워짐 — 사용자가 폼에서 한 일이므로 맞음. * ⚠ 폼에 칸을 늘리면 아래 목록에도 넣을 것. 빠뜨리면 그 칸을 **폼에서 지워도 옛 값이 남는** * 쪽으로 틀림(지워지는 쪽보다 덜 위험). 거울 시험 `test_b05_facility_options_merge`. * ⚠ import 없음 — 시험이 이 파일만 옮겨 돌림. * ========================================================================== */ const revetKeys = (side: "inlet" | "outlet"): string[] => ["form", "length_m", "height_m", "before_m", "after_m"].map((key) => `${side}_revet_${key}`); const wingKeys = (side: "in" | "out"): string[] => [ `wing_${side}`, `wing_${side}_height_m`, `wing_${side}_length_m`, `wing_${side}_angle_deg`, ]; /** 시설 종류별로 **폼이 적는 칸** — `createFacilityOptionsForm().readOptions()` 가 쓰는 키 전부. */ export const FORM_OPTION_KEYS: Readonly> = { pipe: [ "pipe_diameter_mm", "pipe_kind", "wing_wall_type", "inlet_type", "inlet_structure", "inlet_basin_form", "inlet_basin_material", "inlet_basin_length_m", "inlet_basin_before_m", "inlet_basin_after_m", ...revetKeys("inlet"), "outlet_type", ...revetKeys("outlet"), ], box_culvert: ["body_width_m", "body_height_m", ...wingKeys("in"), ...wingKeys("out")], ford_pavement: ["ford_width_m", "ford_height_m", "ford_slope_pct", "thickness_cm", "length_m"], ford_bridge: [ "pipe_kind", "pipe_diameter_mm", "pipe_count", "ford_width_m", "ford_height_m", ...wingKeys("in"), ...wingKeys("out"), ], revetment: ["side", ...revetKeys("inlet"), ...revetKeys("outlet")], }; /** 독립 기슭막이 레지스트리 키 → 폼은 유입·유출 두 채널 키로 적는다. */ const REVETMENT_FIELDS = ["form", "length_m", "height_m", "before_m", "after_m"]; /** 「구조물 옵션 보이기」에서 끈 레지스트리 키 → 시설 폼이 가릴 칸 키(21-10 흠 8). * · 독립 기슭막이는 채널 키(`inlet_revet_*` · `outlet_revet_*`)로 넓힘 * · 유입구 「구조」한 칸이 `inlet_type` 과 `inlet_structure` 를 함께 적음 */ export function hiddenFormKeys( facility: string | null, registryHidden: Iterable, ): Set { const hidden = new Set(registryHidden); if (facility === "revetment") { for (const field of REVETMENT_FIELDS) { if (!hidden.has(field)) continue; hidden.add(`inlet_revet_${field}`); hidden.add(`outlet_revet_${field}`); } } if (hidden.has("inlet_type")) hidden.add("inlet_structure"); return hidden; } interface MergeableAttributes { facility: string; start_m?: number; end_m?: number; options?: Record; /** 집계 항목(options 밖) — 다음 값에 칸이 없으면(undefined) 옛 지정을 그대로 잇는다. */ summary_item?: unknown; } /** 저장할 시설 정보 — 같은 종류면 폼이 모르는 옛 칸을 이어 붙임. */ export function mergeFacilityOptions( previous: T | null, next: T, ): T { if (!previous || previous.facility !== next.facility) return next; const managed = new Set(FORM_OPTION_KEYS[next.facility] ?? []); const kept = Object.entries(previous.options ?? {}).filter(([key]) => !managed.has(key)); const options = { ...Object.fromEntries(kept), ...(next.options ?? {}) }; const summary = next.summary_item === undefined && previous.summary_item !== undefined ? { summary_item: previous.summary_item } : {}; return { ...next, ...summary, options: Object.keys(options).length ? options : undefined }; }