Files
Aislo/M02_MasterTemplete/M02_MasterTemplete_Labor_RowModal.ts
T
eomsangdonandClaude Opus 5.5 6490b999e3 feat(723-2): M02 일위대가 상세 = 일위대가표만 — 수량 칸 휘발 임시 입력 · 우클릭(위로 · 아래로 · 별칭 · 삭제) · 줄 누름 모달
- 새 `Labor_Table.ts` = 서고(사전조합) 상세 — 정보 칸 · 일위대가표(laborBlock 격자) · 원문 패널 · 머리 [로직 추가] [자재 추가]
  · 수량 칸 = 확인용 임시 입력(저장 · dirty 없음 · 짝 셈 settle 로 금액 미리 보기) · 「빈 값 n」 = 비고 붉게
  · 붙여넣기 엑셀 수량 = 임시 칸으로 · 옛 문서 손 수량은 읽을 때 걷음(723-1) · lib.fixed(721-2 인력 · 재료 고정 줄)
- 새 `Labor_RowModal.ts` = 줄 누름 모달(옵션 · 메모 · 켜쌓기율 · 로직 자재 바꾸기 · 세 칸 · 단가산출 보기) · 자재 줄 모달(세 칸)
- `Labor_Detail.ts` = mode precombo 면 Labor_Table 로 · 서고 갈래 걷음(679 → 649 줄)
- 낡은 시험 고침: test_175 화면 글 대조(서고 갈래가 Labor_Table · RowModal 로 옮김)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 21:58:10 +09:00

208 lines
7.5 KiB
TypeScript

