- 원인: 화면 attach · 서버 carry_facility_attributes 가 거리 제한 없이 가장 가까운 관에 속성을 붙여, 관 수 > 속성 수이거나 관을 이웃 쪽으로 옮기면 BOX · 세월교가 옆 관 측점으로 넘어감 - 고침: 화면 carryIndices ↔ 서버 carry_indices 짝 — 허용 오차 0.05m 안 가장 가까운 원본만 · 한 원본 한 번 · 못 붙인 관은 속성 없음(추천은 기존대로 빈칸에만) · 두 파일 머리에 짝 표기 - 관을 옮기면 속성이 따라감: 지도 끌기(onMove → move) · 되돌리기 · 구조물 폼(follow — 사라진 관 하나 · 새 관 하나일 때) - B06 옛 「관 이동 예약」(culvertmove): 저장 때 읽지도 적용하지도 않음 — flushCulvertOptions · createCulvertOptionWriter 에서 이동 키 제거 - 시험: 거울 시험 test_b05_facility_carry(화면 · 서버 같은 답 · 끌기 · 되돌리기) · 옛 최근접 · 이동 예약 적용 시험을 새 규칙으로 고침 - 확인: ORCA 5180 랩탑_보조 — 세월교(173.09) 8m 앞 165m 에 옵션 없는 관 추가 → 저장 → pipe_points.json 165 = 속성 없음 · 173.09 = ford_bridge · 나머지 11건 전후 같음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RrQ65VtGZbae2VKgYVMhmc
110 lines
4.8 KiB
TypeScript
110 lines
4.8 KiB
TypeScript
/* =============================================================================
|
|
* B06_Section_Api_Culvert_Options.ts
|
|
* B06 조정창에서 만진 **배수관 세트 구간값**(기슭막이·집수정 길이·전/후)을
|
|
* 원천인 `pipe_points.json`에 되돌려 쓴다(2026-08-24 사용자).
|
|
*
|
|
* 값을 B06 정본(design)에 따로 담지 않는 이유: 같은 숫자가 B05 배수관 카드에도
|
|
* 있어서 두 곳이 갈리면 어느 쪽이 참인지 알 수 없다. 원천은 하나여야 한다.
|
|
*
|
|
* 조작분은 **세션에만** 쌓고 영구저장은 [저장]·[확정]에서만 한다(CLAUDE.md 5장
|
|
* 조작·데이터 흐름 정책, 2026-08-29 사용자 확정). 종전 800ms 디바운스 자동 PUT은
|
|
* 폐지했다 — 저장을 누르지 않았는데 영구저장소가 바뀌면 [초기화]의 기준선이 흐려진다.
|
|
* 세션에 담는 이유는 B06에서 만진 값을 B05 [저장]으로도 내보내야 하기 때문이다.
|
|
* ========================================================================== */
|
|
|
|
import {
|
|
fetchDetailPipePoints,
|
|
saveDetailPipePoints,
|
|
} from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
|
|
import type { DetailPipeInput } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
|
|
|
|
/** 측점 하나에 얹을 옵션 조각 — 키는 `pipe_points` 옵션 키 그대로.
|
|
* 값은 수치가 대부분이나 날개벽 설치("있음"/"없음")처럼 문자열도 있다. */
|
|
export type CulvertOptionPatch = Record<string, number | string>;
|
|
|
|
/** 세션에 쌓인 조각 — 측점(누가거리 2자리 문자열) → 옵션 조각. */
|
|
type PendingMap = Record<string, CulvertOptionPatch>;
|
|
|
|
export interface CulvertOptionWriter {
|
|
/** 측점 옵션을 세션에 예약한다. 같은 측점의 앞선 예약과는 합쳐진다. */
|
|
queue: (chainageM: number, patch: CulvertOptionPatch) => void;
|
|
/** 예약분을 즉시 내보낸다([저장]·[확정]에서만 부른다). */
|
|
flush: () => Promise<void>;
|
|
}
|
|
|
|
const keyOf = (chainageM: number): string => chainageM.toFixed(2);
|
|
|
|
function readPending(sessionKey: string | null): PendingMap {
|
|
if (!sessionKey) return {};
|
|
try {
|
|
const raw = window.sessionStorage.getItem(sessionKey);
|
|
return raw ? (JSON.parse(raw) as PendingMap) : {};
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
function writePending(sessionKey: string | null, pending: PendingMap): void {
|
|
if (!sessionKey) return;
|
|
try {
|
|
if (Object.keys(pending).length === 0) window.sessionStorage.removeItem(sessionKey);
|
|
else window.sessionStorage.setItem(sessionKey, JSON.stringify(pending));
|
|
} catch {
|
|
/* 세션 저장 실패는 무시 — 값은 화면 캐시에 남아 있다. */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 세션에 쌓인 구간값을 `pipe_points.json`에 한 번에 쓴다. 예약분이 없으면 아무것도
|
|
* 하지 않는다. 성공하면 세션을 비우고, 실패하면 **그대로 두어** 다음 [저장]에서
|
|
* 다시 시도한다 — 조용히 잃으면 화면과 저장분이 갈린다.
|
|
* 옛 「관 이동 예약」(culvertmove)은 읽지 않음 — 관 위치는 B05·B06 이 같은 관 목록 스냅샷으로만
|
|
* 옮김. 옛 세션 잔값을 저장 때 적용하면 관이 이웃 측점으로 튀어 시설 속성이 넘어감.
|
|
*/
|
|
export async function flushCulvertOptions(
|
|
projectId: string | null,
|
|
sessionKey: string | null,
|
|
): Promise<void> {
|
|
const pending = readPending(sessionKey);
|
|
if (!projectId || Object.keys(pending).length === 0) return;
|
|
const current = await fetchDetailPipePoints(projectId);
|
|
let touched = false;
|
|
const points: DetailPipeInput[] = current.pipe_points.map((point) => {
|
|
// 좌표는 서버가 다시 계산하므로 싣지 않는다(DetailPipeInput 규약).
|
|
const { lonlat: _lonlat, ...input } = point;
|
|
const patch = pending[keyOf(point.chainage_m)];
|
|
if (!patch) return input;
|
|
touched = true;
|
|
return { ...input, options: { ...(input.options ?? {}), ...patch } };
|
|
});
|
|
if (touched) await saveDetailPipePoints(projectId, points);
|
|
writePending(sessionKey, {});
|
|
}
|
|
|
|
/**
|
|
* 저장기 하나를 만든다. `projectId`·`sessionKey`가 없으면 예약만 메모리에 남는다
|
|
* (조회 전용 상태). `onError`는 [저장] 시점 실패를 화면에 알리는 쪽이 구현한다.
|
|
*/
|
|
export function createCulvertOptionWriter(
|
|
projectId: () => string | null,
|
|
sessionKey: () => string | null,
|
|
onError?: (message: string) => void,
|
|
): CulvertOptionWriter {
|
|
return {
|
|
queue(chainageM, patch) {
|
|
const key = sessionKey();
|
|
const pending = readPending(key);
|
|
pending[keyOf(chainageM)] = { ...(pending[keyOf(chainageM)] ?? {}), ...patch };
|
|
writePending(key, pending);
|
|
},
|
|
async flush() {
|
|
try {
|
|
await flushCulvertOptions(projectId(), sessionKey());
|
|
} catch (error) {
|
|
onError?.(error instanceof Error ? error.message : "배수관 구간값 저장 실패");
|
|
throw error;
|
|
}
|
|
},
|
|
};
|
|
}
|