/* ============================================================================= * M01_MasterData_UI_Pick.ts * 고르기 모달 — 조달 자료(나라장터) · 가격 연결(시중물가) · 준용 직종을 이름·규격으로 찾아 고르면 ref 를 돌려줌 * ========================================================================== */ import { el, showToast } from "@ui/ui_template_elements"; import { t as L } from "@ui/ui_template_locale"; import { fetchPick, type PickItem, type PickKind } from "./M01_MasterData_Api_Fetch"; import { show } from "./M01_MasterData_UI_Cells"; /** 지역이 갈리는 재료 — 이름·규격만 적어 두고 지역은 계산 때 입력. */ export interface Cond { 이름: string; 규격: string; 지역: "입력"; } export interface PickOptions { kind: PickKind; title: string; /** 지금 연결 글 — 비면 「연결 끊기」 꺼짐. */ current: string; /** 처음 찾을 글 — 그 재료 이름·규격 (없는 결과면 첫 낱말만으로 다시). */ seed: string; /** 고르면 ref · 연결 끊기 = null. */ onPick: (ref: string | null) => void; /** 있으면 「후보 조건으로 연결」 칸이 뜸. */ cond?: { 이름: string; 규격: string; onCond: (c: Cond) => void }; } const button = (text: string): HTMLButtonElement => el("button", { className: "m01-master__row-btn", text, attrs: { type: "button" } }); const input = (value: string, placeholder: string): HTMLInputElement => { const box = el("input", { className: "m01-master__search", attrs: { type: "search", placeholder }, }); box.value = value; return box; }; export function openPickModal(opt: PickOptions): void { const close = (): void => back.remove(); const search = input(opt.seed, L("M01_ProcureSearch")); const list = el("div", { className: "m01-procure__list" }); const cut = button(L("M01_ProcureCut")); cut.disabled = !opt.current; cut.addEventListener("click", () => { opt.onPick(null); close(); }); const shut = button(L("M01_ProcureClose")); shut.addEventListener("click", close); const condRow: HTMLElement[] = []; if (opt.cond) { const { onCond } = opt.cond; const name = input(opt.cond.이름, L("M01_PriceCondName")); const spec = input(opt.cond.규격, L("M01_PriceCondSpec")); const go = button(L("M01_PriceCond")); go.addEventListener("click", () => { if (!name.value.trim()) return; onCond({ 이름: name.value.trim(), 규격: spec.value.trim(), 지역: "입력" }); close(); }); condRow.push(el("div", { className: "m01-procure__cond", children: [name, spec, go] })); } const box = el("div", { className: "m01-procure__box", children: [ el("h3", { text: opt.title }), el("p", { className: "m01-master__muted", text: opt.current }), search, list, ...condRow, el("div", { className: "m01-procure__foot", children: [cut, shut] }), ], }); const back = el("div", { className: "m01-procure", children: [box] }); back.addEventListener("click", (e) => { if (e.target === back) close(); }); document.body.append(back); const paint = (items: PickItem[], total: number): void => { list.replaceChildren( ...(items.length ? items.map((it) => { const tag = it.관급 ? ` · ${L("M01_Govt")}` : ""; const b = el("button", { className: "m01-procure__item", attrs: { type: "button" }, children: [ el("strong", { text: it.이름 }), el("span", { text: `${it.규격} · ${it.단위} · ${show(it.값)}${tag}` }), el("span", { className: "m01-master__muted", text: it.ref }), ], }); b.addEventListener("click", () => { opt.onPick(it.ref); close(); }); return b; }) : [el("p", { className: "m01-master__empty", text: L("M01_NoRows") })]), ...(total > items.length ? [el("p", { className: "m01-master__muted", text: `${items.length} / ${total}` })] : []), ); }; let timer: number | undefined; const run = async (fallback = false): Promise => { try { const q = search.value.trim(); const r = await fetchPick(opt.kind, q); if (!r.total && fallback && q.includes(" ")) { search.value = q.split(/\s+/)[0]; return run(); } paint(r.items, r.total); } catch (error) { showToast(error instanceof Error ? error.message : L("M01_LoadFailed"), "error"); } }; search.addEventListener("input", () => { window.clearTimeout(timer); timer = window.setTimeout(() => void run(), 300); }); void run(true); search.focus(); }