feat(Z01): 기초단가 표 [저장] 방식 교체 · 공종 편집 화면 신설 — PLAN 1-4
- 기초단가 표: 칸 나갈 때 즉시 저장 없앰 · 고친 칸·새 줄·지울 줄은 초안(sessionStorage z01-draft:base:{kind})에 쌓고 [저장] 한 번에 보냄 · [고친 것 버리기]·[줄 추가]·[줄 삭제]·[초기값으로] · 409 면 초안 지키고 알림 · 서버가 revision 을 줄 때만 고치기 단추 섬
- 옛 덮개 길(칸 단위 PUT · 고친 것 모아 보기 · 덮개 판정 표시)과 그 사전 글자 뺌 — 계약 1-0 에서 서버 쪽 삭제
- 공종 편집(Z01_MasterData_UI_WorkItem.ts · _WorkItem_Rows.ts): 왼쪽 목록(상태별 개수·검색) / 머리 / 구성 줄 표(갈래별 · 자원 찾기 = 기초단가 검색) / 품셈 원문 표+[주] 읽기 전용 / [저장]·[초기값으로]·[검수 완료] · 초안 열쇠 z01-draft:work_item:{key}
- 공종 API(sub_laptop_3 e1c71c3e) 모양에 맞춤 — 줄은 id + 계약 칸 열하나만 보냄(extra=forbid) · 저장·초기값 응답(새 GET 모양)으로 바로 다시 그림 · 저장된 고침(changed_columns·is_added) 표시
- 초안 순수 함수 Z01_MasterData_UI_Draft.ts + 시험 test_z01_master_draft.py
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145cr1DMC5Ft8fouQSXtp69
This commit is contained in:
@@ -12,25 +12,22 @@ import { t as L } from "@ui/ui_template_locale";
|
||||
import {
|
||||
fetchBasePrices,
|
||||
fetchMasterRows,
|
||||
fetchOverrides,
|
||||
resetBasePrices,
|
||||
saveBasePrices,
|
||||
type MasterGroup,
|
||||
type MasterRows,
|
||||
type RowsQuery,
|
||||
saveBasePrice,
|
||||
VALUES_TABLE_ID,
|
||||
type WorkItemAxis,
|
||||
} from "./Z01_MasterData_Api_Fetch";
|
||||
import {
|
||||
groupKinds,
|
||||
valueWithUnit,
|
||||
type OverrideState,
|
||||
type ServedKind,
|
||||
} from "./Z01_MasterData_UI_Cells";
|
||||
import { groupKinds, type ServedKind } from "./Z01_MasterData_UI_Cells";
|
||||
import type { RowsView } from "./Z01_MasterData_UI_Rows";
|
||||
|
||||
const AXES: WorkItemAxis[] = ["forest", "const"];
|
||||
|
||||
export function buildSidePanel(
|
||||
groups: MasterGroup[],
|
||||
kinds: ServedKind[],
|
||||
view: RowsView,
|
||||
openWorkItems: (axis: WorkItemAxis) => void,
|
||||
): HTMLElement {
|
||||
const panel = el("div", { className: "z01-master__side" });
|
||||
const activate = (button: HTMLElement): void => {
|
||||
@@ -59,29 +56,32 @@ export function buildSidePanel(
|
||||
title: `${box.label} › ${served.label}`,
|
||||
key: `base-prices/${served.kind}`,
|
||||
load: (query) => fetchBasePrices(served.kind, query),
|
||||
save: (rowId, values) => saveBasePrice(served.kind, rowId, values),
|
||||
edit: {
|
||||
scope: `base:${served.kind}`,
|
||||
save: (body) => saveBasePrices(served.kind, body),
|
||||
reset: (rowKeys) => resetBasePrices(served.kind, rowKeys),
|
||||
},
|
||||
});
|
||||
});
|
||||
grid.append(button);
|
||||
}
|
||||
return { label: box.label, grid };
|
||||
});
|
||||
const overridesButton = el("button", {
|
||||
className: "z01-master__base z01-master__base--wide",
|
||||
attrs: { type: "button" },
|
||||
text: L("Z01_MasterData_Overrides"),
|
||||
});
|
||||
overridesButton.addEventListener("click", () => {
|
||||
activate(overridesButton);
|
||||
view.show({
|
||||
title: L("Z01_MasterData_Overrides"),
|
||||
key: "overrides",
|
||||
searchable: false,
|
||||
// 갈래 이름도 서버 것 — 목록 줄의 kind 를 표 이름으로 바꿔 보임.
|
||||
load: (query) => loadOverrides(query, new Map(kinds.map((k) => [k.kind, k.label]))),
|
||||
|
||||
// 공종 편집 — 구성 줄을 고치고 「입력 필요」 공종을 메움(PLAN 1-4).
|
||||
const workGrid = el("div", { className: "z01-master__base-grid" });
|
||||
for (const axis of AXES) {
|
||||
const button = el("button", {
|
||||
className: "z01-master__base",
|
||||
attrs: { type: "button" },
|
||||
text: L(axis === "forest" ? "Z01_WorkItem_Axis_Forest" : "Z01_WorkItem_Axis_Const"),
|
||||
});
|
||||
});
|
||||
boxes[0]?.grid.append(overridesButton);
|
||||
button.addEventListener("click", () => {
|
||||
activate(button);
|
||||
openWorkItems(axis);
|
||||
});
|
||||
workGrid.append(button);
|
||||
}
|
||||
|
||||
const tree = el("nav", { className: "z01-master__tree" });
|
||||
for (const group of groups) {
|
||||
@@ -132,54 +132,13 @@ export function buildSidePanel(
|
||||
|
||||
panel.append(
|
||||
...boxes.map((box) => section(box.label, box.grid)),
|
||||
section(L("Z01_WorkItem_Title"), workGrid),
|
||||
section(L("Z01_MasterData_Tree"), tree),
|
||||
);
|
||||
attachCollapsible(panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
const STATE_LABEL: Record<OverrideState, Parameters<typeof L>[0]> = {
|
||||
meaning_changed: "Z01_MasterData_List_MeaningChanged",
|
||||
source_changed: "Z01_MasterData_List_SourceChanged",
|
||||
orphan: "Z01_MasterData_List_Orphan",
|
||||
edited: "Z01_MasterData_List_Edited",
|
||||
};
|
||||
|
||||
/**
|
||||
* 고친 것 목록을 표 한 벌로 — 정렬(원본 바뀜 → 주인 없음 → 고쳐짐)과 쪽 나눔은 서버가 함.
|
||||
* 화면이 다시 줄 세우면 맨 위가 그 쪽 안에서만 맨 위가 됨(2026-09-15 브레인 ②). 서버에 검색이 없어 검색 칸은 숨김.
|
||||
*/
|
||||
async function loadOverrides(
|
||||
query: RowsQuery,
|
||||
kindLabels: Map<string, string>,
|
||||
): Promise<MasterRows> {
|
||||
const { items, total } = await fetchOverrides(query);
|
||||
const column = (key: string, label: Parameters<typeof L>[0]) => ({ key, label: L(label) });
|
||||
return {
|
||||
columns: [
|
||||
column("state", "Z01_MasterData_List_State"),
|
||||
column("kind", "Z01_MasterData_List_Kind"),
|
||||
column("row_label", "Z01_MasterData_List_Row"),
|
||||
column("column_label", "Z01_MasterData_List_Column"),
|
||||
column("value", "Z01_MasterData_List_Value"),
|
||||
column("original", "Z01_MasterData_List_Original"),
|
||||
column("current", "Z01_MasterData_List_Current"),
|
||||
],
|
||||
rows: items.map((item) => ({
|
||||
...item,
|
||||
value: valueWithUnit(item.value, item.unit),
|
||||
original: valueWithUnit(item.original, item.unit),
|
||||
...(Object.hasOwn(item, "current")
|
||||
? { current: valueWithUnit(item.current, item.unit) }
|
||||
: {}),
|
||||
// state 가 안 오면 때우지 않고 드러냄(서버 버그).
|
||||
state: L(STATE_LABEL[item.state] ?? "Z01_MasterData_State_Missing"),
|
||||
kind: kindLabels.get(item.kind) ?? item.kind,
|
||||
})),
|
||||
total,
|
||||
};
|
||||
}
|
||||
|
||||
function section(title: string, body: HTMLElement): HTMLElement {
|
||||
return el("section", {
|
||||
className: "z01-master__section ui-collapsible ui-sidebar-section",
|
||||
|
||||
Reference in New Issue
Block a user