fix(b08): 거리이정표 km 칸 끝수 정리 — 3자리 반올림 · 끝 0 지움 · 안 고치면 저장 m 그대로 (261-1)
- 시점 창 · 구간 창 · 다이어그램 선 위 km 칸 · 자동 값 글 = 같은 도우미(fmtKm) - 안 고친 칸은 반올림 값으로 덮지 않음 - 시험 test_261_1 추가 · test_255_1 기대값만 고침 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jonfdxn1Xit9bdkNJpE4G
This commit is contained in:
@@ -39,7 +39,7 @@ import {
|
||||
type DiagramBox,
|
||||
} from "./B08_Quantity_Milestone_Layout";
|
||||
import { openLegInfo } from "./B08_Quantity_UI_Milestone_LegModal";
|
||||
import { openPlaceModal, openSiteInfo, toastKm } from "./B08_Quantity_UI_Milestone_Modal";
|
||||
import { fmtKm, openPlaceModal, openSiteInfo, toastKm } from "./B08_Quantity_UI_Milestone_Modal";
|
||||
import {
|
||||
findSegmentKm,
|
||||
liveMilestone,
|
||||
@@ -87,11 +87,12 @@ function editLegAt(
|
||||
className: "ui-input",
|
||||
attrs: { type: "number", min: "0", step: "any" },
|
||||
});
|
||||
km.value = leg.km == null ? "" : String(leg.km);
|
||||
km.value = leg.km == null ? "" : fmtKm(leg.km);
|
||||
const shown = km.value; // 안 고치면 저장 값 그대로 · 반올림 값으로 안 덮음
|
||||
km.setAttribute("aria-label", "km");
|
||||
if (leg.auto_km != null) {
|
||||
km.placeholder = String(leg.auto_km); // 빈칸 = 자동 값
|
||||
km.title = `자동 값 ${leg.auto_km} km`;
|
||||
km.placeholder = fmtKm(leg.auto_km); // 빈칸 = 자동 값
|
||||
km.title = `자동 값 ${fmtKm(leg.auto_km)} km`;
|
||||
}
|
||||
const box = el("div", { className: "b08-milestone__leg-edit", children: [road.root, km] });
|
||||
box.dataset.b08MilestoneLegEdit = `${from}:${i}`;
|
||||
@@ -108,7 +109,7 @@ function editLegAt(
|
||||
const value = km.value.trim() && Number.isFinite(n) && n >= 0 ? n : null;
|
||||
const patch = {
|
||||
road: (road.select.value || null) as Road | null,
|
||||
...(km.value.trim() && value === null ? {} : { km: value }),
|
||||
...(km.value === shown || (km.value.trim() && value === null) ? {} : { km: value }),
|
||||
};
|
||||
putMilestone(projectId, setLeg(liveMilestone(projectId), from, i, patch));
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from "./B08_Quantity_Milestone_Calc";
|
||||
import { ownerOf, segmentEnds, setLeg } from "./B08_Quantity_Milestone_Layout";
|
||||
import { editMap, type MapPin } from "./B08_Quantity_UI_Milestone_KakaoMap";
|
||||
import { toastKm } from "./B08_Quantity_UI_Milestone_Modal";
|
||||
import { fmtKm, toastKm } from "./B08_Quantity_UI_Milestone_Modal";
|
||||
import { reverseAddress } from "./B08_Quantity_UI_Milestone_Place";
|
||||
import {
|
||||
findSegmentKm,
|
||||
@@ -47,7 +47,13 @@ export function legsEditor(
|
||||
kind: SegKind,
|
||||
): { root: HTMLElement; value: () => Leg[] } {
|
||||
const list = el("div");
|
||||
const rows: Array<{ road: HTMLSelectElement; km: HTMLInputElement; auto: number | null }> = [];
|
||||
const rows: Array<{
|
||||
road: HTMLSelectElement;
|
||||
km: HTMLInputElement;
|
||||
auto: number | null;
|
||||
orig: number | null; // 안 고친 칸 = 원래 값 그대로(반올림 값으로 안 덮음)
|
||||
shown: string;
|
||||
}> = [];
|
||||
const add = (leg: Leg): void => {
|
||||
const road = createSelectField({
|
||||
label: "도로 구분",
|
||||
@@ -56,13 +62,13 @@ export function legsEditor(
|
||||
});
|
||||
const dist = createInputField({ label: "거리 (km)", type: "number", min: 0, compact: true });
|
||||
road.select.value = roadOf(leg.road, kind);
|
||||
dist.input.value = leg.km == null ? "" : String(leg.km);
|
||||
dist.input.value = leg.km == null ? "" : fmtKm(leg.km);
|
||||
const auto = leg.auto_km ?? null;
|
||||
if (auto != null) {
|
||||
dist.input.placeholder = String(auto); // 빈칸 = 자동 값 · 고치면 그 값 우선
|
||||
dist.input.title = `자동 값 ${auto} km`;
|
||||
dist.input.placeholder = fmtKm(auto); // 빈칸 = 자동 값 · 고치면 그 값 우선
|
||||
dist.input.title = `자동 값 ${fmtKm(auto)} km`;
|
||||
}
|
||||
const ref = { road: road.select, km: dist.input, auto };
|
||||
const ref = { road: road.select, km: dist.input, auto, orig: leg.km, shown: dist.input.value };
|
||||
rows.push(ref);
|
||||
const drop = createButton({
|
||||
label: "×",
|
||||
@@ -90,9 +96,9 @@ export function legsEditor(
|
||||
return {
|
||||
root: el("div", { children: [list, more] }),
|
||||
value: () =>
|
||||
rows.map(({ road, km: dist, auto }) => ({
|
||||
rows.map(({ road, km: dist, auto, orig, shown }) => ({
|
||||
road: (ROADS as readonly string[]).includes(road.value) ? (road.value as Road) : null,
|
||||
km: km(dist.value),
|
||||
km: dist.value === shown ? orig : km(dist.value),
|
||||
auto_km: auto,
|
||||
})),
|
||||
};
|
||||
@@ -117,8 +123,9 @@ function openMidLeg(projectId: string): void {
|
||||
road.select.value = roadOf(data.site.mid_road, "mid");
|
||||
const dist = createInputField({ label: "거리 (km)", type: "number", min: 0, compact: true });
|
||||
dist.input.value =
|
||||
data.site.mid_station_m == null ? "" : String(data.site.mid_station_m / 1000);
|
||||
dist.input.placeholder = site?.mid_default_m == null ? "" : String(site.mid_default_m / 1000); // 빈칸 = 노선 ÷ 2
|
||||
data.site.mid_station_m == null ? "" : fmtKm(data.site.mid_station_m / 1000);
|
||||
dist.input.placeholder = site?.mid_default_m == null ? "" : fmtKm(site.mid_default_m / 1000); // 빈칸 = 노선 ÷ 2
|
||||
const shown = dist.input.value; // 안 고치면 저장 값(m) 그대로
|
||||
dist.input.dataset.b08MilestoneLegKm = "start";
|
||||
const save = createButton({
|
||||
label: "수정",
|
||||
@@ -129,7 +136,7 @@ function openMidLeg(projectId: string): void {
|
||||
projectId,
|
||||
setLeg(fresh, "start", 0, {
|
||||
road: road.select.value as Road,
|
||||
km: km(dist.input.value),
|
||||
...(dist.input.value === shown ? {} : { km: km(dist.input.value) }),
|
||||
}),
|
||||
);
|
||||
close();
|
||||
|
||||
@@ -64,6 +64,9 @@ export function toastKm(text: string): void {
|
||||
if (text) showToast(`길찾기 — ${text}`, text.includes(" — ") ? "warning" : "success");
|
||||
}
|
||||
|
||||
/** 칸 · 글에 보이는 km — 3자리(1 m) 반올림 · 끝 0 지움(셈 값은 안 바꿈 · 261-1) */
|
||||
export const fmtKm = (km: number): string => String(Math.round(km * 1000) / 1000);
|
||||
|
||||
let listSeq = 0;
|
||||
|
||||
/** 항목(손 입력 + 제안 목록 · 244-14) · 목적지 ▾ 한 줄 — 목록 밖 글도 받음 · 분류 바꾸면 제안만 바뀜 */
|
||||
@@ -270,9 +273,10 @@ export function openSiteInfo(projectId: string): void {
|
||||
compact: true,
|
||||
});
|
||||
mid.input.step = "0.001";
|
||||
mid.input.placeholder = site?.mid_default_m != null ? String(site.mid_default_m / 1000) : "";
|
||||
mid.input.placeholder = site?.mid_default_m != null ? fmtKm(site.mid_default_m / 1000) : "";
|
||||
mid.input.value =
|
||||
data.site.mid_station_m == null ? "" : String(data.site.mid_station_m / 1000);
|
||||
data.site.mid_station_m == null ? "" : fmtKm(data.site.mid_station_m / 1000);
|
||||
const shown = mid.input.value; // 안 고치면 저장 값(m) 그대로 · 반올림 값으로 안 덮음
|
||||
mid.input.dataset.b08Milestone = "mid_station_m";
|
||||
const road = createSelectField({
|
||||
label: "시점 → 중점 도로",
|
||||
@@ -290,7 +294,10 @@ export function openSiteInfo(projectId: string): void {
|
||||
const fresh = liveMilestone(projectId);
|
||||
putMilestone(
|
||||
projectId,
|
||||
setLeg(fresh, "start", 0, { road: road.select.value as Road, km }),
|
||||
setLeg(fresh, "start", 0, {
|
||||
road: road.select.value as Road,
|
||||
...(text === shown ? {} : { km }),
|
||||
}),
|
||||
);
|
||||
close();
|
||||
},
|
||||
|
||||
@@ -41,7 +41,7 @@ def test_시점_모달_km_카카오_지도():
|
||||
modal = _read("B08_Quantity/B08_Quantity_UI_Milestone_Modal.ts")
|
||||
site = modal[modal.index("export function openSiteInfo") :]
|
||||
assert 'label: "중점 측점 (km)"' in site
|
||||
assert "String(data.site.mid_station_m / 1000)" in site # 보임 = km
|
||||
assert "fmtKm(data.site.mid_station_m / 1000)" in site # 보임 = km(261-1 끝수 정리)
|
||||
assert 'setLeg(fresh, "start", 0, {' in site # 받음 km → 저장 m
|
||||
# 설명 줄 · 등록 주소 · 브이월드 주소 줄 · B01 타일 지도 없음
|
||||
for gone in ("시점 = 노선 첫 점", "프로젝트 등록 주소", "브이월드 주소", "buildAddressMap"):
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
"""261-1 거리이정표 km 칸 끝수 — 보이는 km = 3자리(1 m) 반올림 · 끝 0 지움 · 안 고치면 저장 m 그대로 ·
|
||||
고치면 km × 1000 → m(끝수 없이)."""
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE)
|
||||
UI = "B08_Quantity/B08_Quantity_UI_Milestone"
|
||||
|
||||
|
||||
def _read(rel):
|
||||
return (ROOT / rel).read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def _node(src, expr):
|
||||
result = subprocess.run(
|
||||
["node", "--experimental-strip-types", "--no-warnings",
|
||||
"--input-type=module-typescript", "-e",
|
||||
IMPORTS.sub("", src) + f"\nconsole.log(JSON.stringify({expr}));"],
|
||||
capture_output=True, text=True, encoding="utf-8", timeout=60,
|
||||
) # fmt: skip
|
||||
assert result.returncode == 0, result.stderr
|
||||
return json.loads(result.stdout)
|
||||
|
||||
|
||||
def _fmt(values):
|
||||
modal = _read(f"{UI}_Modal.ts")
|
||||
line = next(ln for ln in modal.splitlines() if ln.startswith("export const fmtKm"))
|
||||
return _node(line.replace("export ", ""), f"{json.dumps(values)}.map(fmtKm)")
|
||||
|
||||
|
||||
def test_km_글_3자리_끝0_지움():
|
||||
assert _fmt([576.595 / 1000, 0.3, 0.1 + 0.2, 12, 0, 1.2345, 0.0005, 2 / 3]) == [
|
||||
"0.577", "0.3", "0.3", "12", "0", "1.235", "0.001", "0.667",
|
||||
] # fmt: skip
|
||||
|
||||
|
||||
def test_고치면_km_곱하기_1000_끝수_없이():
|
||||
layout = _read("B08_Quantity/B08_Quantity_Milestone_Layout.ts")
|
||||
data = {"site": {"mid_station_m": 576.595, "mid_road": "비포장"}, "base": {}, "routes": []}
|
||||
expr = f'setLeg({json.dumps(data)}, "start", 0, {{ km: 0.3 }}).site.mid_station_m'
|
||||
assert _node(layout, expr) == 300
|
||||
# 안 고침 = km 키 없는 patch → 저장 m 그대로
|
||||
expr = f'setLeg({json.dumps(data)}, "start", 0, {{ road: "비포장" }}).site.mid_station_m'
|
||||
assert _node(layout, expr) == 576.595
|
||||
|
||||
|
||||
def test_모든_km_칸_같은_규칙():
|
||||
modal = _read(f"{UI}_Modal.ts")
|
||||
site = modal[modal.index("export function openSiteInfo") :]
|
||||
assert "fmtKm(data.site.mid_station_m / 1000)" in site
|
||||
assert "fmtKm(site.mid_default_m / 1000)" in site
|
||||
assert "...(text === shown ? {} : { km })" in site # 안 고침 = 덮지 않음
|
||||
|
||||
leg = _read(f"{UI}_LegModal.ts")
|
||||
assert "fmtKm(data.site.mid_station_m / 1000)" in leg
|
||||
assert "fmtKm(site.mid_default_m / 1000)" in leg
|
||||
assert "...(dist.input.value === shown ? {} : { km: km(dist.input.value) })" in leg
|
||||
assert "dist.value === shown ? orig : km(dist.value)" in leg # 구간 줄
|
||||
assert "fmtKm(leg.km)" in leg and "fmtKm(auto)" in leg
|
||||
|
||||
diagram = _read(f"{UI}_Diagram.ts")
|
||||
assert "fmtKm(leg.km)" in diagram and "fmtKm(leg.auto_km)" in diagram
|
||||
assert "km.value === shown" in diagram # 선 위 km 고치기 칸 · 안 고침 = 덮지 않음
|
||||
for src in (modal, leg, diagram):
|
||||
assert "String(leg.km)" not in src and "mid_station_m / 1000)" not in src.replace(
|
||||
"fmtKm(data.site.mid_station_m / 1000)", ""
|
||||
) # 맨 String 변환 없음
|
||||
Reference in New Issue
Block a user