fix(B05·B06): 유령 계획고 편집 근본 막음 — 옮긴 관 편집 따라감 · [저장] 때 자리 없는 편집 버림 · 알림(213-2)

- ① 관 옮김 = 편집 키도 새 자리로: B06 movePipeTo 가 resetStationEdit(옛, 새) · B05 측점 갱신이 사라진 것 하나 · 새것 하나(또는 같은 id · 0.006 m 안)를 짝으로 옮김(followedStation) — 세션 초안도 새 키라 옛 키가 다시 안 나감
- ② [저장](PUT profile-alignment)에서 서버가 측점 · 관 · 구조물 자리 어디에도 없는 편집 키를 버림(drop_unplaced_edits · 저장 차례가 관 · 구조물 뒤라 최신) · 응답 dropped_edit_chainages · 화면 알림 「계획고 편집 N곳을 지웠습니다」 · 자리 못 읽으면 안 버림
- ③ test_192_1 = 고정 사본(fixtures/b05_192_1_moved_pipe_longitudinal.json · 226fc805 460~840 m) · 새 시험 test_213_2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3mdb91125mPoC6tpsQx4P
This commit is contained in:
2026-10-10 15:44:28 +09:00
co-authored by Claude Opus 5.5
parent 002165f040
commit ffa7f9d236
13 changed files with 2906 additions and 34 deletions
+2
View File
@@ -86,6 +86,7 @@ import type { RouteProfilePanelCallbacks } from "../../B05_Profile/B05_Profile_U
import { attachWheelHorizontalScroll } from "../../B05_Profile/B05_Profile_UI_Profile_Wheel";
import { needsFullRedraw, type ElevationWindowResult } from "@util/common_util_chart_ywindow";
import type { DesignSideBoard, DesignSideFactory, DesignView } from "./design_profile_slots";
import { noticeDroppedEdits } from "./design_save";
import "../../B06_Section/B06_Section_UI_Style.css";
// SVG 차트 색상(.b06-chart__*)의 정의처는 _Style_Cross.css다. 이걸 빼면 B05로 바로 진입했을 때
// 배경 rect가 브라우저 기본 fill(검정)로 그려진다 — B06을 먼저 방문해야 정상으로 보이던 원인.
@@ -650,6 +651,7 @@ export function createDesignProfile(ctx: DesignProfileContext) {
async save(): Promise<void> {
if (!routeId || !store.dirty()) return;
const saved = await saveProfileAlignment(projectId, routeId, store.edits());
noticeDroppedEdits(saved);
const next = saved.profile_alignment as ProfileAlignment | undefined;
if (next?.base_pvi?.length) {
base = toAlignmentBase(next);
+16 -1
View File
@@ -65,11 +65,26 @@ function reportOnly(label: string): (error: unknown) => void {
};
}
/** 서버가 버린 계획고 편집 자리(측점 · 관 · 구조물이 없는 자리 · 213-2)를 알린다. */
export function noticeDroppedEdits(saved: { dropped_edit_chainages?: number[] }): void {
const dropped = saved.dropped_edit_chainages ?? [];
if (!dropped.length) return;
const where = dropped
.slice(0, 5)
.map((value) => `${value.toFixed(2)}m`)
.join(" · ");
showToast(
`계획고 편집 ${dropped.length}곳을 지웠습니다 — 측점 · 관 · 구조물이 없는 자리: ${where}` +
(dropped.length > 5 ? ` 외 ${dropped.length - 5}곳` : ""),
"info",
);
}
/** 계획고 초안(가게 하나)을 종단 정본에 쓴다. 없으면 아무 일도 하지 않는다. */
async function flushAlignmentDraft(projectId: string, routeId: number): Promise<void> {
const draft = pendingAlignment(routeId);
if (!draft) return;
await saveProfileAlignment(projectId, routeId, draft);
noticeDroppedEdits(await saveProfileAlignment(projectId, routeId, draft));
markAlignmentSaved(routeId);
}