feat(구조물표): 323-6 바꿈 한 길 — 바뀜 신호 하나(구조물 초안도 설계 가게 · watchDrafts) · 종류 바꿈 표 규칙(swapsInPlace · E · F 점형 ↔ 구간형 = 지우고 같은 기준 측점에 새로) · 조용한 되돌림 없앰(알림) · 그리기가 조정값 안 만듦 · replayDraftPipeOptions 걷음

- 구조물 초안 쓰기 = useDesignStore().set("structures")(칸 그대로) → 관 초안과 같은 구독 · 횡단 보기 watchDrafts(구조물 = 늘 · 관 = 자리 · 종류 · 구간 · 잇기가 바뀔 때만 · 옵션만은 폼 길이 그 카드만 · 카드 줌 · 팬 지킴) · 옛 되부름 onStructuresChanged 걷음
- 종류 바꿈 = 표(catalog) swapsInPlace — 정본 다름 · 구조물 자리꼴 다름(E · F) = 지우고 같은 기준 측점에 새로(구간 = 기본 길이) · 같으면 종류 키만 · 막히면 옛 기록 · 옛 종류 폼 + 알림(전엔 [수정] 조기 반환 → 조용히 되돌림 · 드롭다운만 새 종류)
- syncApplied = 사용자가 조작한 세션이 있을 때만 깎음(카드 그리기가 자동 배치를 조정값으로 굳혀 [저장]으로 내보내던 것)
- replayDraftPipeOptions 걷음 — 그림 자료는 형상 얹기(초안 현황) · 다단 조정값은 조정 세션이 정본
- 시험: test_323_6_change(신호 · 거르기 · 표 규칙 · 그리기 · 걷음) · test_319 점형 ↔ 구간형 · 막힘 알림 · 되얹기 시험 = 형상 얹기로 · 옛 기대 글 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
This commit is contained in:
2026-10-14 18:21:41 +09:00
co-authored by Claude Opus 5.5
parent cb4bab7bf2
commit 529baca3ff
16 changed files with 352 additions and 96 deletions
@@ -9,6 +9,7 @@
import type { IrregularStation } from "../../B05_Profile/B05_Profile_UI_IrregularStations";
import type { PlannedStation } from "../../common_util/common_util_structure_station";
import { useDesignStore, type DesignStoreValues } from "./design_store";
/** 현황 — 종류마다 초안이 앞섬(초안 null = 만진 적 없음 · 빈 목록 = 다 지움). */
export function structureStatus<P, S>(
@@ -42,3 +43,35 @@ export function plannedIrregular(
};
});
}
/**
* 바뀜 신호 하나(323-6) — 관 · 구조물 초안을 구독(어느 길로 쓰든 설계 가게 한 곳). 구조물 = 늘 · 관 = 자리 · 종류 ·
* 구간 · 잇기가 바뀔 때만(옵션만 고친 것은 폼 길 `applyPipeOptions` 가 그 카드만 고침 — 카드 줌 · 팬 지킴).
* 돌려주는 함수로 끊음.
*/
export function watchDrafts(projectId: string | null, onChange: () => void): () => void {
const shop = useDesignStore(projectId, null);
type Pipe = NonNullable<DesignStoreValues["pipes"]>[number];
const layout = (pipes: ReadonlyArray<Pipe> | null): string =>
JSON.stringify(
(pipes ?? []).map((p) => [
p.chainage_m,
p.facility ?? "pipe",
p.start_m ?? null,
p.end_m ?? null,
p.summary_item ?? null,
p.group_items ?? null,
]),
);
let seen = layout(shop.get("pipes"));
const offs = [
shop.subscribe("structures", onChange),
shop.subscribe("pipes", (pipes) => {
const key = layout(pipes);
if (key === seen) return;
seen = key;
onChange();
}),
];
return () => offs.forEach((off) => off());
}
+5 -10
View File
@@ -37,14 +37,13 @@ import {
import { readStructurePick } from "../../B05_Profile/B05_Profile_UI_Structure_Pick_Session";
import { NO_SELECTION, PIPE_MATCH_M, selectDesign } from "./design_selection";
import { paintMapPick } from "./design_pick_profile";
import { readPendingPipes } from "../../B05_Profile/B05_Profile_Api_Pipes_Draft";
import { watchDrafts } from "./design_structure_status";
import { applyStructurePick } from "../../B06_Section/B06_Section_UI_Page_Structure_Pick";
import { bindCrossPick } from "../../B06_Section/B06_Section_UI_Page_Pick_Sync";
import { createSectionView } from "../../B06_Section/B06_Section_UI_Section_View";
import { revetFormOf, revetWallSpec } from "../../B06_Section/B06_Section_UI_Cross_Culvert_Const";
import {
applyPipeOptionsToCache,
replayDraftPipeOptions,
type PipeOptionsContext,
} from "../../B06_Section/B06_Section_UI_Page_Pipe_Options";
import {
@@ -145,7 +144,6 @@ export async function mountCrossView(
syncBermSpans(structures);
},
// 구조물(C군 벽)이 늘거나 줄면 그 측점 횡단 제원이 달라진다 — 다시 받아 그린다(2026-09-06).
onStructuresChanged: () => void refreshDetailForStructures(),
onPipesWritten: () => {
window.clearTimeout(pipeReloadTimer);
pipeReloadTimer = window.setTimeout(() => shared.profile?.side?.reload(), 200);
@@ -565,11 +563,10 @@ export async function mountCrossView(
void refreshDetailForStructures(); // 안에서 낡은 설계도 맞춤(314)
});
// 탭이 다시 보일 때 정의 판이 바뀌었으면 종류 · 패널 다시 읽음(322-0)
host.onDispose(
onStaleStructureTypes(() => {
if (host.visible()) void structuresPanel.load().then(restoreStructurePick);
}),
);
const reloadPanel = (): void => void structuresPanel.load().then(restoreStructurePick);
host.onDispose(onStaleStructureTypes(() => host.visible() && reloadPanel()));
// 바뀜 신호 하나(323-6 · `watchDrafts`) — 초안이 어느 길로 바뀌든 보일 때 카드 · 그림 다시(옛 되부름 갈음)
host.onDispose(watchDrafts(projectId, () => host.visible() && void refreshDetailForStructures()));
host.onDispose(() => {
window.clearTimeout(pipeReloadTimer);
window.clearTimeout(wallAdjustTimer);
@@ -669,8 +666,6 @@ export async function mountCrossView(
bindPipeOptions(shared, (chainageM, patch) =>
applyPipeOptionsToCache(pipeOptionsContext, chainageM, patch),
);
// 새로고침 뒤 캐시 상세는 저장분 — 저장 전에 고친 관 옵션을 초안에서 다시 얹는다(24-7a).
replayDraftPipeOptions(pipeOptionsContext, readPendingPipes(projectId));
renderSectionDetail();
// 측점이 없는 구조물 알림 — 관을 나중에 놓으면 그 측점이 안 생겨 수량에서 조용히 빠진다
// (계획서 3-14 ㉯). 만드는 것은 사용자가 누를 때만.