feat(B03): 새 자료로 갈기 전에 「지우고 진행할까」 확인 한 단계 신설
업로드 하나가 그 프로젝트의 설계 산출물·초기값 스냅숏을 되돌릴 수 없게 지운다. 창 넷이 한 프로젝트를 볼 수 있어 말없이 지우면 남의 작업이 사라진다. - `describe_existing_outputs`: 지워질 것을 사람 말로 낸다(전처리·노선·횡단·도면· 수량·원가·초기값). 빈 폴더는 세지 않는다. - 지울 것이 있는데 확인이 없으면 409 `confirm_required` — 무엇이 지워지는지 함께 준다. - 세 갈래(직행·청크·보관함 연결) 전부에 `confirm_replace` 를 붙임. - ⚠ 기본값은 확인 켬(True) — 자동 절차(체인·스크립트)는 물음에 안 걸린다. 사람이 올리는 라우터만 False 로 불러 확인을 받는다. - 화면: 409 를 받으면 「되돌릴 수 없습니다」와 지워질 목록을 보이고 한 번 묻는다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
checkWF1AnalysisStatus,
|
||||
createUploadSession,
|
||||
finalizeUploadSession,
|
||||
ReplaceOutputsConfirmRequired,
|
||||
uploadFileChunk,
|
||||
type UploadedFileResult,
|
||||
} from "./B03_FileInput_Api_Fetch";
|
||||
@@ -77,6 +78,31 @@ export function confirmReplaceUpload(slotLabel: string, fileName: string): Promi
|
||||
* 파일 1건을 청크로 올린다. 중단된 세션이 있으면 그 지점부터 이어 올린다.
|
||||
* 진행 상황은 `onProgress`로만 알린다 — 갱신 주기는 config 값으로 제한한다.
|
||||
*/
|
||||
/**
|
||||
* 「이미 만들어 둔 결과가 지워집니다」를 사람에게 한 번 묻는다.
|
||||
* 되돌릴 수 없는 삭제라 확인 없이는 진행하지 않는다(2026-09-08 사용자 지시).
|
||||
* 자동 절차는 이 자리를 지나지 않는다 — 서버가 확인을 켜고 부른다.
|
||||
*/
|
||||
async function askReplaceOutputs<T>(run: (confirmReplace: boolean) => Promise<T>): Promise<T> {
|
||||
try {
|
||||
return await run(false);
|
||||
} catch (error) {
|
||||
if (!(error instanceof ReplaceOutputsConfirmRequired)) throw error;
|
||||
const detail = error.targets.length
|
||||
? `
|
||||
|
||||
지워지는 것: ${error.targets.join(" · ")}`
|
||||
: "";
|
||||
const agreed = window.confirm(
|
||||
`${error.message}${detail}
|
||||
|
||||
되돌릴 수 없습니다. 계속할까요?`,
|
||||
);
|
||||
if (!agreed) throw new Error("업로드를 취소했습니다 — 기존 결과는 그대로 있습니다.");
|
||||
return await run(true);
|
||||
}
|
||||
}
|
||||
|
||||
export async function uploadOneFile(
|
||||
projectId: string,
|
||||
state: FileSlotState,
|
||||
@@ -97,13 +123,16 @@ export async function uploadOneFile(
|
||||
const fingerprint = state.uploadSessionId ? null : await fileFingerprint(file);
|
||||
let session = state.uploadSessionId;
|
||||
if (!session) {
|
||||
const created = await createUploadSession(
|
||||
projectId,
|
||||
file,
|
||||
chunkSizeBytes,
|
||||
fingerprint,
|
||||
completeUpload,
|
||||
lasFree,
|
||||
const created = await askReplaceOutputs((confirmReplace) =>
|
||||
createUploadSession(
|
||||
projectId,
|
||||
file,
|
||||
chunkSizeBytes,
|
||||
fingerprint,
|
||||
completeUpload,
|
||||
lasFree,
|
||||
confirmReplace,
|
||||
),
|
||||
);
|
||||
if (created.already_uploaded) {
|
||||
state.progressBytes = file.size;
|
||||
@@ -155,13 +184,16 @@ export async function uploadOneFile(
|
||||
}
|
||||
}
|
||||
|
||||
const response = await finalizeUploadSession(
|
||||
projectId,
|
||||
session,
|
||||
totalChunks,
|
||||
completeUpload,
|
||||
fingerprint,
|
||||
lasFree,
|
||||
const response = await askReplaceOutputs((confirmReplace) =>
|
||||
finalizeUploadSession(
|
||||
projectId,
|
||||
session,
|
||||
totalChunks,
|
||||
completeUpload,
|
||||
fingerprint,
|
||||
lasFree,
|
||||
confirmReplace,
|
||||
),
|
||||
);
|
||||
localStorage.removeItem(storageKey);
|
||||
saveB03UploadedFile(projectId, {
|
||||
|
||||
Reference in New Issue
Block a user