fix(M01): 자리 표시 든 요소 줄 모달 — 요소조각의 푼 키로 단가 · 날글 안 찍음 (PLAN 5장 다시 돌림 3)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
@@ -101,6 +101,9 @@ interface Entry {
|
||||
/** 서버 조각으로 그린 단가 자리 · 수량 풀이 — 줄 모달이 씀(붙기 전엔 없음) */
|
||||
unitFrag?: Fragment[];
|
||||
piecesFrag?: Fragment[];
|
||||
/** 서버 요소조각의 첫 요소 — 자리 표시 든 줄이 입력으로 풀린 키 · 글 */
|
||||
unitRef?: string;
|
||||
unitText?: string;
|
||||
}
|
||||
|
||||
/** 재료 고르기 줄 — `요소` 가 글 대신 {구분·상세구분·규격·대표} 객체 */
|
||||
@@ -236,6 +239,8 @@ function entries(ctx: DetailContext): Entry[] {
|
||||
open: (self) =>
|
||||
openMaterialModal({
|
||||
unitFrag: self.unitFrag,
|
||||
unitRef: self.unitRef,
|
||||
unitText: self.unitText,
|
||||
qtyFrag: self.piecesFrag,
|
||||
title: item.이름 ?? (cond ? condLabel(cond) : item.요소),
|
||||
expr: item.수량,
|
||||
@@ -304,7 +309,12 @@ function attachPieces(all: Entry[], ctx: DetailContext): void {
|
||||
const line = [own, ...costs].map((c) => found.get(`${c}|${e.idx}`)).find(Boolean);
|
||||
const pieces = line?.조각;
|
||||
e.qtyFrag = e.piecesFrag = pieces?.length ? toFragments(pieces, ctx) : [];
|
||||
if (line?.요소조각?.length) e.unitFrag = toFragments(line.요소조각, ctx);
|
||||
if (line?.요소조각?.length) {
|
||||
e.unitFrag = toFragments(line.요소조각, ctx);
|
||||
const first = line.요소조각.find((piece) => piece.kind === "요소");
|
||||
e.unitRef = first?.참조?.키;
|
||||
e.unitText = first?.글;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ export interface ModalTarget {
|
||||
values: Record<string, string>;
|
||||
/** 서버 `요소조각` — 단가 자리(요소 · 로직 부르기)를 조각으로 · 없으면 로직 키만 알약으로 */
|
||||
unitFrag?: Fragment[];
|
||||
/** 서버가 입력으로 푼 단가 요소 키 · 글 — 자리 표시(`{…}`) 든 줄이 씀 */
|
||||
unitRef?: string;
|
||||
unitText?: string;
|
||||
/** 서버 수량 조각 — 한 줄 풀이를 조각으로(알약 포함) · 없으면 식 풀이 글 */
|
||||
qtyFrag?: Fragment[];
|
||||
}
|
||||
@@ -333,10 +336,31 @@ export function openMaterialModal(target: ModalTarget): void {
|
||||
rate.append(muted("…")); // 후보 목록이 오면 그 줄로 채움 — 100건 밖이면 키로 따로 찾아 옴
|
||||
} else if (cond) {
|
||||
rate.append(muted(tl("Pill_NoPick")));
|
||||
} else if (element.includes("{")) {
|
||||
// 자리 표시 든 요소 — 날글 대신 서버가 푼 키로 단가 · 못 풀면 입력 이름만
|
||||
const ref = target.unitRef;
|
||||
if (!ref) {
|
||||
const names = [...element.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]).join(" · ");
|
||||
rate.append(muted(`${target.unitText || names} — ${tl("Pill_Unresolved")}`));
|
||||
} else {
|
||||
const show = (brief: ElementBrief | null): void => {
|
||||
rate.replaceChildren(
|
||||
brief
|
||||
? priceView(ref, brief)
|
||||
: muted(`${target.unitText ?? ref} — ${tl("Pill_NoValue")}`),
|
||||
);
|
||||
setSum(brief);
|
||||
};
|
||||
rate.append(muted("…"));
|
||||
const cached = target.prices[ref];
|
||||
if (cached) show(cached);
|
||||
else
|
||||
void searchElements(groupOf(ref), ref)
|
||||
.then((got) => show(got.items.find((it) => it.ref === ref) ?? null))
|
||||
.catch(() => show(null));
|
||||
}
|
||||
} else if (element) {
|
||||
rate.append(
|
||||
muted(`${element} — ${tl(element.includes("{") ? "Pill_NoPick" : "Pill_NoValue")}`),
|
||||
);
|
||||
rate.append(muted(`${element} — ${tl("Pill_NoValue")}`));
|
||||
}
|
||||
body.replaceChildren(
|
||||
rate,
|
||||
|
||||
@@ -59,6 +59,7 @@ const TEXT = {
|
||||
"고르기 조건에 맞는 마스터 줄이 없음 — 단가를 못 찾음",
|
||||
"No master row matches the pick conditions",
|
||||
],
|
||||
Pill_Unresolved: ["입력이 비어 풀리지 않음", "Not resolved — input empty"],
|
||||
Pill_NoValue: ["관리자 값 미확보 — 마스터에 단가가 없음", "No price in master yet"],
|
||||
Modal_Qty: ["수량 식", "Quantity formula"],
|
||||
Modal_Price: ["단가 자료", "Price source"],
|
||||
|
||||
Reference in New Issue
Block a user