refactor(B03): 파일 입력 화면 마무리 — 파일 놓기 칸 · 모달 · 확인창 · 고르는 카드 · 목록 줄 부품 (PLAN 553-1)

- 놓기 칸 = 파일 놓기 칸 부품(한 줄로 눕힘) · 임시 보관함 창 = 모달 + 고르는 카드 + 빈 상태 · 교체 확인 = 확인창
- 업로드 도중 window.confirm 둘 → 확인창 부품(글 그대로 · 단추 이름으로 선택 안내) · 예정노선 고름 줄 = 목록 줄
- 모달 CSS · 임시 목록 옛 CSS 걷음 · 가이드 촬영 선택자 · 지킴 시험

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 22:13:40 +09:00
co-authored by Claude Sonnet 5.5
parent dbff07a4f8
commit 6bb0a5312d
7 changed files with 100 additions and 289 deletions
+17 -44
View File
@@ -8,7 +8,7 @@
import { PROGRESS_UPDATE_INTERVAL_MS, UPLOAD_CHUNK_SIZE_MB } from "@config/config_frontend";
import { fetchWorkflowState, type WorkflowState } from "../A00_Common/b_workflow_nav";
import { createButton } from "@ui/ui_template_elements";
import { askConfirm } from "@ui/ui_template_modal";
import { fileFingerprint } from "./B03_FileInput_Fingerprint";
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
import {
@@ -35,41 +35,12 @@ function L(key: keyof typeof ui_locales): string {
* 명시적 확인을 받는다(2026-08-04 사용자 지시). 확인 시에만 resolve(true).
*/
export function confirmReplaceUpload(slotLabel: string, fileName: string): Promise<boolean> {
return new Promise((resolve) => {
const backdrop = document.createElement("div");
backdrop.className = "b03-file__modal-backdrop";
const modal = document.createElement("div");
modal.className = "b03-file__modal";
modal.setAttribute("role", "alertdialog");
modal.setAttribute("aria-modal", "true");
const title = document.createElement("strong");
title.textContent = L("B03_File_Replace_Title");
const message = document.createElement("p");
message.textContent = `${slotLabel}: ${fileName}\n${L("B03_File_Replace_Message")}`;
const actions = document.createElement("div");
actions.className = "b03-file__modal-actions";
const done = (accepted: boolean): void => {
backdrop.remove();
resolve(accepted);
};
const cancel = createButton({
label: L("B03_File_Replace_Cancel"),
variant: "ghost",
onClick: () => done(false),
});
const accept = createButton({
label: L("B03_File_Replace_Confirm"),
variant: "filled",
onClick: () => done(true),
});
actions.append(cancel, accept);
modal.append(title, message, actions);
backdrop.append(modal);
backdrop.addEventListener("click", (event) => {
if (event.target === backdrop) done(false);
});
document.body.append(backdrop);
accept.focus();
return askConfirm({
title: L("B03_File_Replace_Title"),
message: `${slotLabel}: ${fileName}\n${L("B03_File_Replace_Message")}`,
okLabel: L("B03_File_Replace_Confirm"),
cancelLabel: L("B03_File_Replace_Cancel"),
danger: true,
});
}
@@ -95,21 +66,23 @@ async function askReplaceOutputs<T>(
지워지는 것: ${error.targets.join(" · ")}`
: "";
const agreed = window.confirm(
`${error.message}${detail}
const agreed = await askConfirm({
message: `${error.message}${detail}
되돌릴 수 없습니다. 계속할까요?`,
);
danger: true,
});
if (!agreed) throw new Error("업로드를 취소했습니다 — 기존 결과는 그대로 있습니다.");
// 지형 파일이 그대로면 전처리 결과는 살린다 — 「처음부터 다시」는 여기서 따로 고른다.
const freshStart =
error.preprocessKept &&
window.confirm(
`같은 지형 파일이라 전처리 결과(지표면)는 살려 다시 씁니다.
(await askConfirm({
message: `같은 지형 파일이라 전처리 결과(지표면)는 살려 다시 씁니다.
전처리까지 처음부터 다시 할까요?
[확인] 처음부터 다시 · [취소] 전처리 결과 살림`,
);
전처리까지 처음부터 다시 할까요?`,
okLabel: "처음부터 다시",
cancelLabel: "전처리 결과 살림",
}));
return await run(true, freshStart);
}
}