feat(종단보기): 346-3 [노선 반영] 없앰 — [저장]이 노선부터(POST /route/replan · draft = 계약 RouteDraft · 안 바뀌면 안 보냄) · 뒤 차례는 새 노선 · 횡단 초안 측점 열쇠로 넘김 · 결과 한 줄
- design_save 차례 = 노선 → 관 → 구조물 → 계획고(옛 노선 칸 → 새 노선) → 암거 옵션 → 상단측 → 횡단(carryStationDrafts 뒤 저장) · 잠금 막 = [저장] 하나 · 실패 = 멈춤 · 초안 그대로 · 오류 한 줄 - [확정] 중 노선이 바뀌면 노선만 저장하고 다시 세움(새 노선으로 다시 [확정]) - _RouteEdit_Apply = 노선 저장 이음(편집기 등록부 · pendingRouteSave · saveRouteDraft · carryAfterRouteSave · routeSaveSummary · rebuildAfterRouteSave) · 단추 듣기 없음 - 결과 한 줄 = 관 지킴 · 새 교차 관 · 오목 보충 · 구조물 옮김 · 멀어짐 · 노선 밖 고침(서버 cross_dropped + 화면) · 배수유역 낡음 - RE_Chrome [노선 반영] 지움 · [반영 전으로] → [저장 전으로] · replanRoute 본문 = RouteSaveBody - 시험: test_design_store_save 차례 넷 더함 · 꼴 시험 셋 고쳐 씀 · 323_9 대역 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC34fsxxbweEG3RUjs3Mn
This commit is contained in:
@@ -2,7 +2,13 @@
|
||||
* A00_Common/design/design_save.ts
|
||||
* B05 · B06 **저장 한 줄기**(계획서 24-1) — [저장] · [확정]이 어느 보기에서 눌려도 같은 차례.
|
||||
*
|
||||
* 관 → 구조물 → 계획고 → 암거 옵션 → 오르막(상단측) → 횡단
|
||||
* 노선 → 관 → 구조물 → 계획고 → 암거 옵션 → 오르막(상단측) → 횡단
|
||||
*
|
||||
* 노선(346-3 · [노선 반영] 없앰) — 노선 초안이 마지막 저장과 다를 때만 맨 앞에서 `POST /route/replan`
|
||||
* (draft = 계약 `RouteDraft`). 서버가 노선 → 관 · 구조물 따라감 → 종횡단 → 계획고 · 횡단 이월까지 돌고
|
||||
* 새 노선 번호를 준다. 그 뒤 차례는 **새 노선**으로: 관 · 구조물 초안(좌표 정본) · 계획고 초안(옛 노선 칸 →
|
||||
* 새 노선) · 횡단 초안은 측점 열쇠로 넘겨(`carryStationDrafts`) 저장 · 결과 한 줄 · 페이지 다시 세움.
|
||||
* 노선 저장이 실패하면 서버가 되살리고 여기서 멈춤(다른 초안도 화면 · 세션에 그대로 · 오류 한 줄).
|
||||
*
|
||||
* 차례는 여기 한 곳에만 둔다. 보기마다 다른 것은 **화면이 쥔 값으로 한 번 더 맞추는 일**
|
||||
* (B05 배수유역도 목록 · 구조물 목록 새로 받기 · 종단 패널 다시 그리기)과 횡단 저장 본문뿐이라,
|
||||
@@ -26,9 +32,17 @@ import {
|
||||
} from "../../B05_Profile/B05_Profile_UI_Profile_Edit";
|
||||
import { flushCulvertOptions } from "../../B06_Section/B06_Section_Api_Culvert_Options";
|
||||
import { clearState, stateKey } from "../b_page_state";
|
||||
import {
|
||||
carryAfterRouteSave,
|
||||
pendingRouteSave,
|
||||
rebuildAfterRouteSave,
|
||||
routeSaveSummary,
|
||||
saveRouteDraft,
|
||||
} from "../../B05_Profile/B05_Profile_UI_RouteEdit_Apply";
|
||||
|
||||
/** 저장 차례 — 이 순서 그대로 돈다. */
|
||||
/** 저장 차례 — 이 순서 그대로 돈다(`route` 는 노선 초안이 있을 때만). */
|
||||
export const DESIGN_SAVE_ORDER = [
|
||||
"route",
|
||||
"pipes",
|
||||
"structures",
|
||||
"alignment",
|
||||
@@ -48,7 +62,7 @@ export interface DesignSaveContext {
|
||||
* 차례마다 **기본 창구 대신** 도는 일. 없으면 기본 창구(세션 초안 → 정본)가 돈다.
|
||||
* `pipes` 만 예외 — 기본(세션 초안)이 먼저 나가고 그 뒤에 돈다(화면 목록으로 한 번 더 맞춤).
|
||||
*/
|
||||
steps?: Partial<Record<Exclude<DesignSaveStep, "cross">, () => Promise<void>>>;
|
||||
steps?: Partial<Record<Exclude<DesignSaveStep, "cross" | "route">, () => Promise<void>>>;
|
||||
/** 횡단 — 보기마다 본문이 다르다. `confirm` 이면 단계를 닫는 길로. */
|
||||
cross: (confirm: boolean) => Promise<void>;
|
||||
/** 모두 나간 뒤 — 캐시 비우기 · 화면 다시 읽기 · 알림 · 다음 단계. */
|
||||
@@ -87,12 +101,17 @@ export function noticeDroppedEdits(saved: { dropped_edit_chainages?: number[] })
|
||||
);
|
||||
}
|
||||
|
||||
/** 계획고 초안(가게 하나)을 종단 정본에 쓴다. 없으면 아무 일도 하지 않는다. */
|
||||
async function flushAlignmentDraft(projectId: string, routeId: number): Promise<void> {
|
||||
const draft = pendingAlignment(routeId);
|
||||
/** 계획고 초안(가게 하나)을 종단 정본에 쓴다. 없으면 아무 일도 하지 않는다. 노선이 바뀌었으면
|
||||
* 옛 노선 칸(`from`)의 초안을 새 노선(`routeId`)에 — 측점이 없는 자리는 서버가 버리고 알림(213-2). */
|
||||
async function flushAlignmentDraft(
|
||||
projectId: string,
|
||||
routeId: number,
|
||||
from: number = routeId,
|
||||
): Promise<void> {
|
||||
const draft = pendingAlignment(from);
|
||||
if (!draft) return;
|
||||
noticeDroppedEdits(await saveProfileAlignment(projectId, routeId, draft));
|
||||
markAlignmentSaved(routeId);
|
||||
markAlignmentSaved(from);
|
||||
}
|
||||
|
||||
/** 실패해도 다음 차례로 넘어가는 창구와 그 이름(보기가 준 일에도 같이 씌운다). */
|
||||
@@ -104,9 +123,10 @@ const TOLERANT: Partial<Record<DesignSaveStep, string>> = {
|
||||
|
||||
/** 차례마다 기본 창구. 암거 옵션 · 상단측은 조용히 넘어간다(다음 [저장]에서 다시 나감). */
|
||||
function defaultStep(
|
||||
step: Exclude<DesignSaveStep, "cross">,
|
||||
step: Exclude<DesignSaveStep, "cross" | "route">,
|
||||
projectId: string,
|
||||
routeId: number,
|
||||
fromRouteId: number = routeId,
|
||||
): () => Promise<void> {
|
||||
switch (step) {
|
||||
case "pipes":
|
||||
@@ -114,10 +134,10 @@ function defaultStep(
|
||||
case "structures":
|
||||
return () => flushPendingStructures(projectId);
|
||||
case "alignment":
|
||||
return () => flushAlignmentDraft(projectId, routeId);
|
||||
return () => flushAlignmentDraft(projectId, routeId, fromRouteId);
|
||||
case "culvertOptions":
|
||||
return () =>
|
||||
flushCulvertOptions(projectId, stateKey("culvertopt", projectId, routeId)).catch(
|
||||
flushCulvertOptions(projectId, stateKey("culvertopt", projectId, fromRouteId)).catch(
|
||||
() => undefined,
|
||||
);
|
||||
case "uphill":
|
||||
@@ -134,19 +154,34 @@ export async function saveDesign(
|
||||
options: { confirm?: boolean } = {},
|
||||
): Promise<boolean> {
|
||||
const confirm = options.confirm ?? false;
|
||||
const routeId = ctx.routeId();
|
||||
if (routeId === null) return false;
|
||||
const fromRouteId = ctx.routeId();
|
||||
if (fromRouteId === null) return false;
|
||||
let routeId = fromRouteId;
|
||||
/** 노선을 새로 저장했으면 그 결과 — 뒤 차례는 새 노선 · 화면 몫(옛 노선 판) 일은 건너뜀. */
|
||||
let routeSaved: Awaited<ReturnType<typeof saveRouteDraft>> | null = null;
|
||||
showLoadingOverlay();
|
||||
saving.add(ctx.projectId);
|
||||
try {
|
||||
await ctx.prepare?.();
|
||||
for (const step of DESIGN_SAVE_ORDER) {
|
||||
if (step === "cross") {
|
||||
await ctx.cross(confirm);
|
||||
if (step === "route") {
|
||||
const body = await pendingRouteSave(ctx.projectId); // 안 바뀌었으면 null — 안 보냄
|
||||
if (!body) continue;
|
||||
routeSaved = await saveRouteDraft(ctx.projectId, body); // 실패 = throw(서버가 되살림)
|
||||
if (routeSaved.route_id != null) routeId = routeSaved.route_id;
|
||||
continue;
|
||||
}
|
||||
const own = ctx.steps?.[step];
|
||||
const base = defaultStep(step, ctx.projectId, routeId);
|
||||
if (step === "cross") {
|
||||
if (!routeSaved) await ctx.cross(confirm);
|
||||
else {
|
||||
const outside = await carryAfterRouteSave(ctx.projectId, fromRouteId, routeId);
|
||||
showToast(routeSaveSummary(routeSaved, outside), "info");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 노선이 바뀌면 화면 몫 일(옛 노선 판 목록 · 계획고 판)은 건너뛰고 기본 창구만 새 노선으로.
|
||||
const own = routeSaved ? undefined : ctx.steps?.[step];
|
||||
const base = defaultStep(step, ctx.projectId, routeId, fromRouteId);
|
||||
const run =
|
||||
step === "pipes"
|
||||
? async () => {
|
||||
@@ -159,6 +194,14 @@ export async function saveDesign(
|
||||
}
|
||||
clearState("structure-type-cache", ctx.projectId); // 종류별 캐시는 저장 뒤 비움(54-32)
|
||||
saving.delete(ctx.projectId); // 다 나감 — 뒤처리는 새 상세로 그림
|
||||
if (routeSaved) {
|
||||
// 새 노선 — [확정]은 새 노선을 본 뒤 다시(단계 닫기는 새 횡단 본문이 있어야 함) · 페이지 다시 세움.
|
||||
if (confirm)
|
||||
showToast("노선을 저장했습니다 — 새 노선을 확인한 뒤 다시 [확정]하세요.", "info");
|
||||
else await ctx.done?.(false);
|
||||
await rebuildAfterRouteSave(ctx.projectId);
|
||||
return true;
|
||||
}
|
||||
await ctx.done?.(confirm);
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user