/* =============================================================================
* M02_MasterTemplete_Labor_RowModal.ts
* 일위대가표 줄 누름 모달(PLAN 723-2 · 요구서_내역16 2장 4) — 일위대가표에서 빠진 줄 동작을 모달 안으로.
* · 로직 줄 · 단가산출(SK) 참조 줄 = 머리(이름 · 「1 ㎡ 당」) · [옵션 · 메모](`Labor_Input` · 켜쌓기율) ·
* 로직 자재(바꾸기 · 기본 · 세 칸 · `Labor_LogicMats`) · [단가산출 보기](717-3 `Labor_SangeunModal`)
* · 사용자 자재 줄 = 자재 모달(세 칸 · `Labor_MatTable` 한 줄)
* · 고침 = 그 줄 화면 캐시(저장은 [저장]) · 금액은 표가 다시 셈 → `watch` 로 받아 다시 그림
* ========================================================================== */
import { createButton, el, showToast } from "@ui/ui_template_elements";
import { openModal } from "@ui/ui_template_modal";
import { createText } from "@ui/ui_template_text";
import type { ItemLogic } from "./M02_MasterTemplete_Api_Fetch";
import type { LaborCalc, LaborMat, LaborRow, MatMarkKey } from "./M02_MasterTemplete_Labor_Api";
import { openLaborInput } from "./M02_MasterTemplete_Labor_Input";
import { logicMatRows } from "./M02_MasterTemplete_Labor_LogicMats";
import { setLogicMark, setMark } from "./M02_MasterTemplete_Labor_MatMarks";
import { matTable } from "./M02_MasterTemplete_Labor_MatTable";
import { openRowSangeun } from "./M02_MasterTemplete_Labor_SangeunModal";
import { tc } from "./M02_MasterTemplete_Labor_Text";
import type { SkScope } from "./M02_MasterTemplete_Sangeun_Cover";
import { releaseTied } from "./M02_Labor_LogicMats_Model";
import { keepKnown, valueLine } from "./M02_Labor_Values_Model";
/** 표가 다시 센 답을 받음 — 돌려준 함수 = 그만 받음 */
export type CalcWatch = (fn: (calc: LaborCalc | null) => void) => () => void;
export interface RowModalOptions {
row: LaborRow;
logic: ItemLogic | null;
title: string;
/** 일위대가 머리(이름 · 「1 ㎡ 당」) */
head: string;
/** 줄 분모(SK 단위 기본) */
unit: string;
scope: SkScope;
readOnly: boolean;
/** 셈 주입(프로젝트 복사본) — [단가산출 보기] 없음 */
inject: boolean;
/** 사용자 자재 코드 — 로직 자재 [바꾸기]로 같은 품목 막음 */
userMats: string[];
watch: CalcWatch;
/** 고침 뒤 — `again` = 서버 다시 셈(입력 · 고름 · 별도계상) · 아니면 화면만 */
done: (again: boolean) => void;
/** [단가산출로 저장] 뒤 그 줄을 새 SK 참조 줄로 */
replace?: (row: LaborRow) => void;
}
export function openRowModal(o: RowModalOptions): void {
const row = o.row;
const edit = !o.readOnly;
const info = el("div", { attrs: { "data-row-head": "" } });
const mats = el("div", { attrs: { "data-row-mats": "" } });
let calc: LaborCalc | null = null;
const line = () => calc?.줄.find((l) => !row.참조 && l.로직 === row.로직);
const draw = (): void => {
const values = valueLine(o.logic?.입력 ?? [], row.입력값);
info.replaceChildren(
createText(o.head, "muted", 14),
...(values.text ? [createText(values.text)] : []),
...(values.empty
? [createText(tc("Labor_Empty").replace("{n}", String(values.empty)), "error")]
: []),
...(row.켜쌓기율 != null
? [createText(tc("Labor_Layer").replace("{n}", String(row.켜쌓기율)), "muted")]
: []),
...(row.메모 ? [createText(row.메모, "muted")] : []),
);
const lead = row.참조
? []
: logicMatRows([row], calc, !calc, {
open: () => undefined,
...(edit
? {
swap: (_r: number, at: number, key: string) => {
if (o.userMats.includes(key))
return showToast(tc("Labor_AlreadyMat").replace("{value}", key), "error");
row.고름 = { ...row.고름, [String(at)]: key };
o.done(true);
},
reset: (_r: number, at: number) => {
if (!row.고름) return;
delete row.고름[String(at)];
if (!Object.keys(row.고름).length) delete row.고름;
o.done(true);
},
mark: (_r: number, at: number, key: MatMarkKey, v: string) => {
setLogicMark(row, at, key, v);
o.done(key === "별도계상");
draw();
},
}
: {}),
});
mats.replaceChildren(
...(lead.length
? [el("h4", { text: tc("Labor_Mats") }), matTable({ mats: [], calc, lead })]
: []),
);
};
const option = createButton({
label: tc("Labor_Option"),
variant: "ghost",
onClick: () =>
openLaborInput({
title: o.title,
logic: o.logic,
values: row.입력값,
memo: row.메모,
rate: row.켜쌓기율 ?? null,
readOnly: o.readOnly,
done: (next) => {
const values = o.logic ? keepKnown(o.logic.입력, next.입력값).kept : next.입력값;
const same =
JSON.stringify(values) === JSON.stringify(row.입력값) &&
next.켜쌓기율 === (row.켜쌓기율 ?? null);
const before = row.입력값;
row.입력값 = values;
releaseTied(row, before, line()?.재료 ?? []);
row.메모 = next.메모;
if (next.켜쌓기율 === null) delete row.켜쌓기율;
else row.켜쌓기율 = next.켜쌓기율;
o.done(!same);
draw();
},
}),
});
option.dataset.rowOption = "";
const sk = createButton({
label: tc("Labor_Sangeun"),
variant: "ghost",
onClick: () =>
openRowSangeun({
scope: o.scope,
row,
title: o.title,
head: o.head,
qty: "",
unit: o.unit,
readOnly: o.readOnly,
apply: (p) => {
if (p) row.패치 = p;
else delete row.패치;
o.done(true);
},
...(o.replace ? { replace: o.replace } : {}),
}),
});
sk.dataset.rowSangeun = "";
const stop = o.watch((c) => {
calc = c;
draw();
});
openModal({
title: o.title,
closeLabel: tc("In_Close"),
width: "960px",
actions: [...(row.참조 ? [] : [option]), ...(o.inject ? [] : [sk])],
beforeClose: () => (stop(), true),
mount: (body) => body.append(info, mats),
});
draw();
}
export interface MatModalOptions {
mat: LaborMat;
watch: CalcWatch;
readOnly: boolean;
/** 세 칸 고침 뒤 — `money` = 별도계상(금액에 닿음) */
done: (money: boolean) => void;
}
/** 사용자 자재 줄 — 세 칸(148-4 · 222-1) · 가격 다섯 보기 */
export function openMatModal(o: MatModalOptions): void {
const host = el("div", { attrs: { "data-row-mat": o.mat.코드 } });
let calc: LaborCalc | null = null;
const draw = (): void =>
host.replaceChildren(
matTable(
{ mats: [o.mat], calc },
o.readOnly
? { open: () => undefined }
: {
open: () => undefined,
mark: (_i, key, v) => {
setMark(o.mat, key, v);
o.done(key === "별도계상");
draw();
},
},
),
);
const stop = o.watch((c) => {
calc = c;
draw();
});
openModal({
title: `${o.mat.코드} ${o.mat.이름 ?? ""}`.trim(),
closeLabel: tc("In_Close"),
width: "1100px",
beforeClose: () => (stop(), true),
mount: (body) => body.append(host),
});
draw();
}