fix(m01-easycalc): 고르기 고침 넷 — 다른 일위대가 받는 값 넘김 · 다시 고르기 수량 미리 채움 · 재료 찾기 이름 순서

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ACSGaHdLgnkEkoA4LDtMU
This commit is contained in:
2026-10-01 10:25:29 +09:00
co-authored by Claude Sonnet 5.5
parent cddd343c87
commit 5954b27761
2 changed files with 40 additions and 11 deletions
@@ -173,13 +173,14 @@ async function repick(ctx: Ctx, card: EasyCard): Promise<void> {
if (picking || card.kind === "locked" || card.kind === "percent") return;
picking = true;
try {
const pick = await openEasyPick(card.kind);
const pick = await openEasyPick(card.kind, { qty: card.qty });
if (!pick) return;
const next = updateCard(ctx.doc, card.id, {
name: pick.name,
spec: pick.spec,
unit: pick.unit,
element: pick.element,
qty: pick.qty,
});
ctx.on.change(withInputs(next, pick.needInputs ?? []));
} finally {
+38 -10
View File
@@ -15,7 +15,7 @@ import {
} from "@ui/ui_template_elements";
import { openModal } from "@ui/ui_template_modal";
import { fetchPick, fetchSubs, type SubInfo } from "../M01_MasterData_Api_Fetch";
import { fetchMaterials, type ElementBrief } from "../M01_MasterData_UI_Logic_Api";
import { fetchLogic, fetchMaterials, type ElementBrief } from "../M01_MasterData_UI_Logic_Api";
import { listEasy, listMachines, machineElement, plainError } from "./M01_EasyCalc_Api";
import type { PickResult } from "./M01_EasyCalc_Model";
import "./M01_EasyCalc_Pick.css";
@@ -98,6 +98,23 @@ interface Source {
load: (f: Filter) => Promise<{ total: number; items: Candidate[] } | null>;
}
/** 찾는 말과 이름이 똑같으면 0 · 그 말로 시작하고 말이 거기서 끝나면(「모래, 서울」 「모래(건조)」) 1 ·
* 더 이어지면(「모래적사장」) 2 · 나머지 3 — 앞에 놓음(같은 차례끼리 순서는 그대로) */
const byName = <T extends { 이름?: string }>(items: T[], q: string): T[] => {
const squash = (t: string): string => t.replace(/\s+/g, "").toLowerCase();
const w = squash(q);
const rank = (it: T): number => {
const n = squash(it.이름 ?? "");
if (n === w) return 0;
if (!n.startsWith(w)) return 3;
return /[가-힣a-z0-9]/.test(n.charAt(w.length)) ? 2 : 1;
};
return items
.map((it, i) => ({ it, i, r: rank(it) }))
.sort((a, b) => a.r - b.r || a.i - b.i)
.map((x) => x.it);
};
const num = (v: unknown): number | null => (typeof v === "number" && Number.isFinite(v) ? v : null);
const SOURCES: Partial<Record<Kind, Source>> = {
@@ -137,7 +154,7 @@ const SOURCES: Partial<Record<Kind, Source>> = {
const found = f.q
? await fetchPick("price", f.q).then((r) => ({
total: r.total,
items: r.items
items: byName(r.items, f.q)
.filter((it) => !f.sub || (it as Brief).구분 === f.sub)
.map((it) => ({ ...it, 구분: (it as Brief).구분 })),
}))
@@ -217,13 +234,20 @@ const SOURCES: Partial<Record<Kind, Source>> = {
price: null,
priceText: "",
tag: e.owner,
make: async () => ({
kind: "logic",
name: e.name,
spec: "",
unit,
element: `로직(${e.key})`,
}),
make: async () => {
// 그 일위대가가 받는 값은 같은 이름으로 넘기고 「설계 때 정하는 값」으로 돌려줌(기계와 같은 꼴)
const needInputs = (await fetchLogic(e.key)).logic.입력;
const quote = (n: string): string => (/^[\w가-힣]+$/.test(n) ? n : `'${n}'`);
const rest = needInputs.map((i) => `, ${quote(i.이름)}=${quote(i.이름)}`).join("");
return {
kind: "logic",
name: e.name,
spec: "",
unit,
element: `로직(${e.key}${rest})`,
needInputs,
};
},
};
}),
};
@@ -232,7 +256,10 @@ const SOURCES: Partial<Record<Kind, Source>> = {
};
/** 물음 셋 — 고른 것 · 취소는 null */
export function openEasyPick(kind?: Kind): Promise<PickResult | null> {
export function openEasyPick(
kind?: Kind,
opts: { qty?: number | null } = {},
): Promise<PickResult | null> {
return new Promise((resolve) => {
let settled = false;
const finish = (r: PickResult | null): void => {
@@ -339,6 +366,7 @@ export function openEasyPick(kind?: Kind): Promise<PickResult | null> {
placeholder: te("QtyPlace"),
});
qty.input.inputMode = "decimal";
if (opts.qty != null) qty.input.value = String(opts.qty); // 다시 고르기 — 지금 수량 미리 채움
const rough = el("p", { className: "m01-easypick__rough" });
const parse = (): number | null => {
const n = Number(qty.input.value.trim().replace(/,/g, ""));