/* ============================================================================= * M01_MasterData_UI_Logic_Wizard_Source.ts * 만들기 둘째 걸음 — 원문 절 고르기(책 → 부문 → 장 → 절) · 그 절(아래 절 포함)의 표를 용도와 함께 제시 * 절 목록 = `GET /sections` · 표 = `GET /tables?section=` — 번호를 손으로 넣지 않음 * ========================================================================== */ import { el } from "@ui/ui_template_elements"; import { fetchSections, fetchSectionTables, type SectionBrief, type TableBrief, } from "./M01_MasterData_UI_Logic_Wizard_Api"; import { select, textBox } from "./M01_MasterData_UI_Logic_Wizard_Atom"; import type { Draft } from "./M01_MasterData_UI_Logic_Wizard_Model"; import { tw } from "./M01_MasterData_UI_Logic_Wizard_Text"; import { fail, note, page, type Step } from "./M01_MasterData_UI_Logic_Wizard_Ui"; const sections = new Map>(); const load = (book: string): Promise => { let got = sections.get(book); if (!got) sections.set(book, (got = fetchSections(book))); return got; }; const distinct = (rows: SectionBrief[], key: "division" | "chapter"): string[] => [ ...new Set(rows.map((r) => r[key]).filter(Boolean)), ]; export const sourceStep: Step = { key: "S_Source", question: "Q_Source", ready: (d) => d.section.trim() !== "", render: ({ d, changed }) => { const filters = el("div", { className: "m01w__inline" }); const list = el("div", { className: "m01w__found" }); const picked = el("p", { className: "m01w__picked" }); const tables = el("div", { className: "m01w__stack" }); let all: SectionBrief[] = []; let division = ""; let chapter = ""; let q = ""; const drawTables = (): void => tables.replaceChildren( ...(d.section && !d.tables.length ? [note(tw("Section_None")), note(tw("Section_Skip"))] : d.tables.map((t) => tableRow(t, d, changed))), ); const choose = (s: SectionBrief): void => { d.section = s.section; d.tables = []; picked.textContent = `${tw("Section_Picked")} · ${s.section} ${s.title}`; changed(); drawList(); void fetchSectionTables(d.book, s.section) .then((found) => { d.tables = [...found]; // 처음에는 이 절의 표를 모두 씀 — 빼려면 체크를 풂 drawTables(); changed(); }) .catch(fail); }; const options = (values: string[]): { value: string; text: string }[] => values.map((v) => ({ value: v, text: v })); const draw = (): void => { const inDivision = all.filter((s) => !division || s.division === division); const chapters = distinct(inDivision, "chapter"); if (!chapters.includes(chapter)) chapter = chapters[0] ?? ""; const divisions = distinct(all, "division"); filters.replaceChildren( select( ["산림품셈", "건설품셈"].map((b) => ({ value: b, text: b })), d.book, (v) => { d.book = v; d.section = ""; d.tables = []; division = chapter = ""; picked.textContent = ""; drawTables(); changed(); start(); }, tw("Book"), ), ...(divisions.length ? [ select( options(divisions), division || divisions[0], (v) => ((division = v), (chapter = ""), draw()), tw("Division"), ), ] : []), select(options(chapters), chapter, (v) => ((chapter = v), draw()), tw("Chapter")), textBox(q, tw("Section_Search"), (v) => ((q = v.trim()), drawList())), ); drawList(); }; const drawList = (): void => { const inChapter = all.filter( (s) => (!division || s.division === division) && s.chapter === chapter && (!q || s.section.startsWith(q) || s.title.includes(q)), ); list.replaceChildren( ...inChapter.slice(0, 80).map((s) => { const row = el("button", { className: `m01w__row${s.section === d.section ? " is-active" : ""}`, attrs: { type: "button", "data-section": s.section }, text: `${s.section} ${s.title} · ${tw("Section_Counts", { t: s.tables, l: s.logics })}`, }); row.addEventListener("click", () => choose(s)); return row; }), ); }; const start = (): void => { void load(d.book) .then((rows) => { all = rows; const divisions = distinct(rows, "division"); division = divisions[0] ?? ""; const own = rows.find((s) => s.section === d.section); if (own) { division = own.division; chapter = own.chapter; picked.textContent = `${tw("Section_Picked")} · ${own.section} ${own.title}`; } draw(); }) .catch(fail); }; start(); drawTables(); return page("Q_Source", filters, list, picked, tables); }, }; function tableRow(t: TableBrief, d: Draft, changed: () => void): HTMLElement { const check = el("input", { attrs: { type: "checkbox", "data-table": t.키 } }); check.checked = d.tables.some((x) => x.키 === t.키); check.addEventListener("change", () => { d.tables = check.checked ? [...d.tables, t] : d.tables.filter((x) => x.키 !== t.키); changed(); }); const usage = t.용도?.대상?.join("·") ?? ""; return el("label", { className: "m01w__table", children: [ check, el("div", { className: "m01w__stack", children: [ el("strong", { text: `${t.원문번호} ${t.이름} · ${t.키}` }), el("span", { className: "m01w__muted", text: [ t.기준 ? `${tw("Table_Base")} ${t.기준}` : "", `${tw("Table_Cols")} ${Object.keys(t.값칸 ?? {}).join("·")}`, usage ? `${tw("Table_Usage")} ${usage}` : "", ] .filter(Boolean) .join(" · "), }), ], }), ], }); }