Files
Aislo/Z01_MasterData/Z01_MasterData_UI_Page.ts
T
eomsangdonandClaude Opus 5 feab848e69 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
2026-09-18 23:55:45 +09:00

80 lines
3.2 KiB
TypeScript

/* =============================================================================
* Z01_MasterData_UI_Page.ts
* 마스터 데이터 — B03~B09 와 같은 틀: 좌측 도킹 사이드바(기초단가 · 마스터 트리) / 우측 표
*
* 시스템 관리자만(서버도 require_system_admin 으로 다시 막음) · 워크플로 밖 화면이라 진행단계
* 패널·프로젝트 이름은 안 세움 (2026-09-15 브레인 Z01 — 좌측 패널 양식 갈음 · 기초단가 칸).
* ========================================================================== */
import "@ui/ui_template_workflow_layout.css";
import { ROUTES } from "@config/config_frontend";
import { el, hideLoadingOverlay, showLoadingOverlay, showToast } from "@ui/ui_template_elements";
import { t as L } from "@ui/ui_template_locale";
import { createWorkflowOverlays } from "@ui/ui_template_overlay";
import { navigateTo } from "../A00_Common/router";
import { fetchSessionUser } from "../A06_Login/A06_Login_Api_Fetch";
import { fetchBaseKinds, fetchMasterTree, type MasterGroup } from "./Z01_MasterData_Api_Fetch";
import type { ServedKind } from "./Z01_MasterData_UI_Cells";
import { buildRowsView, type RowsView } from "./Z01_MasterData_UI_Rows";
import { buildSidePanel } from "./Z01_MasterData_UI_Side";
import { buildWorkItemView } from "./Z01_MasterData_UI_WorkItem";
import "./Z01_MasterData_UI_Style.css";
export async function renderZ01MasterData(root: HTMLElement): Promise<void> {
const user = await fetchSessionUser().catch(() => null);
if (user?.role !== "SYSTEM_ADMIN") {
showToast(L("Z01_MasterData_AdminOnly"), "error");
navigateTo(ROUTES.B01_ACCOUNT);
return;
}
showLoadingOverlay();
let groups: MasterGroup[] = [];
let kinds: ServedKind[] = [];
try {
[groups, kinds] = await Promise.all([fetchMasterTree(), fetchBaseKinds()]);
} catch (error) {
showToast(error instanceof Error ? error.message : L("Z01_MasterData_LoadFailed"), "error");
} finally {
hideLoadingOverlay();
}
root.innerHTML = "";
root.append(buildPage(groups, kinds));
}
function buildPage(groups: MasterGroup[], kinds: ServedKind[]): HTMLElement {
// 오른쪽은 표 보기 · 공종 편집 둘 중 하나 — 왼쪽에서 누른 쪽만 보임.
const rows = buildRowsView();
const items = buildWorkItemView();
items.root.hidden = true;
const swap = (showRows: boolean): void => {
rows.root.hidden = !showRows;
items.root.hidden = showRows;
};
const view: RowsView = {
root: rows.root,
show: (source) => {
swap(true);
rows.show(source);
},
};
const layout = el("div", { className: "ui-workflow-layout z01-master" });
const main = el("main", {
className: "ui-workflow-layout__main",
children: [rows.root, items.root],
});
const overlays = createWorkflowOverlays({
title: L("Z01_MasterData_Title"),
optionsContent: buildSidePanel(groups, kinds, view, (axis) => {
swap(false);
items.show(axis);
}),
showProjectName: false,
onOptionsOpenChange: (isOpen) => layout.classList.toggle("is-options-open", isOpen),
});
layout.append(
el("div", { className: "ui-workflow-layout__body", children: [main] }),
overlays.root,
);
return layout;
}