feat(M01): 로직 개선 시험 상세 화면을 컨테이너 넷으로 · 호표를 인력·자재·경비 묶음과 소계로 · 줄을 누르면 쓰는 자료(표·단가) 모달
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
@@ -19,23 +19,20 @@ import {
|
||||
import {
|
||||
ApiError,
|
||||
fetchLogic,
|
||||
fetchLogicFiles,
|
||||
fetchLogics,
|
||||
fetchLogicSubs,
|
||||
saveFiles,
|
||||
type CalcLine,
|
||||
type ElementBrief,
|
||||
type LogicFile,
|
||||
type LogicRow,
|
||||
type LogicSummary,
|
||||
type SaveFile,
|
||||
} from "./M01_MasterData_UI_Logic_Api";
|
||||
import { buildCalc } from "./M01_MasterData_UI_Logic_Calc";
|
||||
import { buildEditor } from "./M01_MasterData_UI_Logic_Edit";
|
||||
import { buildList, logicId, type ListItem, type ListMark } from "./M01_MasterData_UI_Logic_List";
|
||||
import { openPicker } from "./M01_MasterData_UI_Logic_Pick";
|
||||
import type { SideHandle } from "./M01_MasterData_UI_Side";
|
||||
import { tx } from "./M01_MasterData_UI_Logic_Text";
|
||||
import { buildLabDetail } from "./M01_MasterData_UI_LogicLab_Detail";
|
||||
import "./M01_MasterData_UI_Logic_Style.css";
|
||||
|
||||
/** 저장 안 한 로직 하나 — origKey null = 새 로직 · row null = 지움 */
|
||||
@@ -80,12 +77,11 @@ export async function mountM01LogicLab(
|
||||
openKey?: string,
|
||||
): Promise<LogicHandle> {
|
||||
let drafts = loadDrafts();
|
||||
let files: LogicFile[] = [];
|
||||
let opened: Opened | null = null;
|
||||
let calcInputs = "";
|
||||
const editor = el("div", { className: "m01-logic__editor" });
|
||||
const errors = el("div", { className: "m01-logic__reasons", attrs: { hidden: "" } });
|
||||
const calc = el("aside", { className: "m01-logic__calc" });
|
||||
const calc = el("div", { className: "m01lab__calc" });
|
||||
const filterHost = el("div");
|
||||
const waiting = el("p", { className: "m01-logic__muted", text: tx("Loading") });
|
||||
let loaded = false; // 첫 목록이 오기 전에는 「0 줄」 빈 표를 보이지 않음
|
||||
@@ -168,7 +164,6 @@ export async function mountM01LogicLab(
|
||||
list.root.hidden = !listing || !loaded;
|
||||
waiting.hidden = loaded;
|
||||
editor.hidden = back.hidden = listing;
|
||||
calc.hidden = listing;
|
||||
if (!opened) return;
|
||||
if (!opened.row) {
|
||||
editor.replaceChildren(
|
||||
@@ -177,29 +172,18 @@ export async function mountM01LogicLab(
|
||||
return;
|
||||
}
|
||||
const current = opened;
|
||||
buildEditor(editor, {
|
||||
buildLabDetail(editor, {
|
||||
row: current.row as LogicRow,
|
||||
file: current.file,
|
||||
isNew: current.origKey === null,
|
||||
files: files.map((f) => f.file),
|
||||
reasons: current.reasons,
|
||||
prices: current.prices,
|
||||
lines: current.lines,
|
||||
values: current.values,
|
||||
onChange: touch,
|
||||
onFile: (file) => {
|
||||
const f = files.find((x) => x.file === file);
|
||||
if (!f) return;
|
||||
Object.assign(current, { file: f.file, sub: subOf(f), version: f.version });
|
||||
touch();
|
||||
},
|
||||
onPick: openPicker,
|
||||
calcHost: calc,
|
||||
});
|
||||
};
|
||||
|
||||
/** 로직 파일 → 구분(원문 + 부문) — 「공통 03장 토공사」 의 앞말이 부문 */
|
||||
const subOf = (f: LogicFile): string =>
|
||||
f.book === "건설품셈" ? `${f.book} ${f.chapter.split(" ")[0]}` : String(f.book);
|
||||
|
||||
const show = (next: Opened | null): void => {
|
||||
opened = next;
|
||||
errors.hidden = true;
|
||||
@@ -236,12 +220,7 @@ export async function mountM01LogicLab(
|
||||
let allLogics: LogicSummary[] = [];
|
||||
|
||||
const reload = async (): Promise<void> => {
|
||||
const [logics, logicFiles, subs] = await Promise.all([
|
||||
fetchLogics(),
|
||||
fetchLogicFiles(),
|
||||
fetchLogicSubs(),
|
||||
]);
|
||||
files = logicFiles;
|
||||
const [logics, subs] = await Promise.all([fetchLogics(), fetchLogicSubs()]);
|
||||
allLogics = logics;
|
||||
list.setItems(logics);
|
||||
// 왼쪽 「전체」 + 구분 → 상세구분 거름 — 고르면 목록으로 돌아와 그 범위만
|
||||
@@ -263,43 +242,6 @@ export async function mountM01LogicLab(
|
||||
persist();
|
||||
};
|
||||
|
||||
/** 옛 컨테이너의 「새로 만들기」 — 빈 줄 하나를 초안으로 세움(저장 때 서버가 키를 줌) */
|
||||
const onDraftNew = (): void => {
|
||||
const f = files.find((x) => x.file === opened?.file) ?? files[0];
|
||||
if (!f) return;
|
||||
const id = `new:${Date.now()}`;
|
||||
// 원문번호는 절 번호만(부문은 구분 칸) · 키는 저장 때 서버가 줌
|
||||
const row: LogicRow = {
|
||||
키: "",
|
||||
원문번호: `${tx("New_Key")} ${Object.keys(drafts).length + 1}`,
|
||||
이름: "",
|
||||
결과단위: "원/",
|
||||
출처: "",
|
||||
소유: "공용",
|
||||
입력: [],
|
||||
중간: [],
|
||||
호표: [],
|
||||
덧줄: [],
|
||||
끝수: null,
|
||||
};
|
||||
const next: Opened = {
|
||||
id,
|
||||
sub: subOf(f),
|
||||
file: f.file,
|
||||
version: f.version,
|
||||
origKey: null,
|
||||
row,
|
||||
original: null,
|
||||
prices: {},
|
||||
reasons: [],
|
||||
lines: null,
|
||||
values: {},
|
||||
};
|
||||
drafts[id] = pick(next, clone(row));
|
||||
persist();
|
||||
show(next);
|
||||
};
|
||||
|
||||
const onDelete = async (): Promise<void> => {
|
||||
if (!opened?.row) return;
|
||||
if (
|
||||
@@ -389,7 +331,6 @@ export async function mountM01LogicLab(
|
||||
back,
|
||||
el("h2", { text: tx("Title") }),
|
||||
pending,
|
||||
createButton({ label: tx("Bar_New"), variant: "ghost", onClick: onDraftNew }),
|
||||
createButton({ label: tx("Bar_Delete"), variant: "danger", onClick: () => void onDelete() }),
|
||||
discardButton,
|
||||
saveButton,
|
||||
@@ -403,7 +344,6 @@ export async function mountM01LogicLab(
|
||||
className: "m01-logic__main",
|
||||
children: [bar, errors, waiting, list.root, editor],
|
||||
}),
|
||||
calc,
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user