/* ============================================================================= * line_stack_machine_sheet_work.ts * 기계 보기 시간당 작업량(PLAN 846-2 · 요구 883 「시간당 작업량 = 기계의 능력 → 기계에 둠」) * 왼쪽 = 「시간당 작업량」 접이 카드 — 기계 줄 `값.작업량` 식마다 이름 + 변수 칸(기본 = 기계 값 › 식 문서 기본 · * 고친 칸 「고침」 + [원래 값] = 처음 줄 값으로) · 고침 = 지금 줄(저장은 부르는 쪽 — M01 기계 상세 · 내 기계) * 오른쪽 = 시간당 경비 풀이 아래 「시간당 작업량」 — 그 식 풀이(값 줄 + 결과 식 · `hourlyRows` 산출근거와 같음) * 식 없는 기계 = 「시간당 작업량 식 없는 기계」 · 작업량 틀 · 표 연결 = 기계 보기 서버(`M02_MyMachine_Sheet._work`) * ========================================================================== */ import { attachCollapsible, createPanelCard } from "@ui/ui_template_collapsible"; import { el } from "@ui/ui_template_elements"; import type { FormulaDoc } from "../common_util/common_util_formula_doc"; import { calcDoc, type DocLinks } from "../common_util/common_util_formula_doc_calc"; import { specsOf, workDefaults, type WorkSpec } from "../common_util/common_util_formula_doc_work"; import type { MachineRow } from "../common_util/common_util_machine_sheet"; import { lt } from "./line_stack_text"; import { workVarRows } from "./line_stack_machine_work"; import { hourlyRows, workTag } from "./line_stack_work"; export interface SheetWorkOpts { cur: MachineRow; orig: MachineRow; ro: boolean; links: DocLinks; /** 캐시에 없는 연결 — 뒤에서 서버 */ miss: () => void; /** 지금 줄을 고친 뒤 */ changed: () => void; } const specsOfRow = (row: MachineRow): WorkSpec[] => specsOf(row.값?.작업량); const docOf = (o: SheetWorkOpts, key: string): FormulaDoc | null => { try { return o.links.doc(key) ?? null; } catch { o.miss(); return null; } }; const note = (text: string): HTMLElement => el("div", { className: "lstk-empty", text }); /** 왼쪽 카드 — `keep` = 접힘 유지 */ export function sheetWorkCard(o: SheetWorkOpts, keep: boolean): HTMLElement { const card = createPanelCard(lt("Work_Head"), keep); card.dataset.machineWork = "1"; const specs = specsOfRow(o.cur); if (!specs.length) card.append(note(lt("Work_None"))); const was = specsOfRow(o.orig); specs.forEach((spec, i) => { const tpl = docOf(o, spec.식); if (!tpl) return void card.append(note(`${spec.식} — ${lt("Work_Wait")}`)); const first = was.find((s) => s.식 === spec.식) ?? null; const box = el("div", { className: "lstk-mctl", children: [ el("div", { className: "lstk-fhead", children: [workTag("시간당", spec.식), el("span", { className: "lstk-hint", text: tpl.이름 ?? spec.식 })] }), // prettier-ignore ...workVarRows({ tpl, now: workDefaults(tpl, spec), base: workDefaults(tpl, first), ro: o.ro, set: (k, v) => { const vars = { ...(spec.변수 ?? {}) }; const back = first?.변수?.[k]; if (v !== null) vars[k] = v; else if (back !== undefined) vars[k] = back; else delete vars[k]; const list = specsOfRow(o.cur).map((s, j) => (j === i ? { ...s, 변수: vars } : s)); if (!Object.keys(vars).length) delete list[i].변수; (o.cur.값 ??= {}).작업량 = list; o.changed(); }, }), ], }); card.append(box); }); attachCollapsible(card); return card; } /** 오른쪽 풀이 — 식마다 머리 + 값 줄 + 결과 식 */ export function sheetWorkOut(o: SheetWorkOpts): HTMLElement { const specs = specsOfRow(o.cur); const box = el("div", { className: "lstk-msheet-work", attrs: { "data-machine-work-out": "1" } }); box.append(el("strong", { text: lt("Work_Head") })); if (!specs.length) return (box.append(note(lt("Work_None"))), box); for (const spec of specs) { const tpl = docOf(o, spec.식); if (!tpl) { box.append(note(`${spec.식} — ${lt("Work_Wait")}`)); continue; } const a = calcDoc(tpl, o.links, workDefaults(tpl, spec), [], new Map(), {}); box.append( el("div", { className: "lstk-work", children: [workTag("시간당", spec.식), el("span", { className: "lstk-hint", text: tpl.이름 ?? spec.식 })] }), // prettier-ignore ...hourlyRows(tpl, a, (p) => o.links.table(p)).map((r) => el("div", { className: `lstk-work ${r.cls ?? ""}`.trim(), children: r.h }), ), ); } return box; }