/* ============================================================================= * M02_DF_InputPanel_Rule.ts * 입력 판 — 인력 · 기계 · 유가전력의 「조건 찾기」: 이름 또는 키가 이 로직 변수 알약 값과 같은 줄을 잡음 * (옛 로직의 LB:{직종} · EQ:{기계} 꼴) · 지금 잡히는 줄 미리 보기(0 · 여럿이면 까닭) · 글 [한, 영] 짝은 아래 * 선택 자리(choice)는 재료만 — 이 판에 없음 * ========================================================================== */ import { currentLanguageIndex } from "@ui/ui_template_locale"; import { createButton, createInputField, createSelectField, el, showToast, } from "@ui/ui_template_elements"; import type { CfElementRef, CfGroup, CfLogic } from "./M02_DF_Model"; import { catchNote, checkGroupRule, groupRuleElement, variablePills, type GroupRuleField, } from "./M02_DF_InputPanel_Build"; const TEXT = { Help: [ "변수 알약 값과 이름(또는 키)이 같은 줄을 잡음 — 예: 직종 알약 값이 「석공」이면 석공 줄", "Catches the row whose name (or key) equals a variable pill's value", ], SlotName: ["자리 이름(알약 이름)", "Slot name (pill name)"], Field: ["같아야 하는 칸", "Column"], FieldName: ["이름", "Name"], FieldKey: ["키(원문번호)", "Key (source no.)"], Pill: ["변수 알약", "Variable pill"], NoPill: ["변수 알약이 없음 — 변수 입력 상자를 먼저 만듦", "No variable pill yet"], Preview: ["지금 잡히는 줄", "Rows caught now"], Put: ["넣기", "Add"], Failed: ["불러오지 못함", "Could not load"], } as const; type TKey = keyof typeof TEXT; const t = (key: TKey): string => TEXT[key][currentLanguageIndex] as string; /** 미리 보기 길 — 그룹 · 칸 · 알약 값으로 마스터 줄을 읽어 잡힌 수 · 이름 · 단위(부르는 쪽이 줌) */ export type RulePreview = ( group: CfGroup, field: GroupRuleField, value: unknown, ) => Promise<{ found: number; names: string[]; unit: string }>; /** 조건 찾기 요소 추가 판 */ export function renderGroupRule( host: HTMLElement, lg: CfLogic, boxId: string, group: CfGroup, addEl: (lg: CfLogic, el: CfElementRef) => CfLogic, preview: RulePreview, on: { change(lg: CfLogic): void }, ): void { const els = lg.boxes.find((b) => b.id === boxId)?.input?.elements ?? []; const pills = variablePills(lg); const name = createInputField({ type: "text", label: t("SlotName"), compact: true, placeholder: group === "인력" ? "예: 직종" : "예: 기종", }); const field = createSelectField({ label: t("Field"), options: [ { value: "이름", text: t("FieldName") }, { value: "키", text: t("FieldKey") }, ], value: "이름", compact: true, onChange: () => void refresh(), }); const pill = createSelectField({ label: t("Pill"), options: pills.map((p, i) => ({ value: String(i), text: `${p.port.name}${p.example !== null ? ` = ${p.example}` : ""}`, })), value: "0", compact: true, onChange: () => void refresh(), }); const note = el("p", { className: "m02-dfip__note" }); const build = (): CfElementRef => groupRuleElement( name.input.value, field.select.value as GroupRuleField, pills[Number(pill.select.value)]?.port ?? { box: "", name: "" }, ); let seq = 0; const refresh = async (): Promise => { const mine = ++seq; const value = pills[Number(pill.select.value)]?.example; note.textContent = `${t("Preview")}: …`; try { const p = await preview(group, field.select.value as GroupRuleField, value); if (mine !== seq) return; const n = catchNote(p.found, p.names[0], "줄"); note.textContent = `${t("Preview")}: ${n.text}`; note.className = `m02-dfip__note${n.kind === "one" ? "" : " is-bad"}`; } catch { if (mine === seq) note.textContent = t("Failed"); } }; const add = createButton({ label: t("Put"), size: "sm", onClick: () => { const cand = build(); const why = checkGroupRule(cand, els); name.setError(why ?? undefined); if (why) return void showToast(why, "error"); // 잡히는 줄이 하나면 그 단위를 알약 단위로 void preview( group, field.select.value as GroupRuleField, pills[Number(pill.select.value)]?.example, ).then( (p) => on.change(addEl(lg, p.unit ? { ...cand, unit: p.unit } : cand)), () => on.change(addEl(lg, cand)), ); }, }); host.replaceChildren( el("p", { className: "m02-dfip__muted", text: t("Help") }), name.root, ...(pills.length ? [field.root, pill.root, note, add] : [el("p", { className: "m02-dfip__muted is-bad", text: t("NoPill") })]), ); if (pills.length) void refresh(); }