/* ============================================================================= * M01_MasterData_UI_Parts.ts * M01 「기계」 · 「계수」 표 아래 부품 문서(PLAN 813-2 · 요구 722-3 · 724~732) — 단가산출 기초 목록에서 뺀 보조 문서 자리 * 기계 · 상세구분(기종) 고름 = 「시간당 기계경비 계산식」(그 기종 기계가 쓰는 방식 · 기계 수) · 「작업량 공식 틀」(그 기종 틀) * 계수 = 「계수식」(기계 무관 · 돈 아닌 식 넷) * 이름 누름 = 줄 쌓기 읽기 창(`createStackScreen` · 고침 없음) · [단가산출 기초에서 열기] = 지금 수식 탭 그대로 고침 * 서버 = `GET /api/m01/parts?group&detail`(`M01_MasterData_Parts.of_kind`) * ========================================================================== */ import { API_BASE_URL } from "@config/config_frontend"; import { createButton } from "@ui/ui_template_button"; import { el } from "@ui/ui_template_elements"; import { openModal } from "@ui/ui_template_modal"; import { currentLanguageIndex } from "@ui/ui_template_locale"; import { createText } from "@ui/ui_template_text"; import { createStackScreen } from "../A00_Common/line_stack_screen"; import type { Pick } from "./M01_MasterData_UI_Side"; interface Part { 키: string; 이름: string; 결과단위: string; 기계수?: number; } type Parts = Partial>; const T = { 기계경비식: ["시간당 기계경비 계산식", "Hourly machine cost formula"], "작업량 틀": ["작업량 공식 틀", "Output formula templates"], 계수식: ["계수식", "Coefficient formulas"], Open: ["단가산출 기초에서 열기", "Open in unit price basis"], Machines: ["기계", "machines"], None: ["없음", "None"], Close: ["닫기", "Close"], } as const; const tx = (k: keyof typeof T): string => T[k][currentLanguageIndex === 1 ? 1 : 0]; const KINDS = ["기계경비식", "작업량 틀", "계수식"] as const; /** 표 아래 부품 칸 — 기계(상세구분 있음) · 계수만 · 그 밖 = 비움 */ export function renderParts(host: HTMLElement, pick: Pick, openLogic: (key: string) => void): void { host.replaceChildren(); const group = pick.group === "계수" ? "계수식" : pick.group === "기계" ? "기계" : ""; if (!group || (group === "기계" && !pick.detail)) return; let stack: ReturnType | null = null; /** 줄 쌓기 읽기 창 — 고침 없음 · [단가산출 기초에서 열기] = 수식 탭에서 고침 */ const show = (p: Part): void => { stack ??= createStackScreen({ base: "/m01/logic/doc", admin: false, cache: "m01_parts_view" }); const view = stack; openModal({ title: `${p.키} · ${p.이름}`, closeLabel: tx("Close"), width: "1100px", wide: true, mount: (body, close, actions) => { actions.append( createButton({ label: tx("Open"), variant: "ghost", size: 28, onClick: () => { close(); openLogic(p.키); }, }), ); body.append(view.root); view.draw(p.키, true); }, }); }; const q = new URLSearchParams({ group, detail: pick.detail ?? "" }); const mine = (host.dataset.partsAsk = q.toString()); // 늦게 온 옛 답은 버림 void fetch(`${API_BASE_URL}/m01/parts?${q}`, { credentials: "include" }) .then((r): Promise | Parts => (r.ok ? (r.json() as Promise) : {})) .catch((): Parts => ({})) .then((got) => { if (host.dataset.partsAsk !== mine) return; const blocks = KINDS.filter((k) => k in got).map((k) => { const list = got[k] ?? []; const items = list.map((p) => { const b = createButton({ label: `${p.이름}${p.기계수 ? ` · ${tx("Machines")} ${p.기계수}` : ""}`, variant: "ghost", tone: "link", size: 28, onClick: () => show(p), }); b.dataset.part = p.키; return b; }); return el("section", { className: "m01-parts__block", attrs: { "data-parts": k }, children: [ el("h3", { className: "m01-parts__title", text: tx(k) }), items.length ? el("div", { className: "m01-parts__list", children: items }) : createText(tx("None")), ], }); }); if (blocks.length) host.replaceChildren(el("div", { className: "m01-parts", children: blocks })); }); }