feat(497-2 B08 · B09 단추 줄): [초기화] = 확인 창 → 잠금 → 서버 reset → 다시 그림 · [>] = 저장 → 단계 완료 → 다음 길 · B09 [저장] 지움

- B08 줄 [?][<][>][저장][초기화] · POST quantity/reset · 초안 넷 비움 · 이정 저장본 다시 읽음
- B08 B09 쪽 이동 = 저장 뒤 POST quantity/complete · 실패면 머묾
- B09 줄 [?][<][>][초기화] · [내역 초기화] → [초기화] 하나(POST estimation/reset)
- B09 [>] = 고친 값 저장 → POST estimation/complete → DRAFT.next(인쇄 미리보기) · 초안 없으면 흐림
- 시험: 새 test_298_2_b08_b09_buttons · test_184_3 · test_304 · test_481_2 고쳐 씀

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 20:28:16 +09:00
co-authored by Claude Opus 5.5
parent 1c179ac5e3
commit c3e23e3be9
12 changed files with 514 additions and 40 deletions
+34 -17
View File
@@ -4,7 +4,9 @@
*
* 원본 = 기번3 공통 원가산출 엑셀 · 탭 하나 = 화면 표 하나(`…_UI_Tabs` 차례) · 옛 원가 코드 안 봄
* 자료 = GET …/estimation(입력 · 사용자 값 · 서버가 셈한 표) · 조작 = 캐시(`b09-estimation`) +
* 짝 계산(`…_Calc_*.ts`)으로 바로 다시 셈 · [저장] = PUT → 서버 정본 · [업데이트] = POST …/prices
* 짝 계산(`…_Calc_*.ts`)으로 바로 다시 셈 · 저장(PUT → 서버 정본) = 단계 이동 · [>] 때(481-2 · [저장] 단추 없음 · 요구 301)
* [>] = 고친 값 저장 → POST …/estimation/complete(단계 6 완료) → `DRAFT.next`(인쇄 미리보기 · 초안) · 실패면 머묾
* [초기화](요구 298-2) = 확인 창 → POST …/estimation/reset(사용자 값 · 단가 책 지움 · 견적서 남김) → 셈 값 · [업데이트] = POST …/prices
* 호표 칸 누름 = 그 탭 그 줄로 이동 · 자동 저장 없음
* ========================================================================== */
@@ -302,10 +304,11 @@ export function renderB09Estimation(root: HTMLElement): void {
}
};
const saveUser = (): Promise<boolean> =>
run("/estimation", "PUT", "원가 사용자 값을 저장했습니다.", { user });
const save = createButton({ label: "저장", variant: "filled", onClick: () => void saveUser() });
save.dataset.b09Save = "";
// 481-2 고친 값만 저장 · 불러오기 전 · 고침 없음 = 요청 없이 참
const saveChanged = (): Promise<boolean> =>
payload && projectId && readState("b09-estimation", projectId)
? run("/estimation", "PUT", "원가 사용자 값을 저장했습니다.", { user })
: Promise.resolve(true);
const refresh = createButton({
label: "업데이트",
variant: "ghost",
@@ -320,26 +323,24 @@ export function renderB09Estimation(root: HTMLElement): void {
});
refresh.dataset.b09Refresh = "";
const check = createCheckPanel(refresh, (link) => show(link.tab, link.key));
// 184-3 내역 초기화 — 설계내역서 덮개(user.bill) 지우고 바로 저장 → 기본 로직 내역
// 298-2 [초기화] — 내역 · 단가 · 공기 · 요율 등 사용자 값 · 설정 모두 지움 → B08 수량 셈 값(견적서 파일 남김)
const reset = createButton({
label: "내역 초기화",
variant: "ghost",
label: L("Common_Btn_Reset"),
variant: "danger-ghost",
onClick: () =>
void showConfirmDialog(
"설계내역서에서 고친 칸 · 더한 줄 · 지운 줄 · 차례를 모두 지우고 기본 로직 내역으로 되돌립니다. 바로 저장됩니다.",
"원가 계산에서 고친 값과 설정(설계내역서 · 단가 · 공기 · 요율 등)을 모두 지우고 지금 수량 산출 값으로 다시 셈합니다. 올린 견적서 파일은 남습니다.",
"초기화",
).then((ok) => {
if (!ok) return;
const { bill: _bill, ...rest } = user;
void run("/estimation", "PUT", "설계내역서를 초기값으로 되돌렸습니다.", { user: rest });
if (ok) void run("/estimation/reset", "POST", "원가 계산을 초기값으로 되돌렸습니다.");
}),
});
reset.dataset.b09BillReset = "";
reset.dataset.b09Reset = "";
// 카드 = 좌측 패널 공용 카드(269 · 「구조물 배치」 양식)
const section = createPanelCard("데이터 점검", true); // 311 기본 접힘
section.append(refresh, check.list);
// 303-1 · 304-1 하단 고정 줄 — [박스보기|호표보기] · [하이퍼링크|팝업] · 초안 단추 · [?][저장][내역 초기화]
const actions = createDock([save, reset], () => isGroupTab(current) && show(current));
// 303-1 · 304-1 하단 고정 줄 — [박스보기|호표보기] · [하이퍼링크|팝업] · 초안 단추 · [?][<][>][초기화]
const actions = createDock([reset], () => isGroupTab(current) && show(current));
const left = document.createElement("div");
left.className = "ui-sidebar-panel b09-options";
left.append(section, ...DRAFT.panels.map((make) => make(() => user, setUser)), actions);
@@ -355,12 +356,28 @@ export function renderB09Estimation(root: HTMLElement): void {
// 481-2 단계 이동 = 고친 값 저장 성공 뒤 · 실패면 머묾 · 불러오기 전 · 고침 없음 = 요청 없이
onStepClick: (stepIndex: number) => {
if (!projectId) return;
const changed = payload && readState("b09-estimation", projectId);
void (changed ? saveUser() : Promise.resolve(true)).then(
void saveChanged().then(
(ok) => ok && goToWorkflowStage(projectId, WORKFLOW_STEP_ROUTES[stepIndex]),
);
},
});
// [>] = 다음 단계 없음 → 공용 틀 대신 여기서 — 저장 → 단계 6 완료 → 초안 다음 길 · 초안 없으면 흐림 그대로
const next = left.querySelector<HTMLButtonElement>('[data-step-nav="1"]');
const goNext = DRAFT.next;
if (next && goNext) next.disabled = false;
left.addEventListener("ui-step-nav", (event) => {
if ((event as CustomEvent<number>).detail !== 1 || !goNext || !projectId) return;
event.stopPropagation();
void (async () => {
if (!(await saveChanged())) return;
try {
await request(`/projects/${projectId}/estimation/complete`, { method: "POST" });
} catch (error) {
return showToast(`단계를 마치지 못했습니다 — ${String(error)}`, "error");
}
goNext();
})();
});
root.append(layout.root);
show(current);