refactor(B08,B09): 폴더째 old_code 로 옮기고 빈 화면 둘만 남김 (PLAN 7-3)
B08_Quantity 86 · B09_Estimation 111 파일을 old_code/ 로 옮김(지우지 않음). 화면은 메뉴·주소·단계 막대만 남은 빈 틀 둘 — main.py 라우터 13 개는 끊음. B07 이 빌려 쓰던 비탈 길이·면적은 필요한 함수만 B07_DesignDetail_Engine_SlopeGeometry 로 옮겨 적음(면적 적분·노면 면적·측점 묶음은 안 옮김) · 시험 하나를 새로 둠. B07 구조물도 조립(Cad_StandardSheet)은 2026-09-13 에 이미 도면 목록에서 빠져 부르는 곳이 없어 old_code 로 같이 보냄 — 구조물 그림은 되살리지 않음. B06 구조물 몫 조회는 빈 값으로 두어 화면이 그대로 서게 함. B08·B09 를 부르던 시험 25 개도 old_code/resources/tester 로 옮김. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
/* =============================================================================
|
||||
* B09_Estimation_UI_DetailEdit.ts
|
||||
* 호표 본표 **편집** — 일위대가 구성행 수정(`wM_Edit_iLWi`) · 단가산출 Q 식 편집(`wM_Edit_San`) (PLAN 12장 2차)
|
||||
*
|
||||
* - 전부 프로젝트 단위(브레인 판정) — 칸 하나 고칠 때마다 서버에 고친 값만 보내고, 금액은 서버가 다시 셈.
|
||||
* - 줄마다: 수량 칸 · ✕(줄 빼기) · D 의 기계 줄은 Q 식 칸 · 고친 줄 = 「사용자」 + ↺(계산값으로).
|
||||
* - 아래: 줄 더하기 — 단가표 고르개(이름으로 찾기) + 수량.
|
||||
* - ⚠ 화면이 값을 만들지 않음 — 입력한 글을 그대로 보내고, 받을 수 없는 값은 서버가 까닭과 함께 거절.
|
||||
* ========================================================================== */
|
||||
|
||||
import { showToast } from "@ui/ui_template_elements";
|
||||
import { API_BASE_URL } from "@config/config_frontend";
|
||||
import { L, el, userMark } from "./B09_Estimation_UI_Sheet";
|
||||
import { saveEdits, type EditChange } from "./B09_Estimation_UI_Store";
|
||||
|
||||
/** 서버가 줄마다 붙이는 고친 값 표시. */
|
||||
export interface RowEditDto {
|
||||
key: string;
|
||||
user: boolean;
|
||||
removed?: boolean;
|
||||
added?: boolean;
|
||||
was_quantity?: string;
|
||||
output?: string;
|
||||
q_formula?: string;
|
||||
}
|
||||
|
||||
interface SearchRowDto {
|
||||
code: string;
|
||||
name: string;
|
||||
spec: string;
|
||||
unit: string;
|
||||
kind_label: string;
|
||||
}
|
||||
|
||||
export const ROWS_SECTION = "sheet_rows";
|
||||
export const Q_SECTION = "price_basis_q";
|
||||
|
||||
function save(projectId: string, changes: EditChange[], reload: () => void): void {
|
||||
void saveEdits(projectId, changes)
|
||||
.then(() => {
|
||||
showToast(L("B09_Sheet_Saved"), "success");
|
||||
reload();
|
||||
})
|
||||
.catch((error: Error) => showToast(`${L("B09_Sheet_SaveFailed")} ${error.message}`, "error"));
|
||||
}
|
||||
|
||||
/** 수량 칸 — Enter·칸 나가기에 보냄(바뀐 때만). */
|
||||
export function quantityInput(
|
||||
projectId: string,
|
||||
edit: RowEditDto,
|
||||
quantity: string,
|
||||
reload: () => void,
|
||||
): HTMLElement {
|
||||
const input = el("input", "b09s-edit-input");
|
||||
input.type = "text";
|
||||
input.inputMode = "decimal";
|
||||
input.value = quantity;
|
||||
const send = (): void => {
|
||||
const text = input.value.trim();
|
||||
if (text === quantity) return;
|
||||
save(projectId, [{ section: ROWS_SECTION, key: edit.key, value: { quantity: text } }], reload);
|
||||
};
|
||||
input.addEventListener("click", (event) => event.stopPropagation());
|
||||
input.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter") input.blur();
|
||||
});
|
||||
input.addEventListener("change", send);
|
||||
return input;
|
||||
}
|
||||
|
||||
/** 줄 끝 조작 — ✕(빼기) · 고친 줄이면 「사용자」 + ↺. */
|
||||
export function rowControls(
|
||||
projectId: string,
|
||||
edit: RowEditDto,
|
||||
reload: () => void,
|
||||
editing: boolean,
|
||||
): HTMLElement {
|
||||
const box = el("span", "b09s-inline");
|
||||
if (editing && !edit.removed && !edit.added) {
|
||||
const remove = el("button", "b09s-undo", "✕");
|
||||
remove.type = "button";
|
||||
remove.title = L("B09_Sheet_RemoveRow");
|
||||
remove.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
save(projectId, [{ section: ROWS_SECTION, key: edit.key, value: { removed: true } }], reload);
|
||||
});
|
||||
box.append(remove);
|
||||
}
|
||||
if (edit.user) {
|
||||
box.append(
|
||||
userMark(() =>
|
||||
save(projectId, [{ section: ROWS_SECTION, key: edit.key, value: null }], reload),
|
||||
),
|
||||
);
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
/** D 기계 줄의 Q 식 칸 — 식을 글로 넣으면 서버가 셈 → 소수 2자리 확정 → 줄 수량 = 몫 ÷ Q. */
|
||||
export function qFormulaInput(
|
||||
projectId: string,
|
||||
edit: RowEditDto,
|
||||
reload: () => void,
|
||||
): HTMLElement {
|
||||
const box = el("div", "b09s-inline");
|
||||
const input = el("input", "b09s-edit-input b09s-edit-formula");
|
||||
input.type = "text";
|
||||
input.placeholder = `Q = ${edit.output ?? ""}`;
|
||||
input.value = edit.q_formula ?? "";
|
||||
input.addEventListener("click", (event) => event.stopPropagation());
|
||||
input.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter") input.blur();
|
||||
});
|
||||
input.addEventListener("change", () => {
|
||||
const text = input.value.trim();
|
||||
const value = text ? { q_formula: text } : null;
|
||||
save(projectId, [{ section: Q_SECTION, key: edit.key, value }], reload);
|
||||
});
|
||||
box.append(el("span", "b09s-head", "Q"), input);
|
||||
if (edit.q_formula) {
|
||||
box.append(
|
||||
userMark(() => save(projectId, [{ section: Q_SECTION, key: edit.key, value: null }], reload)),
|
||||
);
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
/** 줄 더하기 — 단가표에서 이름으로 찾아 고르고 수량을 넣음. */
|
||||
export function addRowForm(
|
||||
projectId: string,
|
||||
code: string,
|
||||
nextKey: string,
|
||||
reload: () => void,
|
||||
): HTMLElement {
|
||||
const box = el("div", "b09s-bar");
|
||||
const search = el("input", "b09s-edit-input b09s-edit-formula");
|
||||
search.type = "text";
|
||||
search.placeholder = L("B09_Sheet_SearchTitle");
|
||||
const pick = el("select");
|
||||
const amount = el("input", "b09s-edit-input");
|
||||
amount.type = "text";
|
||||
amount.inputMode = "decimal";
|
||||
amount.placeholder = L("B09_Sheet_Col_Quantity");
|
||||
const add = el("button", "b09s-undo", L("B09_Sheet_AddRow"));
|
||||
add.type = "button";
|
||||
let timer = 0;
|
||||
search.addEventListener("input", () => {
|
||||
window.clearTimeout(timer);
|
||||
timer = window.setTimeout(() => {
|
||||
const query = search.value.trim();
|
||||
if (!query) return;
|
||||
void fetch(
|
||||
`${API_BASE_URL}/projects/${encodeURIComponent(projectId)}/estimation/edits/search?q=${encodeURIComponent(query)}&parent=${encodeURIComponent(code)}`,
|
||||
{ credentials: "include" },
|
||||
)
|
||||
.then((response) => response.json() as Promise<{ rows: SearchRowDto[] }>)
|
||||
.then((data) => {
|
||||
pick.replaceChildren(
|
||||
...data.rows.map((row) => {
|
||||
const option = el(
|
||||
"option",
|
||||
"",
|
||||
`[${row.kind_label}] ${row.name} ${row.spec} (${row.unit})`.trim(),
|
||||
);
|
||||
option.value = row.code;
|
||||
return option;
|
||||
}),
|
||||
);
|
||||
})
|
||||
.catch(() => pick.replaceChildren());
|
||||
}, 250);
|
||||
});
|
||||
add.addEventListener("click", () => {
|
||||
if (!pick.value || !amount.value.trim()) return;
|
||||
save(
|
||||
projectId,
|
||||
[
|
||||
{
|
||||
section: ROWS_SECTION,
|
||||
key: nextKey,
|
||||
value: { ref: pick.value, quantity: amount.value.trim() },
|
||||
},
|
||||
],
|
||||
reload,
|
||||
);
|
||||
});
|
||||
box.append(el("span", "b09s-head", L("B09_Sheet_AddRow")), search, pick, amount, add);
|
||||
return box;
|
||||
}
|
||||
Reference in New Issue
Block a user