feat(B02): 프로젝트명을 고칠 수 있는 입력 칸으로 전환·비고 위치 이동

- 「저장될 이름」 미리보기 줄을 없애고 프로젝트명 입력 칸 하나로 통합 — 사업연도·사업지역·
  임도종류로 자동 채우되 사용자가 사이나 끝에 글을 넣거나 자동 부분을 고칠 수 있음
- 손댄 뒤 위 세 항목을 바꾸면 해당 조각만 갈아 끼워 사용자가 넣은 글은 보존
- 비고 칸을 노선 시작 누가거리 앞으로 이동

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-06 17:46:09 +09:00
co-authored by Claude Opus 5
parent 72075563e3
commit 861068cbf4
2 changed files with 44 additions and 26 deletions
+44 -19
View File
@@ -54,15 +54,14 @@ export function renderB02ProjRegister(root: HTMLElement): void {
header.append(title, subtitle);
// 입력 필드
// 프로젝트명은 사업연도 + 사업지역 + 임도종류 + 직접 입력값을 이어 붙여 만든
// (2026-09-06 사용자 확정). 아래 미리보기 줄이 저장될 이름 그대로다.
// 프로젝트명은 사업연도 + 사업지역 + 임도종류로 **자동으로 채워지되 고칠 수 있는 칸**이
// (2026-09-06 사용자 지시 — 종전에는 미리보기 줄이었다). 사용자가 사이나 끝에 글을 넣어도
// 위 세 항목을 다시 바꾸면 그 조각만 갈아 끼워 손댄 글이 살아남는다.
const nameField = createInputField({
label: "프로젝트명 (직접 입력 부분)",
placeholder: "예: 가리왕산지구",
label: "프로젝트명",
placeholder: "예: 2026 가리왕산 간선임도 1구간",
required: true,
});
const namePreview = document.createElement("p");
namePreview.className = "b02-proj__preview";
const regionField = createInputField({
label: L("B02_Proj_Field_Region"),
placeholder: L("B02_Proj_Field_Region_Placeholder"),
@@ -164,15 +163,40 @@ export function renderB02ProjRegister(root: HTMLElement): void {
const roadTypeText = (): string =>
roadTypeField.select.options[roadTypeField.select.selectedIndex]?.textContent ?? "";
const composedName = (): string =>
[
yearField.input.value.trim(),
regionField.input.value.trim(),
roadTypeText(),
nameField.input.value.trim(),
]
.filter((part) => part.length > 0)
.join(" ");
/** 이름을 이루는 자동 조각 — 사업연도·사업지역·임도종류 순서다. */
const nameParts = (): string[] => [
yearField.input.value.trim(),
regionField.input.value.trim(),
roadTypeText(),
];
let lastParts = nameParts();
let nameTouched = false;
nameField.input.addEventListener("input", () => {
nameTouched = true;
});
/**
* 위 세 항목이 바뀌면 이름 칸을 따라 고친다.
*
* 아직 손대지 않은 칸은 통째로 다시 쓰고, 사용자가 고친 뒤에는 **바뀐 조각만 갈아 끼운다**
* — 사이나 끝에 넣은 글이 지워지지 않게 하기 위해서다. 지웠던 조각은 되살리지 않는다.
*/
const syncName = (): void => {
const next = nameParts();
if (!nameTouched) {
nameField.input.value = next.filter((part) => part.length > 0).join(" ");
} else {
let text = nameField.input.value;
next.forEach((part, index) => {
const before = lastParts[index];
if (before && part && part !== before && text.includes(before)) {
text = text.replace(before, part);
}
});
nameField.input.value = text;
}
lastParts = next;
};
const composedName = (): string => nameField.input.value.trim();
const routeLength = (): number | null => {
const start = isBlank(routeStartField.input.value)
? null
@@ -185,14 +209,15 @@ export function renderB02ProjRegister(root: HTMLElement): void {
return end > from ? end - from : null;
};
const refresh = (): void => {
namePreview.textContent = `저장될 이름: ${composedName() || "(입력 대기)"}`;
syncName();
const length = routeLength();
routeEndField.root.querySelector(".ui-field__label")!.textContent =
length === null
? "노선 종료 누가거리 (m)"
: `노선 종료 누가거리 (m) — 연장 ${length.toFixed(1)}m`;
};
for (const field of [nameField, regionField, yearField, routeStartField, routeEndField]) {
// 이름 칸 자신은 여기서 제외한다 — 스스로 고치는 도중에 값을 되돌리면 안 된다.
for (const field of [regionField, yearField, routeStartField, routeEndField]) {
field.input.addEventListener("input", refresh);
}
roadTypeField.select.addEventListener("change", refresh);
@@ -318,10 +343,10 @@ export function renderB02ProjRegister(root: HTMLElement): void {
regionField.root,
roadTypeField.root,
nameField.root,
namePreview,
// 비고는 노선 구간 앞에 둔다 (2026-09-06 사용자 지시).
memoField.root,
routeStartField.root,
routeEndField.root,
memoField.root,
clientOrgField.root,
designDateField.root,
pmField.root,
@@ -64,13 +64,6 @@
}
}
/* 조합된 프로젝트명 미리보기 — 저장될 이름 그대로 보인다 (2026-09-06). */
.b02-proj__preview {
margin: calc(-1 * var(--spacing-8)) 0 0;
color: var(--color-text-secondary);
font-size: var(--text-caption);
}
/* 취소·등록 버튼 줄 */
.b02-proj__actions {
display: flex;