fix(B06): 저장된 표준 횡단면이 새 탭에서 화면에 안 서던 것
표준단면 편집값은 sessionStorage 에만 살아 **탭을 새로 열면** 사라졌음. 그때 브라우저는 config 기본값으로, 서버는 저장분으로 계산해 **같은 측점이 갈렸음**. 표준단면은 모든 측점의 횡단 모양을 정하므로 면적·유토곡선·수량까지 그대로 흐름. - `sections/context` 가 저장분을 **한 칸 더** 실어 보냄(`stored_standard_cross_section`). 기존 `standard_cross_section`(config 기본값)은 그대로 둠 — 옛 화면 안 깨짐, 마이그레이션 없음. - 브라우저는 두 화면이 함께 지나는 한 자리(`fetchSectionContext`)에서 **세션이 비었을 때만** 그 값으로 세움. 그 탭에서 고친 값이 있으면 안 건드림(초안 우선). 실측(용화 5601e828 · route 169): - 고치기 전 — 저장분 암반 횡단경사 **5%** · 측구 상단폭 **0.9m** 인데 화면이 받는 값은 **3%** · **0.69m**(config 기본값)였음. 응답에 저장분 칸 자체가 없었음. - 고친 뒤 — 응답에 0.9·5 가 실리고, **빈 새 탭**에서 B06 을 열면 세션이 `rock.ditch.top_width_m=0.9` · `rock.cross_slope_pct.max=5` 로 섬(검증 탭은 닫음). 시험 `test_b06_stored_standard_reaches_browser.py` 3건 — 칸이 따로 있고 기본은 None · 라우터가 저장분을 실음 · 브라우저가 세션이 빈 경우에만 세움(두 갈래 모두). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -63,13 +63,35 @@ async function requestJson<T>(path: string, init: RequestInit): Promise<T> {
|
|||||||
* (2026-09-06 호출 정리). 노선이 바뀌면 `clearSectionContextCache` 로 버린다. */
|
* (2026-09-06 호출 정리). 노선이 바뀌면 `clearSectionContextCache` 로 버린다. */
|
||||||
export async function fetchSectionContext(projectId: string): Promise<SectionContextResponse> {
|
export async function fetchSectionContext(projectId: string): Promise<SectionContextResponse> {
|
||||||
const cached = readState<SectionContextResponse>("section-context", projectId);
|
const cached = readState<SectionContextResponse>("section-context", projectId);
|
||||||
if (cached) return cached;
|
if (cached) return seedStandardCross(projectId, cached);
|
||||||
const fresh = await requestJson<SectionContextResponse>(
|
const fresh = await requestJson<SectionContextResponse>(
|
||||||
`/projects/${projectId}/sections/context`,
|
`/projects/${projectId}/sections/context`,
|
||||||
{ method: "GET" },
|
{ method: "GET" },
|
||||||
);
|
);
|
||||||
writeState("section-context", fresh, projectId);
|
writeState("section-context", fresh, projectId);
|
||||||
return fresh;
|
return seedStandardCross(projectId, fresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 저장된 표준 횡단면을 **세션이 비어 있을 때만** 채운다(2026-09-07).
|
||||||
|
*
|
||||||
|
* 브라우저 횡단 계산은 `세션 ?? config 기본값` 으로 서는데, 표준단면 세션값은 그 탭에서만
|
||||||
|
* 산다. 그래서 **탭을 새로 열면** 화면은 config 기본값으로, 서버는 저장분으로 계산해
|
||||||
|
* 같은 측점이 갈렸다(실측 — 용화 route 169 저장분은 암반 횡단경사 **5%**·측구 상단폭
|
||||||
|
* **0.9m**, config 기본값은 **3%**·**0.69m**).
|
||||||
|
*
|
||||||
|
* 여기가 두 화면(B05·B06)이 함께 지나는 유일한 자리라 이 한 곳에서 채운다. 사용자가
|
||||||
|
* 그 탭에서 고친 값이 있으면 **건드리지 않는다** — 초안이 언제나 우선이다.
|
||||||
|
*/
|
||||||
|
function seedStandardCross(
|
||||||
|
projectId: string,
|
||||||
|
context: SectionContextResponse,
|
||||||
|
): SectionContextResponse {
|
||||||
|
const stored = context.stored_standard_cross_section;
|
||||||
|
if (stored && readState<unknown>("std-cross", projectId) === null) {
|
||||||
|
writeState("std-cross", stored, projectId);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearSectionContextCache(projectId: string): void {
|
export function clearSectionContextCache(projectId: string): void {
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ export interface SectionContextResponse {
|
|||||||
defaults: SectionOptionDefaults;
|
defaults: SectionOptionDefaults;
|
||||||
/** 표준 횡단면 설정 패널(토사/암/포장) 기본값. */
|
/** 표준 횡단면 설정 패널(토사/암/포장) 기본값. */
|
||||||
standard_cross_section: StandardCrossSection;
|
standard_cross_section: StandardCrossSection;
|
||||||
|
/** 이 프로젝트에 **저장된** 표준 횡단면(사용자가 고쳐 확정한 값). 없으면 null.
|
||||||
|
* 위 `standard_cross_section` 은 config 기본값이라 둘은 다른 것이다(2026-09-07). */
|
||||||
|
stored_standard_cross_section?: StandardCrossSection | null;
|
||||||
/** 암 경계선 기본 오프셋(m)과 상/하 제어 스텝(m). */
|
/** 암 경계선 기본 오프셋(m)과 상/하 제어 스텝(m). */
|
||||||
rock_boundary_default_offset_m: number;
|
rock_boundary_default_offset_m: number;
|
||||||
rock_boundary_step_m: number;
|
rock_boundary_step_m: number;
|
||||||
|
|||||||
@@ -113,6 +113,15 @@ async def get_section_context(project_id: UUID) -> SectionContextResponse | JSON
|
|||||||
run_with_connection(_road_type),
|
run_with_connection(_road_type),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 저장된 표준 횡단면 — 브라우저 계산이 서버와 같은 값을 쓰게 함께 내려보낸다
|
||||||
|
# (2026-09-07). 노선이 없으면 저장분도 없다.
|
||||||
|
stored_standard: dict[str, Any] | None = None
|
||||||
|
if route_context and route_context.get("route_id") is not None:
|
||||||
|
longitudinal_row = await run_with_connection(
|
||||||
|
get_longitudinal_section, project_id, int(route_context["route_id"])
|
||||||
|
)
|
||||||
|
stored_standard = _stored_standard_cross_section(longitudinal_row)
|
||||||
|
|
||||||
defaults = SectionGenerationOptions()
|
defaults = SectionGenerationOptions()
|
||||||
return SectionContextResponse(
|
return SectionContextResponse(
|
||||||
project_id=str(project_id),
|
project_id=str(project_id),
|
||||||
@@ -130,6 +139,7 @@ async def get_section_context(project_id: UUID) -> SectionContextResponse | JSON
|
|||||||
vertical_exaggeration=SECTION_VERTICAL_EXAGGERATION,
|
vertical_exaggeration=SECTION_VERTICAL_EXAGGERATION,
|
||||||
),
|
),
|
||||||
standard_cross_section=STANDARD_CROSS_SECTION,
|
standard_cross_section=STANDARD_CROSS_SECTION,
|
||||||
|
stored_standard_cross_section=stored_standard,
|
||||||
rock_boundary_default_offset_m=STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M,
|
rock_boundary_default_offset_m=STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M,
|
||||||
rock_boundary_step_m=STANDARD_ROCK_BOUNDARY_STEP_M,
|
rock_boundary_step_m=STANDARD_ROCK_BOUNDARY_STEP_M,
|
||||||
earthwork_conversion=EARTHWORK_CONVERSION_FACTORS,
|
earthwork_conversion=EARTHWORK_CONVERSION_FACTORS,
|
||||||
|
|||||||
@@ -238,6 +238,11 @@ class SectionContextResponse(BaseModel):
|
|||||||
defaults: SectionOptionDefaults
|
defaults: SectionOptionDefaults
|
||||||
# 표준 횡단면 설정 패널(토사/암/포장) 기본값. config STANDARD_CROSS_SECTION 사본.
|
# 표준 횡단면 설정 패널(토사/암/포장) 기본값. config STANDARD_CROSS_SECTION 사본.
|
||||||
standard_cross_section: dict[str, Any] = Field(default_factory=dict)
|
standard_cross_section: dict[str, Any] = Field(default_factory=dict)
|
||||||
|
# 이 프로젝트에 **저장된** 표준 횡단면(사용자가 고쳐 [확정]한 값). 없으면 None(=기본값 그대로).
|
||||||
|
# 2026-09-07 추가 — 예전에는 브라우저가 이 값을 받을 길이 없어, 세션이 빈 새 탭에서
|
||||||
|
# **화면은 config 기본값으로, 서버는 저장분으로** 계산해 같은 측점이 갈렸다.
|
||||||
|
# 기존 `standard_cross_section`(기본값)은 그대로 두고 **한 칸만 더한다**.
|
||||||
|
stored_standard_cross_section: dict[str, Any] | None = None
|
||||||
# 암 경계선 기본 오프셋(m)과 상/하 제어 스텝(m).
|
# 암 경계선 기본 오프셋(m)과 상/하 제어 스텝(m).
|
||||||
rock_boundary_default_offset_m: float = -0.5
|
rock_boundary_default_offset_m: float = -0.5
|
||||||
rock_boundary_step_m: float = 0.1
|
rock_boundary_step_m: float = 0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user