feat(M01): 새로 만들기 모달에 새 길 잇기 — 절 목록으로 원문 절 고르기 · 절로 표 거르기 · 표 조건 값 목록 · 초안 시험 계산 · 자체 로직 저장(GX) · 본떠 만들기
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,178 @@
|
||||
/* =============================================================================
|
||||
* 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<string, Promise<SectionBrief[]>>();
|
||||
const load = (book: string): Promise<SectionBrief[]> => {
|
||||
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(" · "),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user