diff --git a/M01_MasterData/M01_MasterData_UI_Combo_Preview.ts b/M01_MasterData/M01_MasterData_UI_Combo_Preview.ts index b688c479..4955f7df 100644 --- a/M01_MasterData/M01_MasterData_UI_Combo_Preview.ts +++ b/M01_MasterData/M01_MasterData_UI_Combo_Preview.ts @@ -5,10 +5,11 @@ * ========================================================================== */ import { createInputField, createSelectField, el } from "@ui/ui_template_elements"; -import { fetchLogic, type LogicInput } from "./M01_MasterData_UI_Logic_Api"; +import { fetchAuto, fetchLogic, type LogicInput } from "./M01_MasterData_UI_Logic_Api"; import { previewCombo, type Combo } from "./M01_MasterData_UI_Combo_Api"; import { tc, type ComboTextKey } from "./M01_MasterData_UI_Combo_Text"; import { formatMoney } from "./M01_MasterData_UI_Logic_Money"; +import { tx } from "./M01_MasterData_UI_Logic_Text"; const COSTS = ["노무비", "재료비", "경비"] as const; const LABEL: Record<(typeof COSTS)[number], ComboTextKey> = { @@ -29,6 +30,8 @@ function inputCell( input: LogicInput, key: string, values: Record>, + sample: Set, + onEdit: () => void, ): HTMLElement { const mine = (values[key] ??= {}); const value = mine[input.이름] ?? initial(input); @@ -40,8 +43,20 @@ function inputCell( const control = "select" in field ? field.select : field.input; control.value = value; control.setAttribute("data-input", `${key}|${input.이름}`); - control.addEventListener("input", () => (mine[input.이름] = control.value)); - control.addEventListener("change", () => (mine[input.이름] = control.value)); + const tag = sample.has(`${key}|${input.이름}`) + ? el("span", { className: "m01-logic__tag", text: tx("Calc_Sample") }) + : undefined; + if (tag) field.root.append(tag); + const edit = (): void => { + mine[input.이름] = control.value; + sample.delete(`${key}|${input.이름}`); + tag?.remove(); + }; + control.addEventListener("input", edit); + control.addEventListener("change", () => { + edit(); + onEdit(); // 바꾸면 다시 미리 보기 계산 + }); return field.root; } @@ -55,6 +70,9 @@ export function buildPreview(combo: () => Combo): PreviewHandle { const values: Record> = {}; /** 서버가 받을 꼴 — 원래 숫자·고르기 값으로 되돌림 */ const inputs = new Map(); + /** 견본(자동값)으로 채운 칸 `로직|입력` — 사용자가 고치면 빠짐 · 로직마다 자동값은 한 번만 받음 */ + const sample = new Set(); + const asked = new Set(); const form = el("div", { className: "m01-logic__stack" }); const out = el("div", { className: "m01-master__grid-wrap", @@ -131,7 +149,18 @@ export function buildPreview(combo: () => Combo): PreviewHandle { inputs.clear(); for (const row of combo().담은로직) { try { - const one = await fetchLogic(row.로직); + const [one, auto] = await Promise.all([ + fetchLogic(row.로직), + asked.has(row.로직) ? Promise.resolve({}) : fetchAuto(row.로직), + ]); + asked.add(row.로직); + const mine = (values[row.로직] ??= {}); + for (const i of one.logic.입력) { + const got = (auto as Record)[i.이름]; + if (got === undefined || got === null || (mine[i.이름] ?? "").trim() !== "") continue; + mine[i.이름] = String(got); + sample.add(`${row.로직}|${i.이름}`); + } inputs.set(row.로직, one.logic.입력); blocks.push( el("div", { @@ -141,7 +170,9 @@ export function buildPreview(combo: () => Combo): PreviewHandle { className: "m01lab__cell--wide", text: `${row.로직} ${one.logic.이름}`, }), - ...one.logic.입력.map((i) => inputCell(i, row.로직, values)), + ...one.logic.입력.map((i) => + inputCell(i, row.로직, values, sample, () => void run()), + ), ], }), ); @@ -150,7 +181,9 @@ export function buildPreview(combo: () => Combo): PreviewHandle { } } form.replaceChildren(...blocks); - out.replaceChildren(el("p", { className: "m01-logic__muted", text: tc("Prev_Wait") })); + if (blocks.length) + await run(); // 열자마자 한 번 미리 보기 계산 + else out.replaceChildren(el("p", { className: "m01-logic__muted", text: tc("Prev_Wait") })); }; const root = el("section", {