- 새 M02_Mat_Place: 가져온 이름 · 규격 → (뺀 이름, 뺀 규격, 지역, 상하차)(material_merge 판독 한 벌) · 고르는 차례 rank - 고르는 차례 = 상하차 맞음(글 없으면 -) › 값 있는 줄 › 줄 지역 › 전국 · 지역 모름 › 서울 › 경기 › 그 밖(값 가운데에 가까운 줄) - Elem_Alias(_same · one · region_of) · Hopyo_Match(목록에 지역 · 상하차 · 값 · 이름 같음 = 뺀 이름 + 지역 · 상하차 같음) · Hopyo_Material(_places · 변형 지역 = 지역 칸 · _prefer) · MatSame._pick · MatFind 후보 글 = 지역 칸 - 다시 고르기 · 찾기 창 · 줄 풍선말 후보 글에 지역 · 상하차(M01 placeNote 한 벌) - 시험: 814_1 · m02_hopyo_match 기대를 지역 칸 꼴로 · 새 지킴 test_830_10_material_region 21 - 불러오기 지킴 812_1 · 814_1 · 815_1 · 817_1 · 827_2 · 827_3 · 828_1 · 828_2 · 778_5 ×2 · 793_2 ×2 · 797_1 ×2 · m02_hopyo_match 통과 · typecheck 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
148 lines
6.5 KiB
TypeScript
148 lines
6.5 KiB
TypeScript
/* =============================================================================
|
|
* line_stack_material.ts
|
|
* 줄 쌓기 가져온 재료 줄 판정 표지 · 같은 파일 물음(PLAN 827-3 · 요구 811-2 · 812-1)
|
|
* 표지 = 문서 머리 `가져온.재료`(서버 `M02_San_MatPick.entry`) — 아직 못 이은 줄 「규격 없음 — 고르기」 ·
|
|
* 「후보 여럿」 · 「우리 표에 없음」 / 그 파일 고름 줄 「파일 고름」 / 별도계상 품목 · 풍선말 = 판정 · 방법 · 단서 · 후보
|
|
* 물음 = 이름 단추로 고른 뒤 한 번 — 「같은 파일의 같은 재료 n 줄에도」 예 = 개인 층 그 파일 고름 +
|
|
* 그 파일 다른 단가산출의 가져온 그대로인 줄(서버 `…/material-picks/{지문}`) · 이 줄 = 화면 [저장] · 별칭 아님
|
|
* ========================================================================== */
|
|
|
|
import { createBadge } from "@ui/ui_template_badge";
|
|
import { showToast } from "@ui/ui_template_elements";
|
|
import { askConfirm } from "@ui/ui_template_modal";
|
|
import type { FormulaDoc } from "../common_util/common_util_formula_doc";
|
|
import type { Picked } from "../common_util/common_util_line_stack_pick";
|
|
import { call, type Layer } from "../M02_MasterTemplete/M02_MasterTemplete_Api_Fetch";
|
|
import { at } from "../M02_MasterTemplete/M02_MasterTemplete_Hopyo_Api";
|
|
import { placeNote } from "../M01_MasterData/M01_MasterData_Api_Fetch";
|
|
import type { MachineCtx } from "./line_stack_machine";
|
|
|
|
interface Cand {
|
|
키: string;
|
|
이름?: string;
|
|
규격?: string;
|
|
단위?: string;
|
|
지역?: string;
|
|
상하차?: string;
|
|
}
|
|
|
|
export interface MatEntry {
|
|
기호: string;
|
|
종: string;
|
|
원래: { 이름: string; 규격: string; 단위: string };
|
|
판정: string;
|
|
방법?: string;
|
|
키?: string | null;
|
|
후보?: Cand[];
|
|
단서?: string[];
|
|
별도계상?: boolean;
|
|
고름?: Cand;
|
|
}
|
|
|
|
interface Head {
|
|
재료?: MatEntry[];
|
|
지문?: string;
|
|
파일?: string;
|
|
}
|
|
|
|
const LINKED = "이어짐";
|
|
/** 같은 파일 고름을 저장했음(문서 이벤트) — 불러오기 판이 다시 읽음 */
|
|
export const MAT_PICKED = "aislo:mat-picked";
|
|
const enc = encodeURIComponent;
|
|
const headOf = (d: FormulaDoc): Head | undefined => (d as { 가져온?: Head }).가져온;
|
|
const text = (c: { 이름?: string; 규격?: string; 지역?: string; 상하차?: string }): string =>
|
|
[`${c.이름 ?? ""} ${c.규격 ?? ""}`.trim(), placeNote(c)].filter(Boolean).join(" · ");
|
|
|
|
/** 그 단가 기호 줄의 재료 판정 칸 */
|
|
export const matEntry = (d: FormulaDoc, sym: string | undefined): MatEntry | undefined =>
|
|
sym ? headOf(d)?.재료?.find((x) => x.기호 === sym) : undefined;
|
|
|
|
/** 풍선말 글 — 판정 · 방법 · 단서 · 후보(앞 다섯) */
|
|
export function matTip(e: MatEntry): string {
|
|
const cands = (e.후보 ?? []).slice(0, 5).map((c, i) => `${i ? "" : "1 순위 "}${text(c)}`);
|
|
return [
|
|
`가져온 「${text(e.원래)}」 — ${e.판정}${e.방법 ? ` (${e.방법})` : ""}`,
|
|
...(e.고름 ? [`파일 고름: ${text(e.고름)}`] : []),
|
|
...(e.단서 ?? []).map((x) => `단서: ${x}`),
|
|
...(cands.length ? [`후보: ${cands.join(" · ")}`] : []),
|
|
].join("\n");
|
|
}
|
|
|
|
/** 줄 표지 — 못 이은 줄 판정 · 파일 고름 · 별도계상 품목(이어진 줄 = 없음) */
|
|
export function materialMark(
|
|
e: MatEntry | undefined,
|
|
link: { 갈래?: string; 키?: string },
|
|
): HTMLElement | null {
|
|
if (!e) return null;
|
|
const filePick = !!e.고름 && link.키 === e.고름.키;
|
|
const label = e.별도계상
|
|
? `별도계상 · ${e.고름 ? `품목 ${text(e.고름)}` : e.키 ? "품목 이음" : e.판정}`
|
|
: filePick
|
|
? "파일 고름"
|
|
: e.판정 !== LINKED && link.갈래 === "숫자"
|
|
? e.판정
|
|
: "";
|
|
if (!label) return null;
|
|
const done = filePick || (e.별도계상 && !!(e.고름 || e.키));
|
|
const badge = createBadge(label, done ? "info" : e.판정 === "우리 표에 없음" ? "neutral" : "warning", { size: 18 }); // prettier-ignore
|
|
badge.title = matTip(e);
|
|
badge.dataset.matMark = e.판정;
|
|
return badge;
|
|
}
|
|
|
|
/** 같은 파일 물음 몸 — 층 · 이 SK · 파일 지문 · 종 · 가져온 글 · 고른 것 */
|
|
export interface SameFile {
|
|
scope: { layer: Layer; owner: string | null };
|
|
me: string;
|
|
지문: string;
|
|
파일: string;
|
|
종: string;
|
|
원래: string;
|
|
p: Picked;
|
|
}
|
|
|
|
/** 이름 단추로 고른 뒤 — 같은 파일 같은 재료에도(물음 하나 · 예일 때만 서버) */
|
|
export async function askFileWide(
|
|
ctx: MachineCtx | undefined,
|
|
d: FormulaDoc,
|
|
sym: string | undefined,
|
|
p: Picked,
|
|
): Promise<void> {
|
|
const e = matEntry(d, sym);
|
|
const head = headOf(d);
|
|
if (!e || e.별도계상 || !head?.지문 || !ctx?.scope || !ctx.me || p.키 === e.키) return;
|
|
await askSameFile({ scope: ctx.scope, me: ctx.me, 지문: head.지문, 파일: head.파일 ?? "", 종: e.종, 원래: text(e.원래), p }); // prettier-ignore
|
|
}
|
|
|
|
/** 같은 파일 같은 재료 물음 — 예 = 개인 층 그 파일 고름 + 그 파일 다른 SK(가져온 그대로인 줄) · 이 SK = 화면 [저장] */
|
|
export async function askSameFile(a: SameFile): Promise<void> {
|
|
const path = `/material-picks/${enc(a.지문)}`;
|
|
const got = await call<{ 수: { 블록: number; 줄: number } | null; 만든: string[] }>(
|
|
at(a.scope, `${path}?종=${enc(a.종)}&me=${enc(a.me)}`),
|
|
).catch(() => null);
|
|
if (!got) return;
|
|
const many = got.수 ? `${got.수.줄} 줄(${got.수.블록} 블록)` : "줄";
|
|
const made = got.만든.length ? ` · 이미 만든 단가산출 ${got.만든.length} 건의 가져온 그대로인 줄도 바뀜` : ""; // prettier-ignore
|
|
const ok = await askConfirm({
|
|
message: `같은 파일(${a.파일})의 같은 재료 「${a.원래}」 ${many}에도 「${text(a.p)}」 적용할까? — 다음에 그 파일을 올리면 고른 것이 보임${made} · 별칭 아님`,
|
|
okLabel: "같은 파일에 적용",
|
|
cancelLabel: "이 줄만",
|
|
});
|
|
if (!ok) return;
|
|
try {
|
|
const out = await call<{ 고침: string[] }>(at(a.scope, path), {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
종: a.종,
|
|
고름: { 키: a.p.키, 이름: a.p.이름, 규격: a.p.규격 ?? "", 단위: a.p.단위 ?? "" },
|
|
파일: a.파일,
|
|
빼기: [a.me],
|
|
}),
|
|
});
|
|
document.dispatchEvent(new CustomEvent(MAT_PICKED)); // 불러오기 「고를 재료」 판 다시 읽기
|
|
showToast(`같은 파일 고름 저장 · 단가산출 ${out.고침.length} 건 고침 — 이 줄은 [저장] 때`, "success"); // prettier-ignore
|
|
} catch (err) {
|
|
showToast(err instanceof Error ? err.message : String(err), "error");
|
|
}
|
|
}
|