feat(497-1 B07 단추 줄): 좌측 아래 [?] [<] [>] [저장] [초기화] · [도각 편집] = 윗줄

- [저장] = 이동 없이 고친 탭(●) + 지금 탭 저장 · 고친 것 없으면 요청 0 · 저장 뒤 단계 색 다시 읽음
- [초기화] = danger-ghost · 맨 오른쪽 · 물음 → 오버레이 → POST design-drawings/reset → CAD 자동백업(도면마다) 지움 → 다시 읽음
- [>] = 지금 길 그대로(고친 것 없으면 저장 요청 없이 이동)
- 새 시험 test_298_2_b07_buttons · test_280_5_b07_settings 줄 자리 고쳐 씀

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 20:26:59 +09:00
co-authored by Claude Opus 5.5
parent 1c179ac5e3
commit 878376e295
6 changed files with 304 additions and 7 deletions
@@ -189,6 +189,20 @@ export function invalidateDesignDrawing(projectId: string, drawingId: string): P
);
}
/** [초기화](298-2) 답 — 풀린 장 수 · 지운 설정 파일 · 잠정으로 돌린 측점 수. */
export interface DesignDrawingResetResponse {
status: string;
project_id: string;
released: number;
cleared: string[];
provisional: number;
}
/** [초기화](298-2) — 저장본 확정 풂 · 설정 지움(서버 `_Router_Reset.py`) · 본문 없음. */
export function resetDesignDrawings(projectId: string): Promise<DesignDrawingResetResponse> {
return requestJson(`/projects/${projectId}/design-drawings/reset`, { method: "POST" });
}
/** B07 단계(4) 완료(281) — 페이지 이동 저장 뒤 B08 · B09 로 갈 때(확정 잠금 없음). */
export function completeDesignStage(projectId: string): Promise<void> {
return requestJson(`/projects/${projectId}/design-drawings-complete`, { method: "POST" });
+40 -6
View File
@@ -12,7 +12,12 @@
import "./B07_DesignDetail_UI_Style.css";
import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
import { hideLoadingOverlay, showLoadingOverlay, showToast } from "@ui/ui_template_elements";
import {
createButton,
hideLoadingOverlay,
showLoadingOverlay,
showToast,
} from "@ui/ui_template_elements";
import { createWorkflowLayout } from "@ui/ui_template_workflow_layout";
import { CURRENT_PROJECT_ID_KEY } from "@config/config_frontend";
// 좌측 패널(도면 목록·설계 정보)은 파일이 700줄을 넘어 떼어냈다(2026-09-04).
@@ -24,6 +29,7 @@ import {
} from "./B07_DesignDetail_UI_Panels";
import { workflowSteps } from "../A00_Common/b_page_scaffold";
import {
clearWorkflowStateCache,
fetchWorkflowState,
goToWorkflowStage,
WORKFLOW_STEP_ROUTES,
@@ -48,7 +54,13 @@ import {
} from "./B07_DesignDetail_Api_Fetch";
import { appendStructureEntities } from "./B07_DesignDetail_UI_Cad_Structures";
import { buildGuideToggle, guideShown } from "./B07_DesignDetail_UI_GuideToggle";
import { hasRedrawable, redrawFromSource, saveEditedDrawings } from "./B07_DesignDetail_UI_Save";
import {
hasRedrawable,
redrawFromSource,
resetDrawings,
saveDrawingsNow,
saveEditedDrawings,
} from "./B07_DesignDetail_UI_Save";
import { openDrawingSettings } from "./B07_DesignDetail_UI_Settings";
// CAD iframe 부모 흐름 · 도각 편집은 M02 와 한 벌로 공용에 둔다(PLAN 10-3).
import { createCadHost, type CadSaveResult } from "@ui/cad_host/cad_host";
@@ -451,13 +463,35 @@ export async function renderB07DesignDetail(root: HTMLElement): Promise<void> {
// 하단 고정은 공용 ui-sidebar-actions로 통일 — 사이드 본문이 [스크롤 영역][액션 줄]로
// 쪼개지고 액션 줄은 스크롤 밖에 남는다(2026-08-18 사용자 지시, B04~B07 공통).
actions.className = "b07-drawing-actions ui-sidebar-actions";
// 액션 칸은 두 줄이다 — 1행 도각 편집 띠(평소 숨김), 2행 [도각 편집]
// (2026-09-02 사용자 지시 — CAD 리본과 겹치던 떠 있는 띠를 여기로 옮김 · 확정 단추는 281 에 지움).
// 액션 칸은 세 줄이다 — 1행 도각 편집 띠(평소 숨김), 2행 [도각 편집]
// (2026-09-02 사용자 지시 — CAD 리본과 겹치던 떠 있는 띠를 여기로 옮김 · 확정 단추는 281 에 지움),
// 3행 [?] [<] [>] [저장] [초기화](298-2 · B05 · B06 과 같은 차례 · [?] [<] [>] 는 공용이 앞에 붙임).
const frameRow = document.createElement("div");
frameRow.className = "b07-drawing-actions__row";
frameRow.append(frameEditor.button);
const actionRow = document.createElement("div");
actionRow.className = "b07-drawing-actions__row";
actionRow.dataset.helpRow = "";
actionRow.append(frameEditor.button);
actions.append(frameEditor.banner, actionRow);
const refreshStages = (): void => {
if (!projectId) return;
clearWorkflowStateCache();
void fetchWorkflowState(projectId)
.then((state) => layout.setStages(state.stages))
.catch(() => undefined);
};
const saveButton = createButton({
label: L("Common_Btn_Save"),
variant: "ghost",
onClick: () => void saveDrawingsNow(store, refreshStages),
});
const resetButton = createButton({
label: L("Common_Btn_Reset"),
variant: "danger-ghost",
onClick: () => void resetDrawings(store),
});
saveButton.disabled = resetButton.disabled = !projectId;
actionRow.append(saveButton, resetButton);
actions.append(frameEditor.banner, frameRow, actionRow);
drawingPanel.append(infoPanelHost, actions);
@@ -6,6 +6,8 @@
* 고친 탭(●) + 지금 탭을 `confirm` 길로 · 안 고친 도면은 보내지 않음(늘 원본에서 새로 그림).
* 잠금 없음 — 저장본도 그대로 고침 · 다시 옮길 때 다시 저장.
* 원본 되돌림 = ⚙ 모달 [원본에서 다시 그리기] — 저장본이면 `invalidate` · 고친 것 버리고 다시 실음.
* 좌측 [저장](298-2) = 이동 없이 같은 저장 · [초기화] = 물음 → `reset`(저장본 풂 · 설정 지움) →
* CAD 자동백업 지움 → 페이지 다시 읽음(기준 = 지금 앞 단계로 그린 원본).
* ========================================================================== */
import {
@@ -19,6 +21,7 @@ import type { CadTabs } from "@ui/cad_host/cad_host_tabs";
import {
confirmDesignDrawing,
invalidateDesignDrawing,
resetDesignDrawings,
type CadDrawing,
type DesignDrawingItem,
type QuantityTable,
@@ -145,3 +148,48 @@ export async function redrawFromSource(store: DrawingStore): Promise<boolean> {
hideLoadingOverlay();
}
}
/** 좌측 [저장](298-2) — 이동 없이 고친 탭 저장 · 저장했으면 `onSaved`(단계 색 다시 읽음). */
export async function saveDrawingsNow(store: DrawingStore, onSaved: () => void): Promise<void> {
if (!store.cadDirty() && !store.tabs.anyDirty()) {
showToast("고친 도면이 없습니다.", "info");
return;
}
if (!(await saveEditedDrawings(store))) return;
showToast("저장했습니다.", "success");
onSaved();
}
/** CAD 자동백업 칸(`openwebcad/src/helpers/autosave.ts`) — 칸 이름 = 도면 id. */
const CAD_RECOVERY_KEY = "OPEN_WEB_CAD__RECOVERY";
/**
* 좌측 [초기화](298-2 · 300 C1 · C3) — 물은 뒤 서버가 저장본을 풀고 설정을 지움 → 고친 것 · CAD 자동백업 버림 →
* 페이지 다시 읽음(다음 GET 이 지금 앞 단계 저장본으로 원본을 그림). [취소] = 그대로.
*/
export async function resetDrawings(store: DrawingStore): Promise<void> {
if (store.isFrameEditing()) {
showToast("도각 편집을 마친 뒤 초기화하세요.", "warning");
return;
}
const ok = await showConfirmDialog(
"이 페이지에서 저장한 도면 · 고친 것 · 설정(축척 · 횡단 배치 · 평면도 종류 · 표식)을 모두 지우고 " +
"지금 앞 단계 값으로 다시 그릴까요?",
"초기화",
);
if (!ok) return;
showLoadingOverlay();
try {
await resetDesignDrawings(store.projectId);
store.drawings.forEach((item) => localStorage.removeItem(`${CAD_RECOVERY_KEY}__${item.id}`));
showToast("초기화했습니다.", "success");
location.reload();
} catch (error) {
showToast(
error instanceof Error ? error.message : "상세 설계를 초기화하지 못했습니다.",
"error",
);
} finally {
hideLoadingOverlay();
}
}