From cb5ea7ede9f4889bc913fedee3c675d08143532d Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sat, 12 Sep 2026 16:02:45 +0900 Subject: [PATCH] =?UTF-8?q?fix(b06):=20=EA=B4=80=20=ED=8E=B8=EC=A7=91?= =?UTF-8?q?=EC=9D=B4=20=EC=96=B4=EB=8A=90=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=A0=80=EC=9E=A5=ED=95=B4=EB=8F=84=20?= =?UTF-8?q?=EA=B0=99=EC=9D=80=20=EA=B2=B0=EA=B3=BC=EA=B0=80=20=EB=90=98?= =?UTF-8?q?=EA=B2=8C=20=EB=A7=9E=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 세 자리가 어긋나 있었음. - B06 저장이 델타를 먼저 내보내고 B05 스냅샷을 나중에 써서, 두 화면을 오가며 만지면 스냅샷이 B06 편집을 통째로 덮었음. B05 [임시저장]과 같은 순서(스냅샷 먼저, 델타 나중)로 바꿈. - B05 저장이 구간값 키만 넘겨, B06 에서 예약한 관 이동·추가·삭제가 조용히 빠졌음. 키 셋을 다 넘김. - B06 좌측 목록이 정본만 읽어 B05 에서 넣고 안 저장한 관이 안 보였음. 구조물과 같은 규칙으로 미저장분을 먼저 씀. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Fe1QWPTfw11PaKh2LjwXdR --- B05_Profile/B05_Profile_UI_Page_Actions.ts | 14 +++++++++++--- B06_Section/B06_Section_UI_Page_Persist.ts | 16 +++++++++------- .../B06_Section_UI_Page_Structures_Panel.ts | 6 +++++- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/B05_Profile/B05_Profile_UI_Page_Actions.ts b/B05_Profile/B05_Profile_UI_Page_Actions.ts index b709f688..591cbe61 100644 --- a/B05_Profile/B05_Profile_UI_Page_Actions.ts +++ b/B05_Profile/B05_Profile_UI_Page_Actions.ts @@ -25,6 +25,7 @@ import { } from "./B05_Profile_Api_Fetch"; import { flushCulvertOptions } from "../B06_Section/B06_Section_Api_Culvert_Options"; import { flushPendingPipes } from "./B05_Profile_Api_Pipes_Draft"; +import { stateKey } from "../A00_Common/b_page_state"; import { invalidateSectionDetail, saveCachedCrossPatches, @@ -170,10 +171,17 @@ export async function tempSaveAction(ctx: PageActionContext): Promise { ); // B06 조정창에서 만진 배수관 구간값도 세션에만 있다 — 함께 내보낸다 // (CLAUDE.md 5장: 영구저장은 [저장]·[확정]에서만). 실패해도 저장은 진행한다. + // ⚠ 키 **셋을 다 넘긴다**(2026-09-12 사용자: 어느 페이지에서 저장해도 결과가 같아야 + // 한다). 종전에는 구간값 키만 넘겨, B06 에서 예약한 관 이동·추가·삭제가 B05 [임시저장] + // 에서는 조용히 빠졌다. if (latest?.route?.id != null) { - await flushCulvertOptions(projectId, `b06:culvertopt:${projectId}:${latest.route.id}`).catch( - () => undefined, - ); + const routeId = latest.route.id; + await flushCulvertOptions( + projectId, + stateKey("culvertopt", projectId, routeId), + stateKey("culvertmove", projectId, routeId), + stateKey("culvertedit", projectId, routeId), + ).catch(() => undefined); } // B06에서 만져 **캐시에 얹힌** 횡단 수정분을 함께 남긴다 — 안 보내면 바로 아래 // 캐시 비우기에서 사라진다. 계획선 저장 **뒤에** 보내야 사용자 수정 1세트가 diff --git a/B06_Section/B06_Section_UI_Page_Persist.ts b/B06_Section/B06_Section_UI_Page_Persist.ts index 41299ad5..6e99d8be 100644 --- a/B06_Section/B06_Section_UI_Page_Persist.ts +++ b/B06_Section/B06_Section_UI_Page_Persist.ts @@ -420,7 +420,15 @@ export function collectSectionEdits(ctx: SectionPersistContext): { /** 세션에 쌓인 조정창·구조물 조작을 정본으로 내보낸다 — [저장]·[확정] 공통 앞단. */ async function flushPendingEdits(ctx: SectionPersistContext, projectId: string): Promise { - // 조정창 구간값은 세션에만 있다 — 정본 payload를 모으기 전에 내보낸다 + // ⚠ 순서는 **B05 [임시저장]과 같아야 한다**(2026-09-12 사용자: 어느 페이지에서 저장해도 + // 결과가 같아야 한다). 관 목록은 B05 가 **전체 스냅샷**으로, B06 이 **바뀐 것만**(추가· + // 삭제·이동·구간값) 담으므로, 스냅샷을 먼저 얹고 그 위에 델타를 적용해야 한다. 반대로 + // 하면 스냅샷이 B06 편집을 통째로 덮는다. + await flushPendingPipes(projectId).catch((error) => { + const detail = error instanceof Error ? ` ${error.message}` : ""; + showToast(`배수관 저장에 실패했습니다.${detail}`, "error"); + }); + // 조정창 구간값·추가·삭제·이동은 세션에만 있다 — 정본 payload를 모으기 전에 내보낸다 // (CLAUDE.md 5장: 영구저장은 [저장]·[확정]에서만). await ctx.flushCulvertOptions(); // B05 3D에서 바꾼 상단측(측구 방향)도 여기서 내보낸다 — 예전에는 B05 [임시저장]에만 @@ -431,12 +439,6 @@ async function flushPendingEdits(ctx: SectionPersistContext, projectId: string): // B05에서 만지고 넘어온 구조물 조작분도 여기서 정본에 남긴다. 실패해도 횡단 // 저장까지 막지는 않는다 — 미저장분은 세션에 남으므로 다시 시도할 수 있다 // (2026-08-29 실측: 타입이 거절되자 sections/save가 아예 나가지 않았다). - // B05 배수유역도에서 고친 관 목록(추가·이동·삭제)도 여기서 정본에 남긴다 — 예전에는 - // B05 [임시저장]에만 실려, B06 에서 저장하면 그 편집이 사라졌다(2026-09-06 대응표). - await flushPendingPipes(projectId).catch((error) => { - const detail = error instanceof Error ? ` ${error.message}` : ""; - showToast(`배수관 저장에 실패했습니다.${detail}`, "error"); - }); await flushPendingStructures(projectId).catch((error) => { const detail = error instanceof Error ? ` ${error.message}` : ""; showToast(`구조물 저장에 실패했습니다.${detail}`, "error"); diff --git a/B06_Section/B06_Section_UI_Page_Structures_Panel.ts b/B06_Section/B06_Section_UI_Page_Structures_Panel.ts index 3a790a29..e7c048dc 100644 --- a/B06_Section/B06_Section_UI_Page_Structures_Panel.ts +++ b/B06_Section/B06_Section_UI_Page_Structures_Panel.ts @@ -24,6 +24,7 @@ import { type StructureInstance, type StructureType, } from "../B05_Profile/B05_Profile_Api_Structures"; +import { readPendingPipes } from "../B05_Profile/B05_Profile_Api_Pipes_Draft"; import { fetchDetailPipePoints } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; import { readStructurePick, @@ -375,7 +376,10 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc structures = readPendingStructures(projectId) ?? stored.structures; section.setStructures(structures); const detail = deps.detail?.() ?? null; - pipeFacilities = pipeResponse.pipe_points.map((pipe) => ({ + // 저장하지 않고 넘어온 관 편집분이 있으면 그것으로 세운다 — 구조물과 같은 규칙이며, + // 이래야 B05 에서 넣고 B06 으로 넘어와도 같은 목록이 보인다(2026-09-12 사용자). + const pendingPipes = readPendingPipes(projectId); + pipeFacilities = (pendingPipes ?? pipeResponse.pipe_points).map((pipe) => ({ chainage_m: pipe.chainage_m, facility: pipe.facility ?? "pipe", start_m: pipe.start_m,