Files
Aislo/M02_MasterTemplete/M02_MasterTemplete_Hopyo_SheetSk.ts
T
eomsangdonandClaude Opus 5.5 4ae8d742f2 fix(835-1): M02 불러오기 화면 여섯 — 보기 자동 저장 취소 · 비목 줄 금액 칸 · 상세 초록 칠 걷음 · 저장 단추 → 고르면 모달 · 모달 1400
- 자동 저장 취소(요구 852-1) — 가져온 식 열기 = 미리 보기 `…/sangeun/from-hopyo/preview`(층 · 재료 고름 안 씀 ·
  같은 내용 SK 있으면 그것을 읽음) · [저장] = `from-hopyo` 몸 `문서`(고친 문서째 · 안쪽 산근 참조 · 내 기계 그때) ·
  옛 길: 단가산출표 목록 [내 단가산출로] · [수정] 모달 단산 줄 누름이 서버 make 로 SK 를 바로 만들었음
  (`FromHopyo.make` 를 `_prep` · `draft` · `make(doc)` 로 · `Imported.new_doc` · `keep` · `draft` · `MatPick.merge`)
- 산출근거 표 비목 줄(노무비 : … = 금액) = 합계 · 그 비목 칸에 금액(엑셀 단가산출 시트와 같음 · 852-2 · `line_stack_out`)
- 단가산출 상세 시트 블록 안 줄 이어짐 초록 안 칠함 — 출처 행 = 인력 · 기계 노무비 행(852-3 · 다른 칠 · 머리 행 그대로)
- 단가산출 목록 [내 단가산출로] 걷음 → 줄 고르면 개인 층 가져온 식 모달(852-4)
- 일위대가표 목록 [내 일위대가로] 걷음 → 줄 고르면 [수정] 모달(852-5 · 모달 머리 [내 일위대가로] 그대로)
- 부품 상자 모달 큰 중간 창 `1400px`(1400 × 880) — 불러오기 [수정] · 가져온 식 모달(852-6)
- 시험: 새 test_835_1(미리 보기 쓰기 0 · [저장] = 옛 길과 같은 SK) · 낡은 시험 고침 —
  test_627(저장 단추 걷음 · MatPick 가짜 없어 import.meta 로 죽던 것) · test_779_5(이어짐 초록 안 칠함) ·
  test_793_3b · test_809_1(수정 모달 단산 줄 = 가져온 식 모달)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-15 11:54:05 +09:00

145 lines
6.6 KiB
TypeScript

