feat(M01): 테스트 컨테이너 고르기 부품 공용화 — 구분 → 상세구분 → 후보 거름 · B·C 에도 재료 바꿔 보기

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
2026-09-20 22:14:22 +09:00
co-authored by Claude Sonnet 5
parent 35588d9aa5
commit 03e6b6511e
8 changed files with 216 additions and 84 deletions
+32 -2
View File
@@ -26,6 +26,8 @@ import {
type NamedFormula,
} from "./M01_MasterData_UI_Logic_Api";
import { formatNumber } from "./M01_MasterData_UI_Logic_Edit";
import { hasPickable, pickPanel, swappedRow, type Swaps } from "./M01_MasterData_UI_Test_Pick";
import { tt } from "./M01_MasterData_UI_Test_Text";
/* ── 원문 표(소요량·계수) 미리보기 — /elements 로 파일을 찾고 /table 로 통째 읽음 ── */
@@ -35,6 +37,7 @@ interface TableRow {
이름?: string;
원문번호?: string;
출처?: string;
?: { 공종?: string; 대상?: string[]; 로직키?: string[] };
조건?: Record<string, string>;
값칸?: Record<string, string>;
줄?: Record<string, unknown>[];
@@ -218,13 +221,24 @@ interface State {
answer: CalcAnswer | null;
selected: number;
calcSeq: number;
swaps: Swaps;
picker: HTMLElement;
}
export function render(host: HTMLElement, logicKey: string): void {
host.replaceChildren(el("p", { className: "m01-logic__muted", text: "불러오는 중…" }));
void fetchLogic(logicKey)
.then((one) => {
const state: State = { one, values: {}, answer: null, selected: 0, calcSeq: 0 };
const swaps: Swaps = new Map();
const state: State = {
one,
values: {},
answer: null,
selected: 0,
calcSeq: 0,
swaps,
picker: pickPanel(one, swaps, () => void recalc(host, state)),
};
paint(host, state);
void recalc(host, state);
})
@@ -249,7 +263,7 @@ async function recalc(host: HTMLElement, state: State): Promise<void> {
const answer = await runCalc({
key: state.one.logic.키,
inputs,
row: state.one.logic,
row: swappedRow(state.one.logic, state.swaps) ?? state.one.logic,
file: state.one.file,
});
if (seq !== state.calcSeq) return; // 최신 응답만 채택
@@ -309,6 +323,14 @@ function paint(host: HTMLElement, state: State): void {
main.append(
el("h3", { text: `${row.} ${row.}` }),
inputWrap,
...(hasPickable(state.one.logic)
? [
el("details", {
attrs: { open: "" },
children: [el("summary", { text: tt("Mat_Title") }), state.picker],
}),
]
: []),
buildHoTable(host, state),
);
if (state.answer && !state.answer.ok) {
@@ -434,6 +456,14 @@ async function buildCard(side: HTMLElement, state: State): Promise<void> {
className: "m01-logic__muted",
text: `${r.source} · ${table. ?? table.} ${table. ?? ""} (${table. ?? ""})`,
}),
...(table.
? [
el("p", {
className: "m01-logic__muted",
text: `${tt("Use_Of")}${[table.., (table.. ?? []).join("·")].filter(Boolean).join(" · ")}`,
}),
]
: []),
el("table", {
className: "m01-logic__grid",
children: [