feat(B06): 종단 구조물 표시를 B05 와 통일 — 같은 알약 레인

PLAN 2-5 (2026-09-07 사용자 지시 4 「구조물 표시 통일」). B06 종단에는 구조물 마크가
아예 없었고 배수관·세월교만 다른 계통으로 그려졌음.

- B05 가 쓰는 **같은 부품**(`buildStructureLane`)을 B06 종단 그래프 아래에 붙임.
  이름·중복 번호·툴팁·약호가 전부 B05 규칙 그대로 따라옴.
- 종단 차트가 `toX`(누가거리 → px)를 내보내 레인이 그래프와 같은 자리를 씀.
- 좌측 「구조물 배치」가 목록을 받을 때마다 `setStructureMarks` 로 넘김(구조물 정본 +
  관 정본). 관을 가상 구조물로 바꾸는 `pipesToStructureMarks` 는 B05 안에 있던 것을
  공용 자리(`B05_Profile_Api_Structures`)로 올려 **한 벌만** 둠.
- 알약을 누르면 그 자리에 가장 가까운 측점 카드를 고름(좌측 목록 클릭과 같은 규칙).
  끌기는 B05 몫이라 여기서는 자리만 보임(배수유역 재분할이 걸림).
- 검증 훅 `window.__b06Marks` 추가(`__corridorBuild` 등과 같은 용도).

자체검증 — 실화면 B06 종단에 레인 1줄·알약 10개(「배수관 1~8 · 세월교 1~2」)가 B05 와
같은 표기로 뜸. 훅 값 `{structures:10, types:36}`. tsc 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-07 08:20:59 +09:00
co-authored by Claude Opus 5
parent 6b76a3f273
commit 679726124b
5 changed files with 117 additions and 1 deletions
@@ -18,9 +18,11 @@ import {
fetchStructures,
fetchStructureTypes,
readPendingStructures,
pipesToStructureMarks,
structureAnchorM,
writePendingStructures,
type StructureInstance,
type StructureType,
} from "../B05_Profile/B05_Profile_Api_Structures";
import { fetchDetailPipePoints } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
import {
@@ -62,6 +64,8 @@ export interface B06StructuresPanelDeps {
focusChainage: (chainageM: number) => void;
/** 바깥(횡단도·3D 넘김값)에서 고른 것이 폼에 실릴 때 — 접힌 좌측 패널을 펼친다. */
reveal?: () => void;
/** 목록이 바뀔 때마다 종단 알약 레인에 같은 목록을 넘긴다(2026-09-07 표시 통일). */
onMarks?: (structures: StructureInstance[], types: StructureType[]) => void;
/** 폼 [수정]으로 바뀐 관 옵션을 캐시에 예약한다 — [저장]·[확정]이 정본에 쓴다
* (2026-08-29: 조정창 구간값과 같은 경로). 없으면 안내만 한다. */
queuePipeOptions?: (chainageM: number, patch: Record<string, number | string>) => void;
@@ -226,10 +230,15 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc
item.structure_id ? item : { ...item, structure_id: crypto.randomUUID().replace(/-/g, "") },
);
let markTypes: StructureType[] = [];
/** 종단 알약 레인에 올릴 목록 — 구조물 정본 + 관 정본(가상 구조물). */
const pushMarks = (): void =>
deps.onMarks?.([...structures, ...pipesToStructureMarks(pipeFacilities)], markTypes);
const section = createStructuresSection({
onChange: (next) => {
structures = withLocalIds(next);
section.setStructures(structures);
pushMarks();
if (deps.projectId) writePendingStructures(deps.projectId, structures);
deps.onStructuresChanged?.();
},
@@ -255,6 +264,7 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc
if (hit) hit.chainage_m = toChainageM;
currentChainageM = toChainageM;
section.setPipeFacilities(pipeFacilities);
pushMarks();
showToast(PIPE_MOVE_NOTICE, "success");
} else {
showToast(PIPE_MOVE_GUIDE, "error");
@@ -321,6 +331,7 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc
fetchDetailPipePoints(projectId),
]);
section.setTypes(types);
markTypes = types;
// 저장하지 않고 나갔던 조작분이 있으면 그것으로 화면을 세운다(B05와 같은 규칙).
structures = readPendingStructures(projectId) ?? stored.structures;
section.setStructures(structures);
@@ -348,6 +359,8 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc
)?.design_flow_m3s ?? null,
}));
section.setPipeFacilities(pipeFacilities);
// 종단 알약 레인 — 구조물 정본 + 계곡 통과 시설(관 정본)을 합쳐 B05 와 같은 표기로.
pushMarks();
// 목록이 늦게 도착하면 그 사이에 고른 시설은 강조될 자리가 없었다 — 다시 세운다
// (2026-09-04 사용자 보고: B06 좌측 목록만 하이라이트가 안 붙음). 폼에 아직 아무것도
// 없으면 세션에 남은 선택(카드형 — 부재키 없는 측점)도 같은 규칙으로 세운다.