Files
Aislo/B05_Profile/B05_Profile_Api_PlusStations.ts
T
eomsangdonandClaude Opus 5.5 22bf1e9822 fix(459 노선 바뀜): [저장] 구조물 초안을 서버 따라감 위에 · 추가 측점 초안 동안 좌표 따라감 · 옮긴 시설 측점 427 꼭짓점 세움 · 관 따라감 선 = points
- 구조물: 노선 저장 뒤 rebaseStructureDraft(관 rebasePipeDraft 짝) — 따라가기만 한 것 = 서버 자리 · 사용자가 옮긴 것 = 초안
- 추가 측점: follow 단계가 좌표로 옮긴 정수 m 를 patch.plusStations 로(FOLLOW_KEYS · stations 단계 입력값) · followPlusDraft(가게 쓰기 · 초안 버림 되돌림 · page 부름은 sub7) · follow 만 쓴 초안은 [저장]이 PUT 안 함
- 427: 초안 planline · 서버 _carry_profile 이 바뀐 구간 관 · 구조물 측점(+ 초안 중 사용자가 옮긴 것)에 seatBornNodes 로 다시 세움(route_moved_stations · seat_route_moved 짝)
- 서버 관 따라감 = 구조물과 같은 points(사용 범위 자름) · 좌표 없는 옛 관은 옛 노선 자리로 먼저 · 따라감 실패 = follow_failed 알림
- 시험 test_459_route_seat_mirror(거울 · 임시 storage · _place_pipes) · 헬퍼 400 · 449 · design_store_save 가짜 창구

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:50:43 +09:00

119 lines
5.5 KiB
TypeScript

/* =============================================================================
* B05_Profile_Api_PlusStations.ts
* 추가 측점(368-2 · 요구 240-21) — 종단 원지반선에서 넣은 일반 측점(정수 m · 이름 없음).
*
* 초안 = 설계 가게 `plusStations`(세션 · 되돌리기 사진첩에 듦) · 저장분 = 서버 `plus_stations.json`
* (`GET/PUT /section/plus-stations`) · [저장] 차례 = 구조물 뒤 · 계획고 앞(`design_save`).
* 측점 계획(`planStations`)이 읽는 곳 = 초안 ?? 저장분(`setPlusStations` · 부를 때마다 읽음).
* ========================================================================== */
import { API_BASE_URL, API_TIMEOUT_MS } from "../config/config_frontend";
import { draftProjectId, useDesignStore } from "../A00_Common/design/design_store";
import { onPageHistory } from "./B05_Profile_UI_Profile_History";
const saved = new Map<string, number[]>();
const loaded = new Map<string, Promise<void>>();
async function request(projectId: string, init: RequestInit = {}): Promise<number[]> {
const controller = new AbortController();
const timer = window.setTimeout(() => controller.abort(), API_TIMEOUT_MS);
try {
const response = await fetch(`${API_BASE_URL}/projects/${projectId}/section/plus-stations`, {
credentials: "include",
headers: { "Content-Type": "application/json" },
signal: controller.signal,
...init,
});
const payload = await response.json().catch(() => null);
if (!response.ok)
throw new Error(payload?.message ?? `요청이 실패했습니다 (${response.status}).`);
return Array.isArray(payload?.chainages) ? payload.chainages.map(Number) : [];
} finally {
window.clearTimeout(timer);
}
}
export const readPlusDraft = (projectId: string): number[] | null =>
useDesignStore(projectId, null).get("plusStations");
/** 지금 추가 측점 — 초안이 있으면 초안(빈 목록 = 다 지움) · 없으면 저장분. */
export const plusStationsOf = (projectId: string): number[] =>
readPlusDraft(projectId) ?? saved.get(projectId) ?? [];
/** 초안을 씀(정렬 · 같은 m 하나) — 바뀜 신호로 B05 종단 · B06 목록이 다시 맞춤. */
export function writePlusDraft(projectId: string, next: number[] | null): void {
const list = next && [...new Set(next.map(Math.round))].sort((a, b) => a - b);
useDesignStore(projectId, null).set("plusStations", list);
}
/** 저장분이 바뀜 — 듣는 쪽을 부름(초안 칸 값 그대로 다시 씀). */
function notify(projectId: string): void {
useDesignStore(projectId, null).set("plusStations", readPlusDraft(projectId));
}
// 되돌리기 · 다시하기는 세션을 바로 되씀 — 바뀜 신호를 다시 냄(종단 측점선 · B06 목록) · 다른 되살림이
// 측점 목록을 다시 세운 뒤(같은 차례 안에서 덮이지 않게)
onPageHistory({ restore: () => void setTimeout(() => notify(draftProjectId()), 0) });
/** 저장분을 한 번 받음(못 받으면 빈 목록) — 읽는 곳 두기(`setPlusStations`)는 횡단 상세 가게 몫. */
export function loadSavedPlusStations(projectId: string): Promise<void> {
if (!loaded.has(projectId))
loaded.set(
projectId,
request(projectId)
.then((list) => {
saved.set(projectId, list);
if (list.length) notify(projectId);
})
.catch(() => undefined),
);
return loaded.get(projectId)!;
}
/** 노선 초안 전 추가 측점 초안(없으면 null) — 초안을 버리면 이것으로(459). */
const beforeRoute = new Map<string, number[] | null>();
/** follow 만 쓴 초안(사용자 초안 없던 때) — 그대로면 [저장]이 PUT 안 함(서버 따라감이 정본 · 459). */
const followedOnly = new Map<string, string>();
/**
* 노선 초안 동안 추가 측점 따라감(459) — 묶음 follow 출력(`plusStations` · 새 노선 정수 m)을 초안에 씀 ·
* `next = null` = 노선 초안을 버림(초안 전 값으로). 부르는 곳 = design_pipeline_page follow 구독
* (관 · 구조물 `writeFacilities` 곁 · 초안 버림 갈래도).
*/
export function followPlusDraft(
projectId: string,
next: ReadonlyArray<number> | null | undefined,
): void {
if (next === null) {
if (!beforeRoute.has(projectId)) return;
writePlusDraft(projectId, beforeRoute.get(projectId)!);
beforeRoute.delete(projectId);
followedOnly.delete(projectId);
return;
}
if (!next) return;
if (!beforeRoute.has(projectId)) beforeRoute.set(projectId, readPlusDraft(projectId));
writePlusDraft(projectId, [...next]);
if (beforeRoute.get(projectId)) followedOnly.delete(projectId);
else followedOnly.set(projectId, JSON.stringify(readPlusDraft(projectId)));
}
/** [저장] 차례 — 사용자 초안이 있으면 정본에 씀 · 없으면(follow 만 쓴 초안 포함 · 459) 저장분만 다시 받음
* (노선 저장이 좌표로 옮겼을 수 있음). */
export async function flushPlusStations(projectId: string): Promise<void> {
const auto = followedOnly.get(projectId);
followedOnly.delete(projectId);
beforeRoute.delete(projectId);
const kept = readPlusDraft(projectId);
if (kept && auto === JSON.stringify(kept))
useDesignStore(projectId, null).set("plusStations", null);
const draft = readPlusDraft(projectId);
const list = draft
? await request(projectId, { method: "PUT", body: JSON.stringify({ chainages: draft }) })
: await request(projectId);
saved.set(projectId, list);
loaded.set(projectId, Promise.resolve());
if (draft) useDesignStore(projectId, null).set("plusStations", null);
else notify(projectId);
}