fix(M02): 가져오기 목록 — 그 부분 문서가 없는 줄은 흐리게 · 누르면 까닭 한 줄 · 확인 창 · 복사 안 감
39-8 H3 — 옮기기 전 층(일위대가 labor 없음 등)에서 고르면 「없는 양식」 으로 실패하던 것 · missingPart(도면 · 면적산출 · 일위대가 · 항목 통째는 막지 않음). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
* 찾기 = 보이는 칸 글 전부에서 띄어쓰기 AND(나무 찾기와 같은 `matchText`).
|
||||
* ========================================================================== */
|
||||
|
||||
import { createInputField, el } from "@ui/ui_template_elements";
|
||||
import { createInputField, el, showToast } from "@ui/ui_template_elements";
|
||||
import { buildPager } from "../M01_MasterData/M01_MasterData_UI_Cells";
|
||||
import { buildHeadFilters } from "../M01_MasterData/M01_MasterData_UI_Head";
|
||||
import "../M01_MasterData/M01_MasterData_UI_Style.css";
|
||||
@@ -89,6 +89,16 @@ export const ITEM_COLS: Record<ListKind, Col<ItemRow>[]> = {
|
||||
],
|
||||
};
|
||||
|
||||
const PART_NAME = { structure: "도면", area: "면적산출", labor: "일위대가" } as const;
|
||||
|
||||
/** 그 부분 문서가 없는 줄의 까닭(항목 하나 복사 · 39-8 H3) — 옮기기 전 층의 일위대가처럼 파일이 없으면
|
||||
* 가져올 것이 없음 · 항목 통째(복사 생성)는 없는 문서를 빈 문서로 세워 막지 않음 · 있으면 null */
|
||||
export function missingPart(kind: ListKind, row: ItemRow): string | null {
|
||||
if (kind === "item") return null;
|
||||
const has = kind === "labor" ? !!row.labor : !!row.수정일?.[kind];
|
||||
return has ? null : `이 항목에는 ${PART_NAME[kind]} 문서가 없음`;
|
||||
}
|
||||
|
||||
/** 찾기 · 쪽 — 보일 줄 · 맞춘 쪽 · 맞는 줄 수(화면 밖 · 시험이 부름) */
|
||||
export function pageOf<R>(
|
||||
rows: R[],
|
||||
@@ -131,7 +141,12 @@ export function buildAllItems(
|
||||
export interface ItemListHandle {
|
||||
root: HTMLElement;
|
||||
/** 줄 · 칸 · 누를 일을 바꿔 그림(찾는 글은 그대로 · 쪽은 1) */
|
||||
show: <R>(rows: R[], cols: Col<R>[], onPick: (row: R) => void) => void;
|
||||
show: <R>(
|
||||
rows: R[],
|
||||
cols: Col<R>[],
|
||||
onPick: (row: R) => void,
|
||||
blocked?: (row: R) => string | null,
|
||||
) => void;
|
||||
/** 표 자리에 글 한 줄(불러오는 중 · 못 받음) */
|
||||
message: (text?: string) => void;
|
||||
}
|
||||
@@ -147,7 +162,12 @@ export function buildItemList(): ItemListHandle {
|
||||
grid.replaceChildren(el("p", { className: "m01-master__empty", text }));
|
||||
};
|
||||
|
||||
const show = <R>(rows: R[], cols: Col<R>[], onPick: (row: R) => void): void => {
|
||||
const show = <R>(
|
||||
rows: R[],
|
||||
cols: Col<R>[],
|
||||
onPick: (row: R) => void,
|
||||
blocked?: (row: R) => string | null,
|
||||
): void => {
|
||||
page = 1;
|
||||
draw = (): void => {
|
||||
const got = pageOf(rows, cols, search.value, page);
|
||||
@@ -167,7 +187,13 @@ export function buildItemList(): ItemListHandle {
|
||||
return el("td", { text, attrs: { title: text } });
|
||||
}),
|
||||
});
|
||||
tr.addEventListener("click", () => onPick(row));
|
||||
// 고를 수 없는 줄 — 흐리게 · 누르면 까닭 한 줄(고르기 안 함)
|
||||
const why = blocked?.(row) ?? null;
|
||||
if (why) {
|
||||
tr.classList.add("is-blocked");
|
||||
tr.title = why;
|
||||
}
|
||||
tr.addEventListener("click", () => (why ? showToast(why, "info") : onPick(row)));
|
||||
return tr;
|
||||
});
|
||||
grid.replaceChildren(
|
||||
|
||||
@@ -33,3 +33,9 @@
|
||||
flex-wrap: wrap;
|
||||
gap: var(--spacing-8);
|
||||
}
|
||||
|
||||
/* 그 부분 문서가 없는 줄(39-8 H3) — 흐리게 · 누르면 까닭만 */
|
||||
.m02-itemlist .m01-master__table-row.is-blocked {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
buildItemList,
|
||||
ITEM_COLS,
|
||||
loadItems,
|
||||
missingPart,
|
||||
type Col,
|
||||
type ItemRow,
|
||||
type ListKind,
|
||||
@@ -141,6 +142,7 @@ export function buildPicker(opt: PickerOptions): { root: HTMLElement } {
|
||||
rows.filter((row) => !opt.skip?.(from, row)),
|
||||
ITEM_COLS[opt.kind],
|
||||
(row) => opt.onPick({ from, row }),
|
||||
(row) => missingPart(opt.kind, row), // 그 부분 문서 없는 줄 = 흐림 · 까닭(39-8 H3)
|
||||
);
|
||||
} catch (error) {
|
||||
if (mine === seq) list.message(TEXT.failed.replace("{value}", why(error)));
|
||||
|
||||
@@ -61,5 +61,9 @@ process.stdout.write(
|
||||
),
|
||||
pageOver: m.pageOf(many, m.ITEM_COLS.structure, "", 9).page,
|
||||
pageEmpty: m.pageOf([], m.ITEM_COLS.structure, "", 2).page,
|
||||
// 그 부분 문서 없는 줄(39-8 H3) — c1 = 일위대가 있음 · 면적 수정일 없음 · c3 = 수정일 · 일위대가 없음
|
||||
missing: ["structure", "area", "labor", "item"].map((k) =>
|
||||
rows.map((r) => m.missingPart(k, r)),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -67,6 +67,15 @@ def test_찾기_쪽(ts):
|
||||
assert ts["pageOver"] == 3 and ts["pageEmpty"] == 1
|
||||
|
||||
|
||||
def test_부분_문서_없는_줄_까닭(ts):
|
||||
"""39-8 H3 — 그 부분 문서가 없으면 고르기 막음(흐림 · 까닭) · 항목 통째는 막지 않음."""
|
||||
structure, area, labor, item = ts["missing"]
|
||||
assert structure == [None, None, "이 항목에는 도면 문서가 없음"]
|
||||
assert area == ["이 항목에는 면적산출 문서가 없음", None, "이 항목에는 면적산출 문서가 없음"]
|
||||
assert labor == [None, None, "이 항목에는 일위대가 문서가 없음"]
|
||||
assert item == [None, None, None]
|
||||
|
||||
|
||||
def test_서버_목록_칸_수정일_labor(world):
|
||||
t = Tree(_as(world), "personal")
|
||||
t.do(op="add-item", 경로=["배수"], 이름="측구", 단위="m")
|
||||
|
||||
Reference in New Issue
Block a user