feat(z01): 줄에 없는 열은 「—」 · columns 밖 열은 안 그림 · main.py 에 시스템 관리자 전용 보호 미리 둠(라우터 import·등록 두 줄은 모듈이 올라오면 주석 풂 — 없는 모듈 import 는 서버를 못 띄움)(브레인)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
2026-09-15 19:09:35 +09:00
co-authored by Claude Opus 5
parent 7150fd8289
commit 399b219371
4 changed files with 24 additions and 4 deletions
@@ -33,6 +33,13 @@ export function cellText(value: unknown, unit?: string | null): string {
return String(value);
}
/** 한 줄의 칸들 — columns 차례대로만(columns 에 없는 열은 안 그림) · 줄에 그 열이 없으면 「—」. */
export function rowCells(row: Record<string, unknown>, columns: MasterColumn[]): string[] {
return columns.map((column) =>
Object.hasOwn(row, column.key) ? cellText(row[column.key], column.unit) : "—",
);
}
export function pageCount(total: number, size: number): number {
return Math.max(1, Math.ceil(total / size));
}
+2 -3
View File
@@ -27,7 +27,7 @@ import {
type MasterRows,
type MasterTable,
} from "./Z01_MasterData_Api_Fetch";
import { cellText, columnTitle, pageCount, shownColumns } from "./Z01_MasterData_UI_Cells";
import { columnTitle, pageCount, rowCells, shownColumns } from "./Z01_MasterData_UI_Cells";
import "./Z01_MasterData_UI_Style.css";
const PAGE_SIZE = 50;
@@ -229,8 +229,7 @@ function buildRowsView(): RowsView {
const body = el("tbody");
for (const row of last.rows) {
const tr = el("tr");
for (const column of columns) {
const text = cellText(row[column.key], column.unit);
for (const text of rowCells(row, columns)) {
tr.append(el("td", { text, attrs: { title: text } }));
}
body.append(tr);
+7
View File
@@ -79,6 +79,7 @@ from common_util.common_util_audit import note_api_call, record_call_burst
from common_util.common_util_auth import (
require_company,
require_project_access,
require_system_admin,
verify_session,
)
@@ -654,6 +655,12 @@ app.include_router(b09_progress_router, dependencies=protected_with_company)
app.include_router(b09_edits_router, dependencies=protected_with_company)
app.include_router(b09_factors_router, dependencies=protected_with_company)
app.include_router(b09_material_prices_router, dependencies=protected_with_company)
# Z01 마스터 데이터 — 회사·프로젝트가 아니라 시스템 관리자만 본다(2026-09-15 브레인 Z01).
# ⚠ 라우터 모듈(랩탑 메인)이 아직 없음 — 없는 것을 import 하면 서버가 안 뜸.
# 올라오면 아래 두 줄 주석을 풂.
system_admin_only = [Depends(verify_session), Depends(require_system_admin)]
# from Z01_MasterData.Z01_MasterData_Router import router as z01_master_data_router
# app.include_router(z01_master_data_router, dependencies=system_admin_only)
# 개발 전용 잠금 해제 — 다른 라우터와 **같은 보호**를 받는다(로그인·회사·프로젝트 접근).
# 그 위에 서버가 환경까지 한 번 더 본다.
app.include_router(dev_unlock_router, dependencies=protected_with_company)
+8 -1
View File
@@ -4,6 +4,8 @@
② `hidden` 열(내부 id · sha · 생성시각)은 기본 숨김 · 「숨긴 열 보기」면 보임
③ 단위가 있는 열의 수는 천 단위 쉼표(소수 자리는 자르지 않음) · 단위 없는 수(연도 등)는 그대로
④ 쪽 수는 줄이 없어도 1
⑤ columns 차례가 열 차례 — rows 에 columns 에 없는 열이 섞여도 안 그림 · columns 에 있는데
줄에 그 열이 없으면 「—」(값이 null 이면 빈칸) (2026-09-15 브레인)
TS 를 실제로 돌린다(`test_b06_berm_review_info` 와 같은 방식).
"""
@@ -19,7 +21,9 @@ SOURCE = PROJECT_ROOT / "Z01_MasterData" / "Z01_MasterData_UI_Cells.ts"
_RUNNER = """
import { writeFileSync } from "node:fs";
import { cellText, columnTitle, pageCount, shownColumns } from "./Z01_MasterData_UI_Cells.js";
import {
cellText, columnTitle, pageCount, rowCells, shownColumns,
} from "./Z01_MasterData_UI_Cells.js";
const columns = [
{ key: "id", label: "id", unit: null, hidden: true },
@@ -40,6 +44,7 @@ writeFileSync(process.argv[2], JSON.stringify({
cellText("보통인부", null),
],
pages: [pageCount(0, 50), pageCount(6999, 50), pageCount(50, 50)],
row: rowCells({ extra: "x", daily_wage_krw: 250000, occupation_name: null }, columns),
}));
"""
@@ -80,3 +85,5 @@ def test_칸_글자와_숨김_열(tmp_path: Path) -> None:
assert got["titles"] == ["id", "직종명", "일 노임 (원)"]
assert got["cells"] == ["", "250,000", "1.23456", "2026", '{"a":1}', "", "보통인부"]
assert got["pages"] == [1, 140, 1]
# columns 차례 · id 는 줄에 없어 「—」 · null 은 빈칸 · extra 는 안 그림
assert got["row"] == ["", "", "250,000"]