feat(M01): 로직 개선 시험 「식으로 새로 만들기」 화면 — 식 적기 · 분석 · 변수 정하기 · 막힌 자리 짚기 · 시험 계산 · 저장 (PLAN 3-3)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
/* =============================================================================
|
||||
* M01_MasterData_UI_LogicLab_New_Var.ts
|
||||
* 변수 한 칸 — 무엇인지(마스터 요소 / 설계 입력 / 고정값 / 표 찾기) · 단위 · 비목을 고름
|
||||
* 고른 것은 `decided[이름]` 에 쌓음(서버 계약 `_화면_계약.md` 7장의 `decided` 그대로)
|
||||
* ========================================================================== */
|
||||
|
||||
import { createButton, createInputField, createSelectField, el } from "@ui/ui_template_elements";
|
||||
import type { DraftDecided, DraftVar } from "./M01_MasterData_UI_Logic_Api";
|
||||
import { openElementPick, openTablePick } from "./M01_MasterData_UI_LogicLab_New_Find";
|
||||
import { tn } from "./M01_MasterData_UI_LogicLab_New_Text";
|
||||
|
||||
export interface VarCtx {
|
||||
decided: Record<string, DraftDecided>;
|
||||
/** 고른 것이 바뀜 — 화면이 검사를 다시 부름 */
|
||||
onEdit: () => void;
|
||||
}
|
||||
export interface VarRow {
|
||||
root: HTMLElement;
|
||||
/** 서버가 다시 준 변수(식으로 정해진 단위 등)를 반영 */
|
||||
update: (v: DraftVar) => void;
|
||||
}
|
||||
|
||||
const KINDS: [string, () => string][] = [
|
||||
["마스터요소", () => tn("K_master")],
|
||||
["설계입력", () => tn("K_input")],
|
||||
["고정값", () => tn("K_fixed")],
|
||||
["표찾기", () => tn("K_table")],
|
||||
];
|
||||
const COSTS = ["노무비", "재료비", "경비"];
|
||||
/** 요소 종류 → 비목 첫 제안 */
|
||||
const COST_OF: Record<string, string> = { 인력: "노무비", 재료: "재료비", 기계: "경비" };
|
||||
|
||||
const costOptions = (): { value: string; text: string }[] => [
|
||||
{ value: "", text: tn("CostNone") },
|
||||
...COSTS.map((c) => ({ value: c, text: c })),
|
||||
];
|
||||
|
||||
const field = (label: string, control: HTMLElement): HTMLElement =>
|
||||
el("label", {
|
||||
className: "m01-logic__field",
|
||||
children: [el("span", { text: label }), control],
|
||||
});
|
||||
|
||||
export function varRow(v: DraftVar, isResult: boolean, ctx: VarCtx): VarRow {
|
||||
const name = v.이름;
|
||||
const info = el("span", { className: "m01-logic__muted m01lab-new__derived" });
|
||||
const body = el("div", { className: "m01lab-new__var-body" });
|
||||
const root = el("div", {
|
||||
className: "m01lab-new__var",
|
||||
attrs: { "data-var": name },
|
||||
children: [
|
||||
el("div", {
|
||||
className: "m01lab-new__var-head",
|
||||
children: [el("strong", { text: name }), info],
|
||||
}),
|
||||
body,
|
||||
],
|
||||
});
|
||||
const now = (): DraftDecided => (ctx.decided[name] ??= {});
|
||||
const set = (patch: DraftDecided): void => {
|
||||
ctx.decided[name] = { ...now(), ...patch };
|
||||
for (const [k, val] of Object.entries(ctx.decided[name])) {
|
||||
if (val === "" || val === undefined) delete (ctx.decided[name] as Record<string, unknown>)[k];
|
||||
}
|
||||
ctx.onEdit();
|
||||
};
|
||||
const text = (label: string, key: "단위" | "값" | "설명", wide = false): HTMLElement => {
|
||||
const box = createInputField({
|
||||
type: "text",
|
||||
value: now()[key] ?? "",
|
||||
onInput: (val) => set({ [key]: val.trim() }),
|
||||
});
|
||||
box.input.setAttribute("data-key", key);
|
||||
const cell = field(label, box.root);
|
||||
if (wide) cell.classList.add("m01lab-new__wide");
|
||||
return cell;
|
||||
};
|
||||
|
||||
let picked = ""; // 마스터 요소·표 찾기에서 고른 것의 글
|
||||
const draw = (): void => {
|
||||
const kind = now().무엇 ?? "";
|
||||
if (v.갈래 === "비목합") {
|
||||
body.replaceChildren(el("span", { className: "m01-logic__muted", text: tn("SumName") }));
|
||||
return;
|
||||
}
|
||||
if (v.갈래 === "정의됨") {
|
||||
if (isResult) return body.replaceChildren();
|
||||
const cost = createSelectField({
|
||||
options: costOptions(),
|
||||
value: now().비목 ?? "",
|
||||
compact: true,
|
||||
label: tn("CostExtra"),
|
||||
onChange: (val) => set({ 비목: val }),
|
||||
});
|
||||
body.replaceChildren(cost.root);
|
||||
return;
|
||||
}
|
||||
const kindBox = createSelectField({
|
||||
options: [
|
||||
{ value: "", text: "—" },
|
||||
...KINDS.map(([value, label]) => ({ value, text: label() })),
|
||||
],
|
||||
value: kind,
|
||||
compact: true,
|
||||
label: tn("Kind"),
|
||||
onChange: (val) => {
|
||||
ctx.decided[name] = val ? { 무엇: val, ...(now().단위 ? { 단위: now().단위 } : {}) } : {};
|
||||
picked = "";
|
||||
draw();
|
||||
ctx.onEdit();
|
||||
},
|
||||
});
|
||||
const parts: HTMLElement[] = [kindBox.root];
|
||||
if (kind === "마스터요소") {
|
||||
const cost = createSelectField({
|
||||
options: costOptions(),
|
||||
value: now().비목 ?? "",
|
||||
compact: true,
|
||||
label: tn("Cost"),
|
||||
onChange: (val) => set({ 비목: val }),
|
||||
});
|
||||
const choose = createButton({
|
||||
label: now().요소 ? tn("Repick") : tn("Pick"),
|
||||
variant: "ghost",
|
||||
onClick: () =>
|
||||
openElementPick((p) => {
|
||||
picked = `${p.name} · ${p.ref}`;
|
||||
set({ 요소: p.ref, 단위: p.unit, 비목: COST_OF[p.group] ?? now().비목 });
|
||||
draw();
|
||||
}),
|
||||
});
|
||||
choose.setAttribute("data-act", "pick-element");
|
||||
parts.push(
|
||||
el("div", {
|
||||
className: "m01lab-new__picked",
|
||||
children: [choose, el("span", { text: picked || now().요소 || tn("NotPicked") })],
|
||||
}),
|
||||
text(tn("Unit"), "단위"),
|
||||
cost.root,
|
||||
);
|
||||
} else if (kind === "설계입력") {
|
||||
parts.push(text(tn("Unit"), "단위"), text(tn("Note"), "설명", true));
|
||||
} else if (kind === "고정값") {
|
||||
parts.push(text(tn("Value"), "값"), text(tn("Unit"), "단위"));
|
||||
} else if (kind === "표찾기") {
|
||||
const choose = createButton({
|
||||
label: now().찾기 ? tn("Repick") : tn("TablePick"),
|
||||
variant: "ghost",
|
||||
onClick: () =>
|
||||
openTablePick((find, label) => {
|
||||
picked = label;
|
||||
set({ 찾기: find });
|
||||
draw();
|
||||
}),
|
||||
});
|
||||
choose.setAttribute("data-act", "pick-table");
|
||||
parts.push(
|
||||
el("div", {
|
||||
className: "m01lab-new__picked",
|
||||
children: [choose, el("span", { text: picked || tn("NotPicked") })],
|
||||
}),
|
||||
text(tn("Unit"), "단위"),
|
||||
);
|
||||
}
|
||||
body.replaceChildren(...parts);
|
||||
};
|
||||
draw();
|
||||
|
||||
const update = (next: DraftVar): void => {
|
||||
v = next;
|
||||
info.textContent =
|
||||
next.갈래 === "정의됨"
|
||||
? `${tn("Derived")}${next.단위 ? ` · ${next.단위}` : ""}`
|
||||
: (next.단위 ?? "");
|
||||
};
|
||||
update(v);
|
||||
return { root, update };
|
||||
}
|
||||
Reference in New Issue
Block a user