Files
Aislo/B05_Profile/B05_Profile_UI_Page_Latest.ts
T
eomsangdonandClaude Opus 5.5 fb5ab57c74 refactor(b05): 좌측 패널 「경로 계산 설정」 상자 Old 로 옮김(54-24)
- 상자 화면(포인트 팔레트 · 알고리즘 · 조건 · 숨긴 [최적 경로 계산]) · solveRouteAction ·
  routePoint/circlePoint 를 B05_Profile/Old/B05_Profile_UI_RouteCalc.ts 로 떼어 옮김
- 좌측 패널 등록 · onSolve · 칸 값(algorithm · paved · 곡선반경 · 경사 상하한 · 회피 통과) 끊음
- tsconfig 에서 B05_Profile/Old 제외 · 서버 경로 계산 API · 자동설계 체인은 그대로
- 시험: 좌측 칸 수 1+4+2 · 칸 제목에서 「경로 계산 설정」 뺌

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4uKaV4ZBhsJN4GHG7yRj4
2026-10-06 12:35:13 +09:00

86 lines
3.8 KiB
TypeScript

/* =============================================================================
* B05_Profile_UI_Page_Latest.ts
* 종단 보기의 **최신 노선 · 설정값** 창구 — 세션 캐시 우선 읽기 · 뒤에서 신선도 확인 · 좌측 폼 되살리기
* 값. 종단 보기 본체(`design_view_profile`)가 700줄을 넘어 여기로 뗐다(계획서 24-3b).
* ========================================================================== */
import {
fetchLatestRoute,
readRouteLatestCache,
writeRouteLatestCache,
type RouteLatestResponse,
} from "./B05_Profile_Api_Fetch";
import type { RoutePanelValues } from "./B05_Profile_UI_Panel";
/**
* 확정 이력이 있으면 매 진입마다 DB(latest) 조회 대신 브라우저 세션 캐시를 우선 사용해
* 응답속도를 높인다. 캐시 미스면 latest를 조회해 적재하고, solve·확정 성공 시 신선한 값으로
* 갱신한다(세션 = 탭 단위, 탭 종료 시 소멸).
*/
export function createLatestSource(projectId: string) {
const read = (): RouteLatestResponse | null => readRouteLatestCache(projectId);
const write = (value: RouteLatestResponse): void => writeRouteLatestCache(projectId, value);
/** 캐시 우선 latest 로드. forceFresh=true(solve/확정 직후)는 항상 DB를 읽고 캐시를 갱신한다. */
async function load(forceFresh = false): Promise<RouteLatestResponse> {
if (!forceFresh) {
const cached = read();
if (cached) return cached;
}
const fresh = await fetchLatestRoute(projectId);
write(fresh);
return fresh;
}
/** 이 응답이 "같은 자료"인지 가리는 서명 — 노선 번호·지표면 모델·지표면 설정. */
const signature = (value: RouteLatestResponse): string =>
[
value.route?.id ?? "",
value.route?.surface_model_id ?? "",
value.surface_params.source_filter,
value.surface_params.method,
String(value.surface_params.smooth),
].join("|");
/**
* 캐시로 먼저 그린 뒤 **뒤에서** 신선도를 확인한다(2026-09-06 캐시·세션 일원화).
* 서명(노선번호·지표면)이 달라졌을 때만 `onChanged` 로 다시 그린다.
*/
async function verify(
shown: RouteLatestResponse,
onChanged: (fresh: RouteLatestResponse) => Promise<void>,
): Promise<void> {
try {
const fresh = await fetchLatestRoute(projectId);
write(fresh);
if (signature(fresh) === signature(shown)) return;
await onChanged(fresh);
} catch {
/* 확인 실패는 조용히 넘긴다 — 화면은 캐시로 이미 서 있다. */
}
}
return { read, write, load, verify };
}
/** 저장된 노선 설정값 → 좌측 폼 되살리기 값. */
export function panelValuesOf(next: RouteLatestResponse): Partial<RoutePanelValues> {
const options = next.route_params?.options ?? {};
return {
contourInterval: next.surface_params.contour_interval_m,
gradeClass: options.grade_class as RoutePanelValues["gradeClass"] | undefined,
stationInterval: next.route_params?.station_interval_m ?? undefined,
crossSampleInterval: next.route_params?.cross_sample_interval_m ?? undefined,
longSampleInterval: next.route_params?.long_sample_interval_m ?? undefined,
terrainType: options.terrain_type as RoutePanelValues["terrainType"] | undefined,
designSpeed: options.design_speed_kph as RoutePanelValues["designSpeed"] | undefined,
maxGradePct: next.route_params?.max_grade_pct ?? undefined,
minVerticalRadius: next.route_params?.min_vertical_radius_m ?? undefined,
minTangentLength: next.route_params?.min_tangent_length_m ?? undefined,
startElevationOffset: next.route_params?.start_elevation_offset_m ?? undefined,
endElevationOffset: next.route_params?.end_elevation_offset_m ?? undefined,
enforcePipeClearance:
(next.route_params?.enforce_pipe_clearance as boolean | undefined) ?? undefined,
};
}