knowledge(마스터): 소요량·계수·로직에 구분·상세구분 — 표·줄마다 칸 · 갈래 목록은 서버가 줌

- 장 파일 137 개의 표 3,028 · 로직 1,351 줄에 구분(원문 + 부문 · 「건설품셈 공통」) · 상세구분(「03장 토공사」) 칸을 직접 둠 · 파일 머리의 부문·차례는 목록 차례로 남김
- 열 차례 — 표는 키 · 원문번호 · 구분 · 상세구분 · 이름 · 기준 · 출처 · 비고 · 조건 · 범위규칙 · 값칸 · 줄 · 주 · 그룹(맨 뒤) · 로직은 소유 다음에 비고
- 서버 — GET /subs?group= 이 구분·상세구분 목록(장 차례대로) · GET /tables 가 파일을 가로질러 구분·상세구분·찾기로 거르고 쪽 나눔 · GET /logics 도 구분·상세구분으로 거름 · 새 줄은 서버가 갈래를 채움 · 죽은 book 칸 걷어냄
- check_master 가 구분·상세구분이 파일 머리·이름과 맞는지 봄 · adopt 가 빌더 줄에 갈래를 찍어 되돌리기 시험 글자까지 같음 · _틀.md · 화면 계약 · 화면 트리 · 시험 같이 고침

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
2026-09-20 16:30:48 +09:00
co-authored by Claude Opus 5
parent 9de61eb251
commit 10418ccbd4
154 changed files with 8227 additions and 8046 deletions
+24 -15
View File
@@ -20,6 +20,7 @@ import {
fetchLogic,
fetchLogicFiles,
fetchLogics,
fetchLogicSubs,
saveFiles,
type CalcLine,
type ElementBrief,
@@ -36,7 +37,8 @@ import "./M01_MasterData_UI_Logic_Style.css";
/** 저장 안 한 로직 하나 — origKey null = 새 로직 · row null = 지움 */
interface Draft {
book: string;
/** 구분(원문 + 부문) — 목록 id 를 세움 */
sub: string;
file: string;
version: string;
origKey: string | null;
@@ -74,7 +76,7 @@ export async function mountM01Logic(host: HTMLElement, listHost: HTMLElement): P
const errors = el("div", { className: "m01-logic__reasons", attrs: { hidden: "" } });
const calc = el("aside", { className: "m01-logic__calc" });
const pending = el("span", { className: "m01-logic__muted" });
const list = buildList((item) => void open(item.id, item.book, item.key));
const list = buildList((item) => void open(item.id, item.sub, item.key));
const persist = (): void => {
try {
@@ -89,8 +91,8 @@ export async function mountM01Logic(host: HTMLElement, listHost: HTMLElement): P
if (d.origKey === null && d.row) {
extra.push({
id,
book: d.book,
chapter: "",
sub: d.sub,
detail: "",
key: d.row.키,
number: d.row.원문번호,
name: d.row.이름,
@@ -116,7 +118,7 @@ export async function mountM01Logic(host: HTMLElement, listHost: HTMLElement): P
};
const pick = (o: Opened, row: LogicRow | null): Draft => ({
book: o.book,
sub: o.sub,
file: o.file,
version: o.version,
origKey: o.origKey,
@@ -131,7 +133,6 @@ export async function mountM01Logic(host: HTMLElement, listHost: HTMLElement): P
const current = opened;
calcInputs = JSON.stringify(current.row?.입력 ?? []);
buildCalc(calc, {
book: current.book,
savedKey: current.origKey,
file: current.file,
row: current.row as LogicRow,
@@ -169,13 +170,17 @@ export async function mountM01Logic(host: HTMLElement, listHost: HTMLElement): P
onFile: (file) => {
const f = files.find((x) => x.file === file);
if (!f) return;
Object.assign(current, { file: f.file, book: f.book, version: f.version });
Object.assign(current, { file: f.file, sub: subOf(f), version: f.version });
touch();
},
onPick: openPicker,
});
};
/** 로직 파일 → 구분(원문 + 부문) — 「공통 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;
@@ -183,17 +188,17 @@ export async function mountM01Logic(host: HTMLElement, listHost: HTMLElement): P
drawCalc();
};
const open = async (id: string, book: string, key: string): Promise<void> => {
const open = async (id: string, sub: string, key: string): Promise<void> => {
const draft = drafts[id];
if (draft?.origKey === null) {
show({ ...draft, id, original: null, prices: {}, reasons: [], lines: null, values: {} });
return;
}
try {
const one = await fetchLogic(book, key);
const one = await fetchLogic(key);
show({
id,
book,
sub,
file: draft?.file ?? one.file,
version: draft?.version ?? one.version,
origKey: key,
@@ -210,9 +215,13 @@ export async function mountM01Logic(host: HTMLElement, listHost: HTMLElement): P
};
const reload = async (): Promise<void> => {
const [logics, logicFiles] = await Promise.all([fetchLogics(), fetchLogicFiles()]);
const [logics, logicFiles, subs] = await Promise.all([
fetchLogics(),
fetchLogicFiles(),
fetchLogicSubs(),
]);
files = logicFiles;
list.setItems(logics);
list.setItems(logics, subs);
persist();
};
@@ -237,7 +246,7 @@ export async function mountM01Logic(host: HTMLElement, listHost: HTMLElement): P
};
const next: Opened = {
id,
book: f.book,
sub: subOf(f),
file: f.file,
version: f.version,
origKey: null,
@@ -280,7 +289,7 @@ export async function mountM01Logic(host: HTMLElement, listHost: HTMLElement): P
persist();
const was = opened;
if (was && was.origKey !== null)
await open(logicId(was.book, was.origKey), was.book, was.origKey);
await open(logicId(was.sub, was.origKey), was.sub, was.origKey);
else show(null);
};
@@ -305,7 +314,7 @@ export async function mountM01Logic(host: HTMLElement, listHost: HTMLElement): P
drafts = {};
await reload();
showToast(tx("Save_Done"), "success");
if (was?.row?.키) await open(logicId(was.book, was.row.키), was.book, was.row.키);
if (was?.row?.키) await open(logicId(was.sub, was.row.키), was.sub, was.row.키);
else show(null);
} catch (error) {
failed(error);