feat(D · E): 떠남 = 공용 물음 [취소 · 폐기 · 저장] · 캐시 = 등록표 · 프로젝트 = 탭 currentProjectId (PLAN 572-3 · 요구 400~403)
- D02 · D03 · E02 말없이 저장 → mountGroupStep {dirty, save, discard} 공용 물음
- D04 · E05 · E06 가드 새로 · E03 = 대표 단면 별 저장 진행 중일 때만 물음
- D02 · E02~E07 세션 직접 읽기 · 쓰기 → 등록표 d02-points · e02-ground · e03-prob · e04-settings · e05-report · e06-rock · e07-settings
- 아홉 페이지 localStorage 지금 프로젝트 직접 읽기 → currentProjectId() (다른 탭이 바꿔도 이 탭 그대로)
- test_560_4 D · E 줄 넓힘(옛 beforeLeave 단언 → dirty · save) · test_401 PENDING 에서 D · E 뺌
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
/* =============================================================================
|
||||
* D02_PlanBase_UI_Page.ts
|
||||
* D02 1단계 기준 지정(요구서 345 3-2 · 389 3장 1 · PLAN 560-2) — 메인 = 카카오맵 · 좌 = 전체 시점 · 종점.
|
||||
* 조작(끌기 · 지도 누름 · 경위도 입력) = sessionStorage 캐시 · [저장] · [>] = PUT 정본(서버가 TM 셈)
|
||||
* · 떠남(단계 아이콘 · [<] [>] · 머리 메뉴 · 뒤로) = 바뀐 것 있으면 저장 뒤 · 실패면 머묾(560-4)
|
||||
* 조작(끌기 · 지도 누름 · 경위도 입력) = 캐시 · [저장] · [>] = PUT 정본(서버가 TM 셈)
|
||||
* · 떠남(단계 아이콘 · [<] [>] · 머리 메뉴 · 뒤로) = 바뀐 것 있으면 공용 물음 [취소][폐기][저장](572-3)
|
||||
* · 자동저장 없음(CLAUDE.md 5장) · 캐시는 그때 서버 값(base)이 지금과 같을 때만 씀(D03 이 고친 값 우선).
|
||||
* 산림청이 아니면(지정 회사 = 보기만) 끌기 · 누름 · 입력 · [저장] 없음 · 떠남 = 그냥.
|
||||
* 캐시 = 등록표 `d02-points`(프로젝트 칸 · 떠남 비우기에 걸림).
|
||||
* 첫 불러오기가 실패해도 지도 · 표는 세움(빈 화면 없음).
|
||||
* 지도 [일반][위성](560-3) = 카카오 HYBRID · 고른 값 = 화면 취향(`aislo:pref:`) 기억.
|
||||
* ========================================================================== */
|
||||
|
||||
import "./D02_PlanBase_UI_Style.css";
|
||||
import { API_BASE_URL, CURRENT_PROJECT_ID_KEY, ROUTES } from "@config/config_frontend";
|
||||
import { API_BASE_URL, ROUTES } from "@config/config_frontend";
|
||||
import {
|
||||
createButton,
|
||||
createInputField,
|
||||
@@ -24,7 +25,14 @@ import { createText } from "@ui/ui_template_text";
|
||||
import { editMap, type MapPin, type MapType } from "@ui/ui_template_kakao_map";
|
||||
import { createSegment } from "@ui/ui_template_segment";
|
||||
import { t } from "@ui/ui_template_locale";
|
||||
import { readByKey, writeByKey } from "../A00_Common/b_page_state";
|
||||
import {
|
||||
clearState,
|
||||
readByKey,
|
||||
readState,
|
||||
writeByKey,
|
||||
writeState,
|
||||
} from "../A00_Common/b_page_state";
|
||||
import { currentProjectId } from "../A00_Common/b_project_switch";
|
||||
import { mountGroupStep } from "../A00_Common/group_workflow";
|
||||
import { fetchSessionUser } from "../A06_Login/A06_Login_Api_Fetch";
|
||||
|
||||
@@ -46,8 +54,6 @@ const KOREA_CENTER: [number, number] = [127.8, 36.3];
|
||||
const DIGITS = 7; // 경위도 소수 7 자리 ≈ 1 cm
|
||||
const PREF_MAP_TYPE = "aislo:pref:d02.map.type";
|
||||
|
||||
const cacheKey = (projectId: string): string => `d02_points_${projectId}`;
|
||||
|
||||
async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const response = await fetch(`${API_BASE_URL}${path}`, {
|
||||
credentials: "include",
|
||||
@@ -68,7 +74,7 @@ function boundaryPoints(view: PointsView): Array<LonLat | null> {
|
||||
}
|
||||
|
||||
export async function renderD02PlanBase(root: HTMLElement): Promise<void> {
|
||||
const projectId = localStorage.getItem(CURRENT_PROJECT_ID_KEY) ?? "";
|
||||
const projectId = currentProjectId();
|
||||
const mapHost = el("div", { className: "d02-map" });
|
||||
const table = el("div", { className: "d02-points" });
|
||||
const info = createText("", "muted");
|
||||
@@ -87,7 +93,9 @@ export async function renderD02PlanBase(root: HTMLElement): Promise<void> {
|
||||
mountGroupStep(root, ROUTES.D02_PLAN_BASE, {
|
||||
mainContent: mapHost,
|
||||
leftPanel: left,
|
||||
beforeLeave: async () => !editable || !dirty() || store(),
|
||||
dirty: () => editable && dirty(),
|
||||
save: () => store(),
|
||||
discard: () => clearState("d02-points", projectId),
|
||||
});
|
||||
|
||||
let view: PointsView | null = null;
|
||||
@@ -103,7 +111,7 @@ export async function renderD02PlanBase(root: HTMLElement): Promise<void> {
|
||||
const serverPoints = (): Array<LonLat | null> => (view ? boundaryPoints(view) : [null, null]);
|
||||
const dirty = (): boolean => JSON.stringify(points) !== JSON.stringify(serverPoints());
|
||||
const remember = (): void =>
|
||||
sessionStorage.setItem(cacheKey(projectId), JSON.stringify({ base: serverPoints(), points }));
|
||||
writeState("d02-points", { base: serverPoints(), points }, projectId);
|
||||
|
||||
const map = editMap({
|
||||
hint: t("D02_Map_Hint"),
|
||||
@@ -232,7 +240,7 @@ export async function renderD02PlanBase(root: HTMLElement): Promise<void> {
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
);
|
||||
sessionStorage.removeItem(cacheKey(projectId));
|
||||
clearState("d02-points", projectId);
|
||||
active = firstEmpty();
|
||||
paint();
|
||||
showToast(t("D02_Saved"), "success");
|
||||
@@ -254,10 +262,10 @@ export async function renderD02PlanBase(root: HTMLElement): Promise<void> {
|
||||
if (mapHint) mapHint.textContent = t("D02_View_Only");
|
||||
}
|
||||
adopt(await request<PointsView>(`/projects/${projectId}/plan/points`));
|
||||
const cached = JSON.parse(sessionStorage.getItem(cacheKey(projectId)) ?? "null") as {
|
||||
const cached = readState<{
|
||||
base?: Array<LonLat | null>;
|
||||
points?: Array<LonLat | null>;
|
||||
} | null;
|
||||
}>("d02-points", projectId);
|
||||
const sameBase = JSON.stringify(cached?.base) === JSON.stringify(points);
|
||||
if (editable && sameBase && cached?.points?.length === 2) points = cached.points;
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user