/* =============================================================================
* M02_MasterTemplete_Hopyo_SheetSk.ts
* 엑셀 보기 창 오른쪽 판 바꾸기(PLAN 780-2 · 809-1 · 요구 627 · 702) — 탭 따라:
* · 일위대가표 = 호표 목록(`Hopyo_Sheet` 판 그대로) · 단가산출표 · 단가산출 상세 = 단가산출 목록
* (블록 줄 = 단산 N 이름 · 단위 · 부름 수 / 일위대가 밖 · 만든 가져온 식 SK · 재료 = `hopyo-marks`)
* · 블록 줄 누름 → 지금 탭의 그 블록 자리 + 개인 층 `Hopyo_Imported` 모달(미리 보기 · 저장은 모달 [저장] · 835-1 ·
* 옛 [내 단가산출로] 단추 걷음) · 줄 끝 [가져온 식으로 열기](만들 층이 개인 밖일 때만)
* (경쟁사 로직 그대로 · 엑셀 단가 · 요소 이음 · 소요량 이음)
* · 시트 칸 고름 → 그 행 블록을 목록에서 강조(`SheetNav.blockAt`)
* · 목록은 처음 단가산출 탭에 올 때 읽음 · 기초 판정 · 블록 판 · [옮기기] 없음(요구 703)
* · 머리 = 「고를 재료」 판(827-3 `Hopyo_MatPick`) — 저장 안 한 고름은 블록 만들 때 같이 감
* ========================================================================== */
import { createBadge } from "@ui/ui_template_badge";
import { createButton, el } from "@ui/ui_template_elements";
import { t as L } from "@ui/ui_template_locale";
import { createListRow } from "@ui/ui_template_list_row";
import { createText } from "@ui/ui_template_text";
import type { HopyoScope } from "./M02_MasterTemplete_Hopyo_Api";
import { openImported } from "./M02_MasterTemplete_Hopyo_Imported";
import { createMatPick } from "./M02_MasterTemplete_Hopyo_MatPick";
import { readHopyoMarks, type SheetNav } from "./M02_MasterTemplete_Hopyo_SheetNav";
import type { BlockMark } from "./logic/M02_MasterTemplete_Hopyo_SheetMap";
export interface SheetSkOptions {
scope: HopyoScope;
key: string;
target: HopyoScope;
canWrite: boolean;
nav: SheetNav;
/** 호표 목록 판 — 단가산출 탭이면 숨김 · 그 뒤에 단가산출 판을 붙임 */
side: HTMLElement;
/** 목록 읽은 뒤(부르는 쪽 끝내기 글) */
onChange?: () => void;
}
const STRIPE = { done: "var(--color-success)", ready: "var(--color-info)", out: "var(--color-border)" } as const; // prettier-ignore
export function createSheetSk(opt: SheetSkOptions) {
const list = el("div", { className: "m02-hopyo-sheet__list" });
const mat = createMatPick({ scope: opt.scope, key: opt.key, target: opt.target }); // 827-3
const root = el("div", { className: "m02-hopyo-sheet__side m02-hopyo-sheet__sk", children: [mat.root, list] }); // prettier-ignore
root.setAttribute("data-hopyo-sk-side", "");
root.hidden = true;
opt.side.after(root);
let blocks: BlockMark[] | null = null;
let asked = false;
let sheet = "";
let current: number | null = null;
const mineAt = { layer: "personal", owner: null } as const; // 줄 고름 모달(807-1 · 835-1) = 늘 개인 층
const open = (no: number, mine: boolean): void =>
openImported({ file: opt.scope, key: opt.key, no, target: mine ? mineAt : opt.target, canWrite: mine || opt.canWrite, picks: mat.pending(), onMade: mat.reload }); // prettier-ignore
function draw(): void {
if (!blocks) return;
list.replaceChildren(
...blocks.map((b) => {
const calls = b.부름?.length ?? 0;
const sub = [b.단위 ?? "", calls ? `${L("M02_JudgeCalls")} ${calls}` : L("M02_JudgeOutside")].filter(Boolean).join(" · "); // prettier-ignore
const go = createButton({ label: L("M02_CheckImported"), size: 24, variant: "ghost", onClick: (e) => { e.stopPropagation(); open(b.번, false); } }); // prettier-ignore
go.dataset.skOpen = String(b.번);
go.hidden = opt.target.layer === "personal" && !opt.target.owner; // 만들 층 = 개인이면 줄 고름과 같은 일
const tail = el("span", { className: "ui-toolbar", children: [...(b.SK ? [createBadge(`${L("M02_JudgeMade")} ${b.SK}`, "success")] : []), go] }); // prettier-ignore
const row = createListRow({
label: `${L("M02_JudgeBlock").replace("{n}", String(b.번))} ${b.이름 ?? ""}`,
sub,
trailing: tail,
stripe: b.SK ? STRIPE.done : calls ? STRIPE.ready : STRIPE.out,
active: b.번 === current,
size: 36,
onClick: () => {
pick(b.번);
open(b.번, true); // 고르면 모달 — 저장할지는 모달에서(835-1)
},
});
row.dataset.judgeSk = String(b.번);
return row;
}),
);
}
/** 목록 — 처음 단가산출 탭에 올 때 한 번 */
function ensure(): void {
if (asked) return;
asked = true;
mat.ensure();
if (!blocks) list.replaceChildren(createText(L("M02_JudgeLoading"), "hint"));
void readHopyoMarks(opt.scope, opt.key, opt.target)
.then((got) => {
blocks = got;
draw();
opt.onChange?.();
})
.catch((e: Error) => list.replaceChildren(createText(e.message, "error")));
}
/** 블록 고름 — 그 자리 뜀 */
function pick(no: number): void {
current = no;
draw();
const list = opt.nav.role(sheet) === "단가산출표";
const spot = list
? (opt.nav.spotOf(no, "단가산출표") ?? opt.nav.spotOf(no, "단가산출상세"))
: (opt.nav.spotOf(no, "단가산출상세") ?? opt.nav.spotOf(no, "단가산출표"));
if (spot) opt.nav.jumpTo(spot.시트, spot.행);
}
return {
root,
/** 아직 SK 안 만든 블록 수 · 그중 일위대가 밖 — 목록 전 null */
count: (): { 블록: number; 밖: number; 줄: number } | null => {
if (!blocks) return null;
const left = blocks.filter((b) => !b.SK);
return { 블록: left.length, 밖: left.filter((b) => !b.부름?.length).length, 줄: 0 };
},
/** 그 블록으로 뜀 */
show(번: number): void {
ensure();
pick(번);
},
/** 시트 고름 · 탭 바꿈 — 판 바꾸기 · 강조 */
at(sel: { 시트: string; 칸: string }): void {
sheet = sel.시트;
const role = opt.nav.role(sel.시트);
const sk = role === "단가산출표" || role === "단가산출상세";
opt.side.hidden = sk;
root.hidden = !sk;
opt.side.parentElement?.classList.toggle("is-sk", sk);
if (!sk) return;
ensure();
const no = opt.nav.blockAt(sel.시트, Number(/\d+/.exec(sel.칸)?.[0] ?? 0));
if (no !== null && no !== current) {
current = no;
draw();
}
},
destroy(): void {},
/** 저장 안 한 재료 고름(827-3) */
dirty: mat.dirty,
};
}
export type SheetSk = ReturnType<typeof createSheetSk>;