feat(M01): B01 마스터 데이터 단추 하나로 · 시중물가 「조달 찾기」 모달 · 찾기 API
- B01 옛 「마스터 데이터」(Z01) 단추 제거 · 「마스터 요소」 이름을 「마스터 데이터」 로
- 재료 › 시중물가 줄마다 「조달 찾기」 — 나라장터자재 + 시중물가 조달 줄을 이름·규격으로 찾아 「나라장터:<열쇠>」 연결 · 노랑 표시 · 연결 끊기
- GET /api/m01/procurement 추가 · 값 묶음(조달{…}) 확정 전이라 연결은 조달›연결 칸에 임시로 담음
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
/* =============================================================================
|
||||
* M01_MasterData_UI_Procure.ts
|
||||
* 재료 › 시중물가 「조달 찾기」 모달 — 조달 자료를 이름·규격으로 찾아 고르면 ref 를 돌려줌
|
||||
* ========================================================================== */
|
||||
|
||||
import { el, showToast } from "@ui/ui_template_elements";
|
||||
import { t as L } from "@ui/ui_template_locale";
|
||||
import { fetchProcurement, type ProcureItem } from "./M01_MasterData_Api_Fetch";
|
||||
import { show } from "./M01_MasterData_UI_Cells";
|
||||
|
||||
const button = (text: string): HTMLButtonElement =>
|
||||
el("button", { className: "m01-master__row-btn", text, attrs: { type: "button" } });
|
||||
|
||||
/** 고르면 `onPick(ref)` · 연결 끊기 = `onPick(null)`. */
|
||||
export function openProcureModal(current: string, onPick: (ref: string | null) => void): void {
|
||||
const close = (): void => back.remove();
|
||||
const input = el("input", {
|
||||
className: "m01-master__search",
|
||||
attrs: { type: "search", placeholder: L("M01_ProcureSearch") },
|
||||
});
|
||||
const list = el("div", { className: "m01-procure__list" });
|
||||
const cut = button(L("M01_ProcureCut"));
|
||||
cut.disabled = !current;
|
||||
cut.addEventListener("click", () => {
|
||||
onPick(null);
|
||||
close();
|
||||
});
|
||||
const shut = button(L("M01_ProcureClose"));
|
||||
shut.addEventListener("click", close);
|
||||
const box = el("div", {
|
||||
className: "m01-procure__box",
|
||||
children: [
|
||||
el("h3", { text: L("M01_ProcureTitle") }),
|
||||
el("p", { className: "m01-master__muted", text: current }),
|
||||
input,
|
||||
list,
|
||||
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: ProcureItem[], total: number): void => {
|
||||
list.replaceChildren(
|
||||
...(items.length
|
||||
? items.map((it) => {
|
||||
const b = el("button", {
|
||||
className: "m01-procure__item",
|
||||
attrs: { type: "button" },
|
||||
children: [
|
||||
el("strong", { text: it.이름 }),
|
||||
el("span", { text: `${it.규격} · ${it.단위} · ${show(it.값)}` }),
|
||||
el("span", { className: "m01-master__muted", text: it.ref }),
|
||||
],
|
||||
});
|
||||
b.addEventListener("click", () => {
|
||||
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 (): Promise<void> => {
|
||||
try {
|
||||
const r = await fetchProcurement(input.value.trim());
|
||||
paint(r.items, r.total);
|
||||
} catch (error) {
|
||||
showToast(error instanceof Error ? error.message : L("M01_LoadFailed"), "error");
|
||||
}
|
||||
};
|
||||
input.addEventListener("input", () => {
|
||||
window.clearTimeout(timer);
|
||||
timer = window.setTimeout(() => void run(), 300);
|
||||
});
|
||||
void run();
|
||||
input.focus();
|
||||
}
|
||||
Reference in New Issue
Block a user