/* ============================================================================= * M01_MasterData_UI_LogicLab_SourceFind.ts * 원문 패널 맨 위 「단가산출 기초 찾기」(PLAN 807-1 · 요구 699) — M01 로직 상세 · M02 단가산출 원문 패널이 같이 씀. * M01 로직 목록(로그인 길 `/m02/logic-lib/logics` · 처음 칠 때 한 번 읽음) · 낱말 맞춤 = 공용 `matchLogic` * (키 · 원문번호 · 이름 · 구분 · 별칭) · 30 줄까지 · 고르면 `onPick`(부르는 쪽이 그 출처 원문을 보임 · 기억 안 함) * 빈 칸 = 목록 비움 + `onClear`(부르는 쪽이 처음 화면으로) * ========================================================================== */ import { API_BASE_URL } from "@config/config_frontend"; import { currentLanguageIndex } from "@ui/ui_template_locale"; import { createFindList } from "@ui/ui_template_find_list"; import { matchLogic } from "@ui/ui_template_logic_find"; const TEXT = { Find: ["단가산출 기초 찾기 — 이름 · 원문번호", "Find base build-up — name · clause"], Many: ["{n} 개 중 30 개 — 낱말을 더 침", "Showing 30 of {n} — type more"], None: ["맞는 기초 없음", "No match"], Failed: ["기초 목록을 못 읽음", "Failed to load"], } as const; const ts = (key: keyof typeof TEXT, n = 0): string => TEXT[key][currentLanguageIndex === 1 ? 1 : 0].replace("{n}", String(n)); export interface FoundLogic { 키: string; 원문번호?: string; 이름: string; 구분?: string; 상세구분?: string; 별칭?: string | null; 결과단위?: string; 출처?: string; } const MAX = 30; let all: Promise | null = null; // 한 화면 수명 동안 한 번(M01 로직 목록) const logics = (): Promise => (all ??= fetch(`${API_BASE_URL}/m02/logic-lib/logics`, { credentials: "include" }) .then((r) => (r.ok ? (r.json() as Promise<{ logics: FoundLogic[] }>) : Promise.reject())) .then((d) => d.logics) .catch((e: unknown) => { all = null; throw e; })); export function createSourceFind(opts: { onPick: (logic: FoundLogic) => void; onClear: () => void; }): { root: HTMLElement; clear: () => void } { const find = createFindList({ placeholder: ts("Find") }); find.root.classList.add("m01lab-src__find"); find.root.setAttribute("data-source-find", ""); let seq = 0; const run = async (): Promise => { const q = find.search.value.trim(); const mine = ++seq; if (!q) { find.setRows([]); find.hint.textContent = ""; return opts.onClear(); } let rows: FoundLogic[]; try { rows = await logics(); } catch { find.hint.textContent = ts("Failed"); return; } if (mine !== seq) return; const hit = rows.filter((x) => matchLogic(q, x).ok); find.hint.textContent = hit.length > MAX ? ts("Many", hit.length) : hit.length ? "" : ts("None"); // prettier-ignore find.setRows( hit.slice(0, MAX).map((x) => ({ key: x.키, tag: x.원문번호 || x.키, name: x.이름, sub: [x.구분, x.상세구분].filter(Boolean).join(" · "), unit: x.결과단위 ?? "", onPick: () => { find.setRows([]); find.hint.textContent = `${x.원문번호 || x.키} ${x.이름}`; opts.onPick(x); }, })), ); }; find.search.addEventListener("input", () => void run()); return { root: find.root, clear: () => { find.search.value = ""; find.setRows([]); find.hint.textContent = ""; }, }; }