wip(M01): 테스트 컨테이너 후보 값 다섯 표시 · 옛 줄 검색을 품셈재료 이름으로 · 멈춤 사유 쉬운 말 · 빈 입력 안내
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
} from "./M01_MasterData_UI_Logic_Api";
|
||||
import { formatNumber } from "./M01_MasterData_UI_Logic_Edit";
|
||||
import { hasPickable, pickPanel, swappedRow } from "./M01_MasterData_UI_Test_Pick";
|
||||
import { tt } from "./M01_MasterData_UI_Test_Text";
|
||||
import { plainReason, tt } from "./M01_MasterData_UI_Test_Text";
|
||||
|
||||
type Step =
|
||||
{ kind: "job" } | { kind: "ask"; spec: LogicInput } | { kind: "pick" } | { kind: "result" };
|
||||
@@ -107,7 +107,7 @@ function wizard(host: HTMLElement, one: LogicOne): void {
|
||||
} else {
|
||||
const box = el("input", {
|
||||
className: "m01-test__input",
|
||||
attrs: { type: "text", inputmode: "decimal" },
|
||||
attrs: { type: "text", inputmode: "decimal", placeholder: tt("Enter_Value") },
|
||||
});
|
||||
box.value = values[name] ?? "";
|
||||
box.addEventListener("input", () => changed(box.value));
|
||||
@@ -154,7 +154,7 @@ function resultView(answer: CalcAnswer): HTMLElement[] {
|
||||
className: "m01-test__reasons",
|
||||
children: [
|
||||
el("strong", { text: tt("Result_Stopped") }),
|
||||
el("div", { text: answer.reason }),
|
||||
el("div", { text: plainReason(answer.reason) }),
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
} from "./M01_MasterData_UI_Logic_Api";
|
||||
import { formatNumber } from "./M01_MasterData_UI_Logic_Edit";
|
||||
import { hasPickable, pickPanel, swappedRow, type Swaps } from "./M01_MasterData_UI_Test_Pick";
|
||||
import { tt } from "./M01_MasterData_UI_Test_Text";
|
||||
import { plainReason, tt } from "./M01_MasterData_UI_Test_Text";
|
||||
|
||||
/* ── 원문 표(소요량·계수) 미리보기 — /elements 로 파일을 찾고 /table 로 통째 읽음 ── */
|
||||
|
||||
@@ -298,7 +298,7 @@ function paint(host: HTMLElement, state: State): void {
|
||||
} else {
|
||||
const box = el("input", {
|
||||
className: "m01-logic__input",
|
||||
attrs: { type: "text", inputmode: "decimal" },
|
||||
attrs: { type: "text", inputmode: "decimal", placeholder: tt("Enter_Value") },
|
||||
});
|
||||
box.value = state.values[spec.이름] ?? "";
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
@@ -337,7 +337,10 @@ function paint(host: HTMLElement, state: State): void {
|
||||
main.append(
|
||||
el("div", {
|
||||
className: "m01-logic__reasons",
|
||||
children: [el("strong", { text: "멈춤" }), el("div", { text: state.answer.reason })],
|
||||
children: [
|
||||
el("strong", { text: "멈춤" }),
|
||||
el("div", { text: plainReason(state.answer.reason) }),
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
type FlowKind,
|
||||
} from "./M01_MasterData_UI_Test_C_Model";
|
||||
import { hasPickable, pickPanel, swappedRow, type Swaps } from "./M01_MasterData_UI_Test_Pick";
|
||||
import { tt } from "./M01_MasterData_UI_Test_Text";
|
||||
import { plainReason, tt } from "./M01_MasterData_UI_Test_Text";
|
||||
import "./M01_MasterData_UI_Test_C_Style.css";
|
||||
|
||||
const TEXT = {
|
||||
@@ -169,7 +169,7 @@ function mount(host: HTMLElement, one: LogicOne): void {
|
||||
}
|
||||
rest.replaceChildren(...nodes);
|
||||
const reason = answer && !answer.ok ? answer.reason : "";
|
||||
stopped.textContent = reason ? `${tc("Stopped")} — ${reason}` : "";
|
||||
stopped.textContent = reason ? `${tc("Stopped")} — ${plainReason(reason)}` : "";
|
||||
stopped.hidden = !reason;
|
||||
};
|
||||
|
||||
@@ -255,7 +255,7 @@ function inputColumn(
|
||||
className: "m01c__input",
|
||||
attrs: { type: "text", inputmode: "decimal" },
|
||||
});
|
||||
if (spec.범위) box.placeholder = `${spec.범위[0]} ∼ ${spec.범위[1]}`;
|
||||
box.placeholder = spec.범위 ? `${spec.범위[0]} ∼ ${spec.범위[1]}` : tt("Enter_Value");
|
||||
box.value = values[spec.이름] ?? "";
|
||||
box.addEventListener("input", () => {
|
||||
values[spec.이름] = box.value;
|
||||
|
||||
@@ -37,14 +37,24 @@ export function swappedRow(logic: LogicRow, swaps: Swaps): LogicRow | undefined
|
||||
};
|
||||
}
|
||||
|
||||
type Brief = ElementBrief & { 구분?: string; 상세구분?: string };
|
||||
type Brief = ElementBrief & {
|
||||
구분?: string;
|
||||
상세구분?: string;
|
||||
값들?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
const distinct = (items: Brief[], key: "구분" | "상세구분"): string[] => [
|
||||
...new Set(items.map((i) => i[key]).filter((v): v is string => !!v)),
|
||||
];
|
||||
|
||||
const label = (it: Brief): string =>
|
||||
[`${it.이름 ?? ""} ${it.규격 ?? ""}`.trim(), formatNumber(it.값)].filter(Boolean).join(" · ");
|
||||
/** 후보 글 — 값 열이 여럿이면 있는 열을 다 보임(계산은 그 가운데 낮은 값) */
|
||||
const label = (it: Brief): string => {
|
||||
const cols = Object.entries(it.값들 ?? {})
|
||||
.filter(([, v]) => v !== null && v !== undefined)
|
||||
.map(([k, v]) => `${k} ${formatNumber(v)}`);
|
||||
const prices = cols.length ? cols : [formatNumber(it.값) || tt("Mat_NoPrice")];
|
||||
return [`${it.이름 ?? ""} ${it.규격 ?? ""}`.trim(), ...prices].join(" · ");
|
||||
};
|
||||
|
||||
function pickRow(one: LogicOne, line: HoLine, i: number, swaps: Swaps, onChange: () => void) {
|
||||
const cond =
|
||||
@@ -109,7 +119,7 @@ function pickRow(one: LogicOne, line: HoLine, i: number, swaps: Swaps, onChange:
|
||||
region: "울진",
|
||||
})
|
||||
: line.종류 === "재료"
|
||||
? searchPrice((line.이름 ?? "").split(/[\s(·]/)[0]) // 옛 줄 — 이름 첫 낱말로 자재품목(구분 붙음)
|
||||
? searchPrice(one.prices[line.요소 as string]?.이름 ?? (line.이름 ?? "").split(/[\s(·]/)[0]) // 옛 줄(고르기 조건 없음) — 품셈재료 이름 · 없으면 이름 첫 낱말
|
||||
: searchElements(line.종류, line.이름 ?? "");
|
||||
void found
|
||||
.then((r) => {
|
||||
|
||||
@@ -29,14 +29,24 @@ const TEXT = {
|
||||
Ask_Range: ["범위 {min} ∼ {max}", "Range {min} ∼ {max}"],
|
||||
Mat_Title: ["쓰이는 재료·기계", "Materials and machines"],
|
||||
Mat_Note: [
|
||||
"기본은 원문 그대로 — 바꿔 보면 결과가 달라짐(저장 안 함)",
|
||||
"Default is the source as is — swapping only changes this trial",
|
||||
"기본은 원문 그대로 — 바꿔 보면 결과가 달라짐(저장 안 함) · 값이 여럿이면 그중 낮은 값으로 셈",
|
||||
"Default is the source as is — swapping only changes this trial; the lowest listed price is used",
|
||||
],
|
||||
Mat_None: ["이 공종은 재료·기계를 따로 고르지 않습니다", "No materials or machines to pick"],
|
||||
Mat_Default: ["기본 (원문 그대로)", "Default (as in source)"],
|
||||
Pick_All: ["(전체)", "(All)"],
|
||||
Use_Of: ["이 표의 용도", "Used for"],
|
||||
Mat_Price: ["단가", "Unit price"],
|
||||
Mat_NoPrice: ["단가 없음", "No price"],
|
||||
Stop_Missing: [
|
||||
"「{name}」 값을 아직 안 넣었습니다 — 값을 넣으면 계산됩니다",
|
||||
"「{name}」 is empty — enter it to calculate",
|
||||
],
|
||||
Stop_NoRow: [
|
||||
"넣은 값에 맞는 표 줄이 없습니다 — 값을 바꿔 보세요",
|
||||
"No table row fits these values — try other values",
|
||||
],
|
||||
Enter_Value: ["값을 넣어 주세요", "Enter a value"],
|
||||
Result_Title: ["결과", "Result"],
|
||||
Result_Stopped: ["계산이 멈춤", "Calculation stopped"],
|
||||
Col_Name: ["이름", "Name"],
|
||||
@@ -48,6 +58,14 @@ const TEXT = {
|
||||
|
||||
export type TestTextKey = keyof typeof TEXT;
|
||||
|
||||
/** 엔진이 멈춘 까닭을 설계자 말로 — 모르는 글은 그대로 */
|
||||
export function plainReason(reason: string): string {
|
||||
const missing = /입력 「([^」]+)」 없음/.exec(reason);
|
||||
if (missing) return tt("Stop_Missing", { name: missing[1] });
|
||||
if (/맞는 줄 0개/.test(reason)) return tt("Stop_NoRow");
|
||||
return reason;
|
||||
}
|
||||
|
||||
export function tt(key: TestTextKey, fill: Record<string, string | number> = {}): string {
|
||||
const entry = TEXT[key];
|
||||
const text: string = entry[currentLanguageIndex as 0 | 1] ?? entry[0];
|
||||
|
||||
Reference in New Issue
Block a user