- 조합 Api 가 층 길(/api/m02/layers/{layer}/combos) · 키 하나는 /api/m02/combos/key 로 — 회사 · 개인 조합도 읽음
- 로직 고르기 · 미리 보기는 /api/m02/logic-lib(로그인) 로 읽음
- M01 조합 화면 · M02 구조물 조합 쪽 부름을 새 자리로
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BU9gbNE3aWFUvE61pS2WGt
120 lines
4.5 KiB
TypeScript
120 lines
4.5 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_Labor_Pick.ts
|
|
* [로직 더하기] 창 — 구분 → 상세구분 → 찾기로 사용자가 로직을 하나씩 고름(자동 추천 없음)
|
|
* ========================================================================== */
|
|
|
|
import {
|
|
createButton,
|
|
createInputField,
|
|
createSelectField,
|
|
el,
|
|
showToast,
|
|
} from "@ui/ui_template_elements";
|
|
import { t as L } from "@ui/ui_template_locale";
|
|
import type { LogicSummary, SubBrief } from "../M01_MasterData/M01_MasterData_UI_Logic_Api";
|
|
import {
|
|
fetchLibLogics as fetchLogics,
|
|
fetchLibLogicSubs as fetchLogicSubs,
|
|
} from "./M02_MasterTemplete_Labor_Api";
|
|
import { tc } from "./M02_MasterTemplete_Labor_Text";
|
|
import "../M01_MasterData/M01_MasterData_UI_Logic_Style.css"; // 창 겉틀
|
|
import "./M02_MasterTemplete_Labor.css";
|
|
|
|
const opts = (values: string[]): { value: string; text: string }[] =>
|
|
[L("M01_All"), ...values].map((v, i) => ({ value: i ? v : "", text: v }));
|
|
|
|
export function openLogicPicker(done: (logic: LogicSummary) => void): void {
|
|
const subs: SubBrief[] = [];
|
|
let all: LogicSummary[] = [];
|
|
const sub = createSelectField({ options: opts([]), compact: true, label: tc("Sub") });
|
|
const detail = createSelectField({ options: opts([]), compact: true, label: tc("Detail") });
|
|
detail.select.disabled = true;
|
|
const searchField = createInputField({ type: "search", placeholder: tc("Pick_Search") });
|
|
const list = el("div", { className: "m01-logic__pick-list" });
|
|
const hint = el("p", { className: "m01-logic__muted", text: tc("Pick_Hint") });
|
|
const close = (): void => backdrop.remove();
|
|
|
|
const find = (): void => {
|
|
const q = searchField.input.value.trim().toLowerCase();
|
|
const found = all.filter(
|
|
(x) =>
|
|
(!sub.select.value || x.구분 === sub.select.value) &&
|
|
(!detail.select.value || x.상세구분 === detail.select.value) &&
|
|
(!q || `${x.키} ${x.이름}`.toLowerCase().includes(q)),
|
|
);
|
|
hint.textContent = found.length ? `${found.length} / ${all.length}` : tc("Pick_None");
|
|
list.replaceChildren(
|
|
...found.slice(0, 100).map((x) => {
|
|
const row = el("button", {
|
|
className: "m01-logic__pick-row m01-logic__pick-row--button",
|
|
attrs: { type: "button", "data-pick": x.키 },
|
|
children: [
|
|
el("div", {
|
|
className: "m01-logic__stack",
|
|
children: [
|
|
el("strong", { text: x.이름 }),
|
|
el("span", {
|
|
className: "m01-logic__muted",
|
|
text: `${x.키} · ${x.구분} ${x.상세구분}`.trim(),
|
|
}),
|
|
],
|
|
}),
|
|
el("span", { text: `${x.결과단위}${x.blocked ? ` · ${tc("Pick_Blocked")}` : ""}` }),
|
|
],
|
|
});
|
|
row.addEventListener("click", () => {
|
|
close();
|
|
done(x);
|
|
});
|
|
return row;
|
|
}),
|
|
);
|
|
};
|
|
sub.select.addEventListener("change", () => {
|
|
const d = subs.find((s) => s.name === sub.select.value)?.details ?? [];
|
|
detail.setOptions(opts(d), "");
|
|
detail.select.disabled = d.length === 0;
|
|
});
|
|
|
|
const dialog = el("div", {
|
|
className: "m01-logic__pick m01-combo",
|
|
attrs: { role: "dialog", "aria-label": tc("Pick_Title") },
|
|
children: [
|
|
el("div", {
|
|
className: "m01-logic__section-head",
|
|
children: [
|
|
el("h3", { text: tc("Pick_Title") }),
|
|
createButton({ label: tc("Pick_Close"), variant: "ghost", onClick: close }),
|
|
],
|
|
}),
|
|
el("div", {
|
|
className: "m01-logic__filters",
|
|
children: [
|
|
sub.root,
|
|
detail.root,
|
|
searchField.root,
|
|
createButton({ label: tc("Pick_Find"), variant: "filled", onClick: find }),
|
|
],
|
|
}),
|
|
hint,
|
|
list,
|
|
],
|
|
});
|
|
const backdrop = el("div", { className: "m01-logic__backdrop", children: [dialog] });
|
|
backdrop.addEventListener("click", (ev) => ev.target === backdrop && close());
|
|
backdrop.addEventListener("keydown", (ev) => ev.key === "Escape" && close());
|
|
document.body.append(backdrop);
|
|
searchField.input.addEventListener("keydown", (ev) => ev.key === "Enter" && find());
|
|
searchField.input.focus();
|
|
|
|
void Promise.all([fetchLogicSubs(), fetchLogics()])
|
|
.then(([s, l]) => {
|
|
subs.push(...s);
|
|
all = l;
|
|
sub.setOptions(opts(s.map((x) => x.name)), "");
|
|
})
|
|
.catch((error) =>
|
|
showToast(error instanceof Error ? error.message : tc("Load_Failed"), "error"),
|
|
);
|
|
}
|