feat(736-2): 빠진 길 둘 — 찾아 넣기 창 [신규 자재](개인 층) · 기계 탭 · 엑셀 연결 줄 갈래대로 탭
- 개인 층 재료 고름 = 창 머리 [신규 자재] — 만들어 바로 자재 목록에 넣음(`openNewMaterial`) - 찾아 넣기 창 기계(M01 EQ) 탭 — 「M01 전체」 묶음 안 · 부르는 쪽이 `machine` 을 줄 때만 보임 - 엑셀 연결 = 줄 갈래대로 탭(노임 = 인력 · 기계 = 기계 · 자재 = 재료 · 그 밖 = 기초) — 불러오기 고르기 · 남은 줄 · 못옮김 · 안 이은 줄 연결 모두 같은 창 · 따로 [기계] 단추 걷음 - 낡은 시험 고침(721 · 729-3: 기계 고름 길이 찾아 넣기 창으로 옮김) + 736-2 글 시험 추가 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,13 +12,16 @@
|
||||
* · (727-1) 기초 = 로직마다 한 줄(`logicHeads` · 옵션은 넣은 뒤 고름 · 옛 창도 같이) · 서버 목록 그대로
|
||||
* · (736-1) `kinds` = 탭 막기(자리마다 고를 수 있는 종류만 · 불러오기 엑셀 연결 · 별칭 장부) · `material` = 재료 고름을
|
||||
* 일위대가 「자재 목록」 자리로(`element` 대신) · `query` = 찾기 칸 미리 넣기 · 부르는 곳 = 일위대가 [항목 추가]
|
||||
* · (736-2) `machine` = M01 기계(EQ · 기계 줄 이음) 고름 · 개인 층에서 재료 고름이면 머리 [신규 자재](개인 층에 만들어 바로 고름) ·
|
||||
* 기계 줄은 「M01 전체」 탭 묶음 안(인력 · 재료와 같은 탭)
|
||||
* · (728-2) 돈 아닌 기초(결과단위가 「원」 아님 · ㎥/시간 등 21 개) = 목록에서 뺌
|
||||
* ========================================================================== */
|
||||
|
||||
import { createDataTable } from "@ui/ui_template_data_table";
|
||||
import { createInputField, el, showToast } from "@ui/ui_template_elements";
|
||||
import { createButton, createInputField, el, showToast } from "@ui/ui_template_elements";
|
||||
import { t as L } from "@ui/ui_template_locale";
|
||||
import { aliasNote, matchLogic } from "@ui/ui_template_logic_find";
|
||||
import { openNewMaterial } from "./M02_Material_Pick";
|
||||
import { openModal } from "@ui/ui_template_modal";
|
||||
import { createTabs } from "@ui/ui_template_tabs";
|
||||
import { createText } from "@ui/ui_template_text";
|
||||
@@ -40,7 +43,7 @@ type Tab = "m01" | "sk" | "pre";
|
||||
|
||||
/** M01 인력 · 재료 한 줄(`/logic-lib/pick` · 코드 = LB- · MT- 키) */
|
||||
export interface M01Element {
|
||||
종류: "인력" | "재료";
|
||||
종류: "인력" | "재료" | "기계";
|
||||
코드: string;
|
||||
이름: string;
|
||||
규격: string;
|
||||
@@ -48,8 +51,8 @@ export interface M01Element {
|
||||
값: number | null;
|
||||
}
|
||||
|
||||
/** 고를 수 있는 종류 — logic = M01 단가산출 · 기초 로직 · labor = 인력 · material = 재료 · sk = M02 단가산출 · pre = 일위대가 */
|
||||
export type FindKind = "logic" | "labor" | "material" | "sk" | "pre";
|
||||
/** 고를 수 있는 종류 — logic = M01 단가산출 · 기초 로직 · labor = 인력 · material = 재료 · machine = 기계 · sk = M02 단가산출 · pre = 일위대가 */
|
||||
export type FindKind = "logic" | "labor" | "material" | "machine" | "sk" | "pre";
|
||||
|
||||
export interface FindPickOptions {
|
||||
layer: Layer;
|
||||
@@ -67,6 +70,8 @@ export interface FindPickOptions {
|
||||
ref?: (ref: LaborRef, name: string) => void;
|
||||
/** M01 인력 · 재료 고름 — 없으면 그 줄 회색 막힘 */
|
||||
element?: (e: M01Element) => void;
|
||||
/** 736-2 M01 기계(EQ) 고름 — 있을 때만 기계 줄이 보임 */
|
||||
machine?: (e: M01Element) => void;
|
||||
}
|
||||
|
||||
/** 727-1 기초마다 한 줄(옛 창 `Labor_Pick` 도 씀) — 규격 칸 = 「옵션 N」 */
|
||||
@@ -131,7 +136,7 @@ export function openFindPicker(opt: FindPickOptions): void {
|
||||
const ok = (k: FindKind): boolean => !opt.kinds || opt.kinds.includes(k);
|
||||
const allowed = (
|
||||
[
|
||||
["m01", ok("logic") || ok("labor") || ok("material")],
|
||||
["m01", ok("logic") || ok("labor") || ok("material") || (!!opt.machine && ok("machine"))],
|
||||
["sk", ok("sk")],
|
||||
["pre", ok("pre")],
|
||||
] as const
|
||||
@@ -144,6 +149,7 @@ export function openFindPicker(opt: FindPickOptions): void {
|
||||
let std: StandardRow[] | null = null;
|
||||
let people: PickItem[] = [];
|
||||
let goods: PickItem[] = [];
|
||||
let machines: PickItem[] = [];
|
||||
const lists: Record<"sk" | "pre", SangeunRow[] | null> = { sk: null, pre: null };
|
||||
let shown: Line[] = [];
|
||||
let seq = 0;
|
||||
@@ -203,7 +209,12 @@ export function openFindPicker(opt: FindPickOptions): void {
|
||||
단위: unitOf(m.단위 ?? ""),
|
||||
값: Number.isFinite(Number(m.값)) && m.값 !== null ? Number(m.값) : null,
|
||||
};
|
||||
const put = kind === "재료" && opt.material ? opt.material : opt.element;
|
||||
const put =
|
||||
kind === "기계"
|
||||
? opt.machine
|
||||
: kind === "재료" && opt.material
|
||||
? opt.material
|
||||
: opt.element;
|
||||
return {
|
||||
품명: m.이름,
|
||||
규격: e.규격,
|
||||
@@ -233,6 +244,7 @@ export function openFindPicker(opt: FindPickOptions): void {
|
||||
...(ok("logic") ? logicLines(q) : []),
|
||||
...(ok("labor") ? elementLines("인력", people) : []),
|
||||
...(ok("material") ? elementLines("재료", goods) : []),
|
||||
...(opt.machine && ok("machine") ? elementLines("기계", machines) : []),
|
||||
];
|
||||
} else {
|
||||
const words = q.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
@@ -255,13 +267,18 @@ export function openFindPicker(opt: FindPickOptions): void {
|
||||
call<{ items: PickItem[] }>(
|
||||
`/logic-lib/pick?kind=${kind}&q=${encodeURIComponent(q)}&limit=200`,
|
||||
).then((d) => d.items);
|
||||
const getMachine = (): Promise<PickItem[]> =>
|
||||
call<{ items: PickItem[] }>(
|
||||
`/logic-lib/elements?group=${encodeURIComponent("기계")}&q=${encodeURIComponent(q)}&limit=200`,
|
||||
).then((d) => d.items.map((x) => ({ ...x, 단위: "시간", 값: null }))); // 기계 단위 · 값 = 칸별 표(시간당 경비는 서버 셈)
|
||||
try {
|
||||
const [p, g] = await Promise.all([
|
||||
const [p, g, m] = await Promise.all([
|
||||
ok("labor") ? get("job") : Promise.resolve([]),
|
||||
q && ok("material") ? get("price") : Promise.resolve([]),
|
||||
opt.machine && ok("machine") ? getMachine() : Promise.resolve([]),
|
||||
]);
|
||||
if (mine !== seq) return;
|
||||
[people, goods] = [p, g];
|
||||
[people, goods, machines] = [p, g, m];
|
||||
} catch (error) {
|
||||
if (mine !== seq) return;
|
||||
showToast(error instanceof Error ? error.message : tc("Load_Failed"), "error");
|
||||
@@ -289,8 +306,29 @@ export function openFindPicker(opt: FindPickOptions): void {
|
||||
},
|
||||
});
|
||||
|
||||
// 736-2 개인 층 + 재료 고름 = 머리 [신규 자재] — 만들어 바로 고름(우리 M01 에 없는 자재)
|
||||
const newMat = createButton({
|
||||
label: L("M02_FindNewMaterial"),
|
||||
variant: "ghost",
|
||||
onClick: () =>
|
||||
openNewMaterial(opt.owner, search.input.value.trim(), (row) =>
|
||||
take(() =>
|
||||
opt.material?.({
|
||||
종류: "재료",
|
||||
코드: row.키,
|
||||
이름: row.이름,
|
||||
규격: row.규격,
|
||||
단위: row.단위,
|
||||
값: row.값,
|
||||
}),
|
||||
)(),
|
||||
),
|
||||
});
|
||||
newMat.dataset.materialNew = "";
|
||||
const canNew = opt.layer === "personal" && !!opt.material && ok("material");
|
||||
({ close } = openModal({
|
||||
title: opt.title ?? L("M02_FindTitle"),
|
||||
...(canNew ? { actions: [newMat] } : {}),
|
||||
closeLabel: tc("Pick_Close"),
|
||||
dialogClass: "m01-logic__pick m01-combo",
|
||||
width: "960px", // 표 여섯 칸
|
||||
|
||||
@@ -170,6 +170,17 @@ export const candText = (c: HopyoTarget, twins: Set<string>): string =>
|
||||
export const canPickMaterial = (line: Pick<HopyoLine, "갈래" | "단가">): boolean =>
|
||||
MATERIAL.has(line.갈래) || !!line.단가?.재료비;
|
||||
|
||||
/** 736-2 [다른 항목 검색…] 탭 — 줄 갈래대로(서버 이음 칸 = 노임 LB · 기계 EQ · 자재 · 로직) */
|
||||
export const kindsOf = (line: Pick<HopyoLine, "갈래" | "단가">): FindKind[] =>
|
||||
line.갈래 === "노임"
|
||||
? ["labor"]
|
||||
: line.갈래 === "기계"
|
||||
? ["machine"]
|
||||
: [
|
||||
...(MATERIAL.has(line.갈래) ? [] : (["logic"] as const)),
|
||||
...(canPickMaterial(line) ? (["material"] as const) : []),
|
||||
];
|
||||
|
||||
/** 고른 후보 → 만들기 대상(배 1 · 몫 있으면 배 = 몫) */
|
||||
export const pickedTargets = (picked: { c: HopyoTarget; 몫: number | null }[]): BuildTarget[] =>
|
||||
picked.map(({ c, 몫 }) => ({
|
||||
@@ -294,11 +305,8 @@ export function openHopyoChoose(opt: ChooseOptions): void {
|
||||
: []),
|
||||
],
|
||||
});
|
||||
// 736-1 찾아 넣기 창 하나 — 이을 수 있는 종류만(자재 줄 = 재료 · 그 밖 = 기초 로직 · 재료비 있는 줄은 재료도)
|
||||
const kinds: FindKind[] = [
|
||||
...(material ? [] : (["logic"] as const)),
|
||||
...(canPickMaterial(line) ? (["material"] as const) : []),
|
||||
];
|
||||
// 736-1 · 736-2 찾아 넣기 창 하나 — 줄 갈래대로 탭(노임 = 인력 · 기계 = 기계 · 자재 = 재료 · 그 밖 = 기초 로직 · 재료비 있는 줄은 재료도)
|
||||
const kinds = kindsOf(line);
|
||||
const find = createButton({
|
||||
label: "다른 항목 검색…",
|
||||
size: "sm",
|
||||
@@ -308,9 +316,11 @@ export function openHopyoChoose(opt: ChooseOptions): void {
|
||||
layer: getActiveLayer(),
|
||||
owner: getActiveOwner(),
|
||||
kinds,
|
||||
query: searchHint(line),
|
||||
query: searchHint(line.갈래 === "기계" ? { 이름: line.규격 || line.이름 } : line), // 기계 = 규격 낱말(굴착기)
|
||||
logic: (x) => opt.done([{ 로직: x.키, 입력값: {}, 배: 1 }]),
|
||||
material: (e) => opt.done([{ 자재: e.코드, 배: 1 }]),
|
||||
element: (e) => opt.done([{ 노임: e.코드, 배: 1 }]),
|
||||
machine: (e) => opt.done([{ 기계: e.코드, 배: 1 }]),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,13 +13,11 @@
|
||||
|
||||
import { createBadge } from "@ui/ui_template_badge";
|
||||
import { createBanner } from "@ui/ui_template_banner";
|
||||
import { createFindList } from "@ui/ui_template_find_list";
|
||||
import { createButton, createSelectField, el, showToast } from "@ui/ui_template_elements";
|
||||
import { t as L } from "@ui/ui_template_locale";
|
||||
import { openModal } from "@ui/ui_template_modal";
|
||||
import { createTable, type Cell } from "@ui/ui_template_table";
|
||||
import { createText } from "@ui/ui_template_text";
|
||||
import { call } from "./M02_MasterTemplete_Api_Fetch";
|
||||
import {
|
||||
guessHopyo,
|
||||
leftHopyo,
|
||||
@@ -35,7 +33,7 @@ import {
|
||||
type LinkBase,
|
||||
} from "./M02_MasterTemplete_Hopyo_Api";
|
||||
import { bodyOf, draftKey, loadDraft, saveDraft, toTarget } from "./M02_MasterTemplete_Hopyo_Check";
|
||||
import { openHopyoChoose } from "./M02_MasterTemplete_Hopyo_Choose";
|
||||
import { kindsOf, openHopyoChoose } from "./M02_MasterTemplete_Hopyo_Choose";
|
||||
import { openFindPicker } from "./M02_MasterTemplete_Find_Pick";
|
||||
import { openLost } from "./M02_MasterTemplete_Hopyo_Lost";
|
||||
|
||||
@@ -118,49 +116,6 @@ function pickedText(at: LeftScope, no: number, 차례: number): string {
|
||||
.join(" + ");
|
||||
}
|
||||
|
||||
/** M01 기계(EQ) 하나 고르기 — 이름 · 규격 낱말 */
|
||||
export function pickMachine(
|
||||
line: { 이름: string; 규격?: string },
|
||||
done: (code: string, name: string) => void,
|
||||
): void {
|
||||
openModal({
|
||||
title: `${L("M02_LeftMachine")} — ${line.이름} ${line.규격 ?? ""}`.trim(),
|
||||
closeLabel: L("M02_Close"),
|
||||
dialogClass: "m02-modal",
|
||||
mount: (body, close) => {
|
||||
const find = createFindList({ placeholder: L("M02_LeftMachineFind") });
|
||||
let seq = 0;
|
||||
const run = (q: string): void => {
|
||||
const mine = ++seq;
|
||||
call<{ total: number; items: { ref: string; 이름?: string; 규격?: string }[] }>(
|
||||
`/logic-lib/elements?group=${encodeURIComponent("기계")}&q=${encodeURIComponent(q)}&limit=100`,
|
||||
).then(
|
||||
(got) => {
|
||||
if (mine !== seq) return;
|
||||
find.hint.textContent = `${got.total}`;
|
||||
find.setRows(
|
||||
got.items.map((x) => ({
|
||||
key: x.ref,
|
||||
name: `${x.이름 ?? ""} ${x.규격 ?? ""}`.trim(),
|
||||
sub: x.ref,
|
||||
onPick: () => {
|
||||
close();
|
||||
done(x.ref, `${x.이름 ?? ""} ${x.규격 ?? ""}`.trim());
|
||||
},
|
||||
})),
|
||||
);
|
||||
},
|
||||
(error) => showToast(why(error), "error"),
|
||||
);
|
||||
};
|
||||
find.search.addEventListener("input", () => run(find.search.value));
|
||||
find.search.value = line.이름.split(/[\s(]/)[0] ?? "";
|
||||
run(find.search.value);
|
||||
body.append(find.root);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** 남은 줄 창 — `map` 이 없으면 `nos` 를 서버에 물어 채움 · 닫을 때 `onClose(map)` */
|
||||
export function openLeft(
|
||||
at: LeftScope & { 파일: string; nos?: number[]; map?: LeftMap },
|
||||
@@ -213,9 +168,11 @@ export function openLeft(
|
||||
layer: at.target.layer,
|
||||
owner: at.target.owner,
|
||||
title: `${line.이름} ${line.규격}`.trim(),
|
||||
kinds: kindsOf(line), // 736-2 줄 갈래대로 탭(노임 = 인력 · 기계 = 기계 · 자재 = 재료)
|
||||
logic: (x) => link(no, line.차례, { 로직: x.키 }, x.이름),
|
||||
element: (e) =>
|
||||
link(no, line.차례, e.종류 === "인력" ? { 노임: e.코드 } : { 자재: e.코드 }, e.이름),
|
||||
machine: (e) => link(no, line.차례, { 기계: e.코드 }, e.이름),
|
||||
});
|
||||
const makeSk = async (no: number, block: number, 로직: string): Promise<void> => {
|
||||
try {
|
||||
@@ -277,16 +234,6 @@ export function openLeft(
|
||||
createButton({ label: L("M02_LeftChoose"), size: 24, variant: "ghost", onClick: () => choose(no, line) }),
|
||||
createButton({ label: L("M02_LeftFind"), size: 24, variant: "ghost", onClick: () => find(no, line) }),
|
||||
); // prettier-ignore
|
||||
if (line && row.갈래 === "기계")
|
||||
box.append(
|
||||
createButton({
|
||||
label: L("M02_LeftMachine"),
|
||||
size: 24,
|
||||
variant: "ghost",
|
||||
onClick: () =>
|
||||
pickMachine(line, (code, name) => link(no, row.차례, { 기계: code }, name)),
|
||||
}),
|
||||
);
|
||||
const none = createButton({
|
||||
label: NONE,
|
||||
size: 24,
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
type SangeunLink,
|
||||
type SangeunLost,
|
||||
} from "./M02_MasterTemplete_Hopyo_Api";
|
||||
import { pickMachine } from "./M02_MasterTemplete_Hopyo_Left";
|
||||
|
||||
const why = (error: unknown): string => (error instanceof Error ? error.message : String(error));
|
||||
const NONE = "우리에 없음";
|
||||
@@ -120,18 +119,17 @@ export function openLost(opt: LostOptions): void {
|
||||
size: 24,
|
||||
variant: "ghost",
|
||||
onClick: () =>
|
||||
kind === "기계"
|
||||
? pickMachine(x, (code) => put(x.id, { id: x.id, 기계: code }))
|
||||
: openFindPicker({
|
||||
layer: opt.scope.layer,
|
||||
owner: opt.scope.owner,
|
||||
title: `${x.이름} ${x.규격 ?? ""}`.trim(),
|
||||
logic: () => showToast(L("M02_GridLinkNoLogic"), "info"),
|
||||
element: (e) =>
|
||||
e.종류 === (kind === "노임" ? "인력" : "재료")
|
||||
? put(x.id, { id: x.id, [kind]: e.코드 })
|
||||
: showToast(L("M02_LostKind").replace("{k}", kind), "warning"),
|
||||
}),
|
||||
// 736-2 줄 갈래대로 탭 하나(노임 = 인력 · 기계 = 기계 · 자재 = 재료) → 이음 칸 `노임` · `기계` · `자재`
|
||||
openFindPicker({
|
||||
layer: opt.scope.layer,
|
||||
owner: opt.scope.owner,
|
||||
title: `${x.이름} ${x.규격 ?? ""}`.trim(),
|
||||
kinds: [kind === "노임" ? "labor" : kind === "기계" ? "machine" : "material"],
|
||||
query: x.이름.split(/[\s(]/)[0] ?? "",
|
||||
logic: () => showToast(L("M02_GridLinkNoLogic"), "info"),
|
||||
element: (e) => put(x.id, { id: x.id, [kind]: e.코드 }),
|
||||
machine: (e) => put(x.id, { id: x.id, 기계: e.코드 }),
|
||||
}),
|
||||
});
|
||||
const drop = createButton({
|
||||
label: NONE,
|
||||
|
||||
@@ -14,7 +14,6 @@ import { openModal } from "@ui/ui_template_modal";
|
||||
import { createTable } from "@ui/ui_template_table";
|
||||
import { getActiveLayer, getActiveOwner } from "./M02_MasterTemplete_Api_Fetch";
|
||||
import { openFindPicker } from "./M02_MasterTemplete_Find_Pick";
|
||||
import { pickMachine } from "./M02_MasterTemplete_Hopyo_Left";
|
||||
import type { LaborDoc } from "./M02_MasterTemplete_Labor_Api";
|
||||
import type { LaborFixed } from "./M02_MasterTemplete_Labor_Calc";
|
||||
|
||||
@@ -57,19 +56,14 @@ function openLink(doc: LaborDoc, changed: () => void): void {
|
||||
title: `${fx.이름} ${fx.규격 ?? ""}`.trim(),
|
||||
logic: () => showToast(L("M02_GridLinkNoLogic"), "info"),
|
||||
element: (e) => done(e.종류 === "인력" ? "노임" : "자재", e.코드),
|
||||
machine: (e) => done("기계", e.코드), // 736-2 기계도 같은 창 탭
|
||||
}),
|
||||
});
|
||||
const machine = createButton({
|
||||
label: L("M02_LeftMachine"),
|
||||
size: 24,
|
||||
variant: "ghost",
|
||||
onClick: () => pickMachine(fx, (code) => done("기계", code)),
|
||||
});
|
||||
return [
|
||||
fx.이름,
|
||||
fx.규격 ?? "",
|
||||
fx.단위 ?? "",
|
||||
el("div", { className: "ui-toolbar", children: [find, machine] }),
|
||||
el("div", { className: "ui-toolbar", children: [find] }),
|
||||
];
|
||||
});
|
||||
body.replaceChildren(
|
||||
|
||||
@@ -84,6 +84,8 @@ export function openSangeunLinePicker(
|
||||
...scope,
|
||||
logic: (x) => done({ 종류: "로직", 로직: x.키, 입력값: {}, 수량: 1 }),
|
||||
ref: (ref) => done({ 참조: ref, 수량: 1 }),
|
||||
element: (e) => done({ 종류: e.종류, 요소: e.코드, 이름: e.이름, 단위: e.단위, 수량: 1 }),
|
||||
element: (e) =>
|
||||
e.종류 !== "기계" &&
|
||||
done({ 종류: e.종류, 요소: e.코드, 이름: e.이름, 단위: e.단위, 수량: 1 }),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* · 일위대가 [자재 추가] 단추(`addMaterialButton` · 148 에서 `Labor_Detail` 이 덜어 냄)
|
||||
* · 개인 층(148-3) = 개인 자재(「개인」 표 · 앞) + M01 · 머리 [신규 자재] → 품명 · 단위(필수) · 규격 · 단가 →
|
||||
* 서버가 품번(`PM` + 6자리)을 주고 그 줄로 고름(`M02_Material_Own`)
|
||||
* · (736-2) `openNewMaterial` = 찾아 넣기 창(`Find_Pick`) 재료 탭 머리 [신규 자재] — 입력 창만 따로 열고 저장하면 그 줄을 돌려줌
|
||||
* 모양(`m02-pick*`)은 `M02_MasterTemplete_Structure.css`.
|
||||
* ========================================================================== */
|
||||
|
||||
@@ -25,7 +26,7 @@ export interface MaterialItem {
|
||||
}
|
||||
|
||||
/** 개인 자재 한 줄(서버 `개인자재.json` 꼴) */
|
||||
interface OwnRow {
|
||||
export interface OwnRow {
|
||||
키: string;
|
||||
이름: string;
|
||||
규격: string;
|
||||
@@ -55,6 +56,7 @@ const TEXT = {
|
||||
key: "품번 — 저장 때 자동",
|
||||
save: "저장",
|
||||
back: "목록으로",
|
||||
cancel: "취소",
|
||||
needName: "품명 · 단위는 꼭 적음",
|
||||
};
|
||||
|
||||
@@ -163,12 +165,39 @@ export function pickMaterial(done: (item: MaterialItem) => void, own?: OwnLayer)
|
||||
});
|
||||
}
|
||||
|
||||
/** 736-2 신규 자재 입력 창 — [저장] = 개인 층에 만들고 그 줄로 `saved` · 창 닫음 */
|
||||
export function openNewMaterial(
|
||||
owner: string | null,
|
||||
seed: string,
|
||||
saved: (row: OwnRow) => void,
|
||||
): void {
|
||||
openModal({
|
||||
title: TEXT.newMat,
|
||||
closeLabel: TEXT.pickClose,
|
||||
dialogClass: "m02-modal m02-pick",
|
||||
mount: (body, close) =>
|
||||
body.append(
|
||||
newForm(
|
||||
{ owner },
|
||||
seed,
|
||||
(row) => {
|
||||
close();
|
||||
saved(row);
|
||||
},
|
||||
close,
|
||||
TEXT.cancel,
|
||||
),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
/** 신규 자재 입력(148-3) — [저장] = 서버 추가 → 그 줄로 `saved` · 실패는 서버 까닭 그대로 */
|
||||
function newForm(
|
||||
own: OwnLayer,
|
||||
seed: string,
|
||||
saved: (row: OwnRow) => void,
|
||||
back: () => void,
|
||||
backLabel: string = TEXT.back,
|
||||
): HTMLElement {
|
||||
const name = createInputField({ label: TEXT.name, value: seed, required: true });
|
||||
const spec = createInputField({ label: TEXT.spec });
|
||||
@@ -224,7 +253,7 @@ function newForm(
|
||||
createText(TEXT.key),
|
||||
el("div", {
|
||||
className: "ui-btn-group",
|
||||
children: [createButton({ label: TEXT.back, variant: "ghost", onClick: back }), save],
|
||||
children: [createButton({ label: backLabel, variant: "ghost", onClick: back }), save],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -57,7 +57,7 @@ def test_721_2_부르는_곳_셋만_새_창():
|
||||
for key in ('key: "m01"', 'key: "sk"', 'key: "pre"'):
|
||||
assert key in find
|
||||
assert "createSelectField" not in find and "Hopyo" not in find
|
||||
assert '(kind: "job" | "price")' in find and "/elements" not in find
|
||||
assert '(kind: "job" | "price")' in find and "/elements" in find # 736-2 기계(EQ) 묶음
|
||||
|
||||
|
||||
def test_721_2_고정_줄_꼴():
|
||||
@@ -122,11 +122,32 @@ def test_736_1_항목_추가_찾기_창_하나():
|
||||
pick = _text(M02 / "M02_MasterTemplete_Labor_Pick.ts")
|
||||
assert "material: lib.material," in pick
|
||||
find = _text(M02 / "M02_MasterTemplete_Find_Pick.ts")
|
||||
assert 'kind === "재료" && opt.material ? opt.material : opt.element' in find
|
||||
assert 'export type FindKind = "logic" | "labor" | "material" | "sk" | "pre";' in find
|
||||
assert 'kind === "재료" && opt.material' in find and "? opt.material" in find
|
||||
assert '"logic" | "labor" | "material" | "machine" | "sk" | "pre"' in find
|
||||
# 불러오기 엑셀 연결 = 기초(자재 아닌 줄) · 재료(자재 · 재료비 줄) · 별칭 장부 = 기초 · 재료 둘
|
||||
choose = _text(M02 / "M02_MasterTemplete_Hopyo_Choose.ts")
|
||||
assert "다른 로직 검색" not in choose and "다른 자재 검색" not in choose
|
||||
assert 'material ? [] : (["logic"] as const)' in choose
|
||||
assert 'MATERIAL.has(line.갈래) ? [] : (["logic"] as const)' in choose
|
||||
assert 'canPickMaterial(line) ? (["material"] as const) : []' in choose
|
||||
assert 'kinds: ["logic", "material"],' in _text(M02 / "M02_MasterTemplete_Hopyo_Ledger.ts")
|
||||
|
||||
|
||||
def test_736_2_빠진_길_둘_신규_자재_기계_탭():
|
||||
"""736-2 — 개인 층 재료 고름 머리 [신규 자재] · 엑셀 연결 = 줄 갈래대로 탭(노임 인력 · 기계 기계 · 자재 재료)."""
|
||||
find = _text(M02 / "M02_MasterTemplete_Find_Pick.ts")
|
||||
assert 'opt.layer === "personal" && !!opt.material && ok("material")' in find
|
||||
assert "openNewMaterial(opt.owner" in find and "actions: [newMat]" in find
|
||||
assert "machine?: (e: M01Element) => void;" in find
|
||||
assert 'opt.machine && ok("machine")' in find
|
||||
pick = _text(M02 / "M02_Material_Pick.ts")
|
||||
assert "export function openNewMaterial(" in pick and "{ owner }," in pick
|
||||
choose = _text(M02 / "M02_MasterTemplete_Hopyo_Choose.ts")
|
||||
assert 'line.갈래 === "노임"' in choose and '["labor"]' in choose and '["machine"]' in choose
|
||||
assert "element: (e) => opt.done([{ 노임: e.코드, 배: 1 }])," in choose
|
||||
assert "machine: (e) => opt.done([{ 기계: e.코드, 배: 1 }])," in choose
|
||||
left = _text(M02 / "M02_MasterTemplete_Hopyo_Left.ts")
|
||||
assert "kinds: kindsOf(line)" in left and "pickMachine" not in left
|
||||
lost = _text(M02 / "M02_MasterTemplete_Hopyo_Lost.ts")
|
||||
assert '"machine"' in lost and "pickMachine" not in lost
|
||||
unlinked = _text(M02 / "M02_MasterTemplete_Labor_Unlinked.ts")
|
||||
assert 'machine: (e) => done("기계", e.코드)' in unlinked and "pickMachine" not in unlinked
|
||||
|
||||
@@ -115,9 +115,9 @@ def test_마법사_끝내기_막음_단가산출_칸_목록_열():
|
||||
assert "unlinkedBar(doc, edit)" in _text("M02_MasterTemplete_Labor_Grid.ts")
|
||||
assert "unlinkedBar(cur, edit &&" in _text("M02_MasterTemplete_Labor_Table.ts") # 일위대가표 띠
|
||||
left = _text("M02_MasterTemplete_Hopyo_Left.ts")
|
||||
assert (
|
||||
"/logic-lib/elements?group=" in left and "M01_MasterData_UI_Logic_Api" not in left
|
||||
) # 일반 사용자 길
|
||||
# 일반 사용자 길 — 기계 고름은 찾아 넣기 창(736-2)이 `/logic-lib/elements` 로
|
||||
assert "M01_MasterData_UI_Logic_Api" not in left
|
||||
assert "/logic-lib/elements?group=" in _text("M02_MasterTemplete_Find_Pick.ts")
|
||||
for name in (
|
||||
"M02_MasterTemplete_Hopyo_Left.ts",
|
||||
"M02_MasterTemplete_Hopyo_WizardSk.ts",
|
||||
|
||||
@@ -226,6 +226,7 @@ export const ui_locales_m2 = {
|
||||
M02_FindM01: ["M01 전체", "All M01"],
|
||||
M02_FindClass: ["구분", "Class"],
|
||||
M02_FindLogic: ["단가산출 기초", "Unit-cost logic"],
|
||||
M02_FindNewMaterial: ["신규 자재", "New material"],
|
||||
M02_FindElementLater: ["일위대가 표 개편 뒤", "After the unit-cost table rework"],
|
||||
M02_FindOptions: ["옵션 {n}", "{n} options"],
|
||||
// 729-3 불러오기 마법사 — 단가산출 단계 · 안 이은 줄 · 일위대가표 띠
|
||||
@@ -297,7 +298,6 @@ export const ui_locales_m2 = {
|
||||
M02_LeftChoose: ["고르기", "Pick"],
|
||||
M02_LeftFind: ["찾기", "Find"],
|
||||
M02_LeftMachine: ["기계", "Machine"],
|
||||
M02_LeftMachineFind: ["기계 이름 · 규격으로 찾기", "Search machines by name or spec"],
|
||||
M02_LeftNoneTip: [
|
||||
"우리 M01 에 없음 — 엑셀 숫자 그대로 고정 줄(사용자가 고른 것으로 남음)",
|
||||
"Not in M01 — keep the Excel numbers as a fixed row",
|
||||
@@ -331,5 +331,4 @@ export const ui_locales_m2 = {
|
||||
"Not in M01 — kept out of the SK with a note",
|
||||
],
|
||||
M02_LostClear: ["고름 지움", "Clear"],
|
||||
M02_LostKind: ["{k} 줄 — 같은 갈래 요소만 이음", "{k} row — link the same kind only"],
|
||||
} as const;
|
||||
|
||||
Reference in New Issue
Block a user