feat(z01): 마스터 화면에 공종 축 산림·건설 상자 — base-prices 목록·한 표가 공종 축 둘을 포함 · 거르기 인자 · WorkItems 읽기 모듈 신설

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 19:00:53 +09:00
co-authored by Claude Fable 5
parent 459033786b
commit e6bc8f8deb
8 changed files with 434 additions and 24 deletions
+37 -2
View File
@@ -10,6 +10,7 @@
import {
createButton,
createInputField,
createSelectField,
el,
showConfirmDialog,
showToast,
@@ -21,6 +22,7 @@ import {
cellInfo,
clampPage,
columnTitle,
filterValue,
latestOnly,
nextSort,
noticeLines,
@@ -90,7 +92,12 @@ function stateText(info: CellInfo, column: MasterColumn): string {
export function buildRowsView(): RowsView {
let source: RowsSource | null = null;
const state = { page: 1, q: "", sort: null as SortOrder | null };
const state = {
page: 1,
q: "",
sort: null as SortOrder | null,
filters: {} as Record<string, string>,
};
let showHidden = false;
let last: MasterRows | null = null;
let selected: { index: number; key: string } | null = null;
@@ -137,9 +144,10 @@ export function buildRowsView(): RowsView {
const prev = pagerButton("Z01_MasterData_Prev", () => goTo(state.page - 1));
const next = pagerButton("Z01_MasterData_Next", () => goTo(state.page + 1));
const end = pagerButton("Z01_MasterData_Last", () => goTo(Infinity));
const filterBox = el("div", { className: "z01-master__toolbar" });
const toolbar = el("div", {
className: "z01-master__toolbar",
children: [search.root, hiddenLabel],
children: [search.root, filterBox, hiddenLabel],
});
const pager = el("div", {
className: "z01-master__pager",
@@ -180,6 +188,7 @@ export function buildRowsView(): RowsView {
size: PAGE_SIZE,
q: state.q,
sort: state.sort,
filters: state.filters,
});
} catch (error) {
grid.classList.remove("is-loading");
@@ -250,6 +259,30 @@ export function buildRowsView(): RowsView {
const outdated = outdatedBanner(meta.source);
banner.replaceChildren(...outdated.map((text) => el("p", { text: `⚠ ${text}` })));
banner.hidden = outdated.length === 0;
// 거르기 — 선택지·이름·줄 수·기본값도, 어느 줄을 거를지 판정도 서버 것(화면은 고른 값만 보냄).
filterBox.replaceChildren(
...(meta.filters ?? []).map(
(filter) =>
createSelectField({
label: filter.label,
options: filter.options.map((option) => ({
value: option.value,
text:
option.rows == null
? option.label
: `${option.label} (${option.rows.toLocaleString("ko-KR")})`,
})),
value: filterValue(filter, state.filters),
onChange: (value) => {
state.filters[filter.key] = value;
state.page = 1;
void load();
},
}).root,
),
);
// 숨긴 열이 없는 표(17 중 15)에서는 눌러도 아무 일이 없어 체크를 감춤.
hiddenLabel.hidden = !meta.columns.some((column) => column.hidden);
const columns = shownColumns(meta.columns, showHidden);
const headRow = el("tr", { children: columns.map((column) => headCell(meta, column)) });
const body = el("tbody");
@@ -429,6 +462,8 @@ export function buildRowsView(): RowsView {
source = picked;
state.page = 1;
state.sort = null;
state.filters = {};
filterBox.replaceChildren();
title.textContent = picked.title;
titleKey.textContent = picked.key;
notice.hidden = true;