From 5907de85c618a45a5805499a2aeede9c02e3d83e Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 9 Sep 2026 20:32:50 +0900 Subject: [PATCH] =?UTF-8?q?feat(B06):=20=EC=B8=A1=EA=B5=AC=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=EA=B3=BC=20=EA=B2=B0=EA=B3=BC=EB=A5=BC=20=EA=B0=80?= =?UTF-8?q?=EB=A5=B8=20=EC=B9=B8=20`ditch=5Fchoice`=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용자가 누른 선택(`ditch_choice`)과 자동 판정 결과(`ditch_enabled`)를 다른 칸으로 나눔. - 캐시(`CrossDesignChoice`)에 칸이 없어 토글이 화면에서 안 돌던 자리 이음 — 선택이 있으면 그것을 따르고, 없으면 저장분 결과를 자동값과 다를 때만 선택으로 살림. - 서버 patch 스키마에도 같은 칸을 둠. 최상위 `None` 은 병합에서 걷히므로 「자동」으로 되돌리는 길은 아직 없음(카드 토글은 켬/끔 둘뿐이라 지금은 손해 없음). 화면 실측(카드에서 껐다 켜기)은 PLAN.md 3-16 에 미체크로 남아 있음. Co-Authored-By: Claude Opus 5 (1M context) --- B06_Section/B06_Section_Api_Types.ts | 2 ++ B06_Section/B06_Section_Cross_Design_Session.ts | 3 +++ B06_Section/B06_Section_Cross_Refresh.ts | 6 +++++- B06_Section/B06_Section_Schema.py | 3 +++ B06_Section/B06_Section_UI_Page.ts | 2 ++ B06_Section/B06_Section_UI_Page_Persist.ts | 3 +++ 6 files changed, 18 insertions(+), 1 deletion(-) diff --git a/B06_Section/B06_Section_Api_Types.ts b/B06_Section/B06_Section_Api_Types.ts index 3629355d..c5d1a4a0 100644 --- a/B06_Section/B06_Section_Api_Types.ts +++ b/B06_Section/B06_Section_Api_Types.ts @@ -560,6 +560,8 @@ export interface CrossSectionPatch { ditch_type?: string | null; paved?: boolean; two_stage_slope?: boolean; + /** 측구를 둘지 **사용자 선택**(결과 `ditch_enabled` 와 다른 칸, 2026-09-09). */ + ditch_choice?: boolean | null; /** 절·성토 면적(㎡) — 브라우저가 계산해 보낸다(2026-09-06 사용자 확정). */ cut_area_m2?: number; fill_area_m2?: number; diff --git a/B06_Section/B06_Section_Cross_Design_Session.ts b/B06_Section/B06_Section_Cross_Design_Session.ts index 7b6ec3f9..5e5f74df 100644 --- a/B06_Section/B06_Section_Cross_Design_Session.ts +++ b/B06_Section/B06_Section_Cross_Design_Session.ts @@ -20,6 +20,9 @@ export interface CrossDesignChoice { ditch_type?: string | null; paved?: boolean; two_stage_slope?: boolean; + /** 측구를 둘지 **사용자가 정한 선택**(`null` = 자동 판정). 결과(`ditch_enabled`)가 아니다. + * ⚠ 이 칸이 없으면 측구 토글이 눌려도 캐시에 안 담겨 **화면에서 안 돈다**(2026-09-09). */ + ditch_choice?: boolean | null; } type ChoiceMap = Record; diff --git a/B06_Section/B06_Section_Cross_Refresh.ts b/B06_Section/B06_Section_Cross_Refresh.ts index f2d44caa..fa240d14 100644 --- a/B06_Section/B06_Section_Cross_Refresh.ts +++ b/B06_Section/B06_Section_Cross_Refresh.ts @@ -289,7 +289,11 @@ function refreshLocally(input: CrossRefreshInput): number[] | null { (design.two_stage_slope === undefined ? true : Boolean(design.two_stage_slope)), // ⚠ **선택과 결과를 갈라 넘긴다**(2026-09-09). 선택이 있으면 그것을 따르고, // 없으면 옛 저장분의 결과를 **자동값과 다를 때만** 선택으로 살린다. - ditchChoice: typeof design.ditch_choice === "boolean" ? design.ditch_choice : null, + ditchChoice: choice + ? (choice.ditch_choice ?? null) + : typeof design.ditch_choice === "boolean" + ? design.ditch_choice + : null, ditchEnabled: typeof design.ditch_enabled === "boolean" ? design.ditch_enabled : null, // 세월교 월류 하강은 계획선 편집으로 바뀌지 않는다 — 저장분 값을 그대로 잇는다. surfaceDropM: typeof design.surface_drop_m === "number" ? design.surface_drop_m : 0, diff --git a/B06_Section/B06_Section_Schema.py b/B06_Section/B06_Section_Schema.py index b6ef1a76..b1171532 100644 --- a/B06_Section/B06_Section_Schema.py +++ b/B06_Section/B06_Section_Schema.py @@ -170,6 +170,9 @@ class CrossSectionPatch(BaseModel): ditch_type: str | None = None paved: bool | None = None two_stage_slope: bool | None = None + # 측구를 둘지 **사용자 선택**(결과 `ditch_enabled` 와 다른 칸, 2026-09-09). None 은 + # 「안 보냄」이라 병합에서 걷힌다 — 「자동」을 patch 로 되돌릴 길은 아직 없다. + ditch_choice: bool | None = None # 구조물이 선 측점의 폐회로 절·성토 면적(㎡) — 브라우저가 계산해 보낸다 # (2026-09-06 사용자 확정: 「일단은 브라우저 계산으로」). 서버는 초기값을 만들 때만 # 같은 코드를 Node 로 돌린다(`B06_Section_Server_Calc_Node.ts`). diff --git a/B06_Section/B06_Section_UI_Page.ts b/B06_Section/B06_Section_UI_Page.ts index 837ec2ce..567e9119 100644 --- a/B06_Section/B06_Section_UI_Page.ts +++ b/B06_Section/B06_Section_UI_Page.ts @@ -240,6 +240,7 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise { ditch_type: change.ditch_type, paved: change.paved, two_stage_slope: change.two_stage_slope, + ditch_choice: change.ditch_choice, }; sectionView.refreshCard(chainageM); } @@ -254,6 +255,7 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise { ditch_type: change.ditch_type, paved: change.paved, two_stage_slope: change.two_stage_slope, + ditch_choice: change.ditch_choice, }); // (3) 계산은 브라우저 안에서 — B05·B06 이 같이 쓰는 창구 하나로 돌린다. await reconcileStaleDesigns({ force: true }); diff --git a/B06_Section/B06_Section_UI_Page_Persist.ts b/B06_Section/B06_Section_UI_Page_Persist.ts index e45c50c3..e69a3d1d 100644 --- a/B06_Section/B06_Section_UI_Page_Persist.ts +++ b/B06_Section/B06_Section_UI_Page_Persist.ts @@ -382,6 +382,9 @@ export function collectSectionEdits(ctx: SectionPersistContext): { if (choice.ditch_type !== undefined) patch.ditch_type = choice.ditch_type; if (choice.paved !== undefined) patch.paved = choice.paved; if (choice.two_stage_slope !== undefined) patch.two_stage_slope = choice.two_stage_slope; + // ⚠ 「자동」(null)은 patch 로 못 보낸다 — 서버 병합이 최상위 null 을 걷는다. 카드 토글은 + // 자동으로 되돌아가지 않으므로(켬/끔 둘 뿐) 지금은 손해가 없다(2026-09-09). + if (typeof choice.ditch_choice === "boolean") patch.ditch_choice = choice.ditch_choice; }); if (detail) { // 구조물 폐회로 면적 — **카드를 그리지 않은 측점까지** 여기서 다 계산한다