feat(M02): 조합 고치기 · 자재 목록을 실제 갈라 내기 길에 연결 · 알약 자재 항목 = 옆 판 목록 · 줄 누르면 이은 칸으로
- 가정하던 서버 함수를 실제 branchCombo · branchMaterialCombo · fetchMaterialCombo(M02 Api_Fetch)로 바꾸고 가정 파일은 열 조합 읽기만 남김 - 재료산출 알약 막대 자재 항목을 그 열 자재 조합 담은 자재로 · 알약 더하기 · 빼기(onItemsChange)와 옆 판 표를 같은 목록으로 맞춤 - 옆 판 줄을 누르면 이은 알약을 눌러 그 칸으로 이동(깜빡임) · 옆 판에서 고치면 알약 막대만 다시 만듦(시트 고침 유지) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWHNLQYPZN7kKf1rzrrRZq
This commit is contained in:
@@ -24,8 +24,17 @@ import {
|
||||
type StructurePanelHandle,
|
||||
} from "./M02_MasterTemplete_Structure";
|
||||
import { buildComboBar } from "./M02_MasterTemplete_Combo";
|
||||
import { buildMaterialPanel } from "./M02_MasterTemplete_Materials";
|
||||
import {
|
||||
buildMaterialPanel,
|
||||
loadMaterialState,
|
||||
toPills,
|
||||
type MaterialPanel,
|
||||
type MaterialState,
|
||||
} from "./M02_MasterTemplete_Materials";
|
||||
import type { SpreadsheetHandle } from "../A00_Common/spreadsheet/spreadsheet";
|
||||
import type { LinkOptions } from "../A00_Common/spreadsheet/spreadsheet_links";
|
||||
import { searchMaterials } from "../M01_MasterData/M01_MasterData_Api_Fetch";
|
||||
import type { MaterialItem } from "./M02_MasterTemplete_Api_Fetch";
|
||||
export interface Selection {
|
||||
layer: Layer;
|
||||
projectId: string | null;
|
||||
@@ -110,6 +119,9 @@ export function createMain(isAdmin: boolean, onSaved?: () => void): MainHandle {
|
||||
let editor: EditorHandle | null = null;
|
||||
let sheet: SheetEditorHandle | null = null;
|
||||
let panel: StructurePanelHandle | null = null;
|
||||
let sheetHandle: SpreadsheetHandle | null = null;
|
||||
let matPanel: MaterialPanel | null = null;
|
||||
let matState: MaterialState | null = null;
|
||||
let seq = 0;
|
||||
let dirty = false;
|
||||
|
||||
@@ -144,6 +156,9 @@ export function createMain(isAdmin: boolean, onSaved?: () => void): MainHandle {
|
||||
editor?.destroy();
|
||||
editor = null;
|
||||
sheet = null;
|
||||
sheetHandle = null;
|
||||
matPanel = null;
|
||||
matState = null;
|
||||
panel?.destroy();
|
||||
panel = null;
|
||||
extra.replaceChildren();
|
||||
@@ -166,21 +181,36 @@ export function createMain(isAdmin: boolean, onSaved?: () => void): MainHandle {
|
||||
}
|
||||
|
||||
/** 면적산출 · 재료산출 스프레드시트 — 위 알약 막대(sub7 `links`) · 면적 = 그 열 일위대가 입력값 ·
|
||||
* 재료 = 자재DB 찾기(`pillsFor`) · 시스템 층은 면적 = 조합 줄 + [조합 고치기] · 재료 = 자재 목록 옆 판 */
|
||||
* 재료 = 자재DB 찾기(`pillsFor`) · 시스템 층은 면적 = 조합 줄 + [조합 고치기] ·
|
||||
* 재료 = 그 열 자재 조합 담은 자재(알약 항목 = 옆 판 표 = 같은 목록) + 옆 판
|
||||
* (`pillItems` = 옆 판에서 고친 목록으로 알약 막대만 다시 만들 때) */
|
||||
async function mountSheet(
|
||||
doc: unknown,
|
||||
kind: "area" | "material",
|
||||
at: Selection,
|
||||
readOnly: boolean,
|
||||
pillItems?: MaterialItem[],
|
||||
): Promise<void> {
|
||||
const mine = seq;
|
||||
try {
|
||||
const master = at.layer === "system" && kind === "material";
|
||||
if (master && !matState) matState = await loadMaterialState(at.name);
|
||||
const [{ createSpreadsheet }, pills] = await Promise.all([
|
||||
import("../A00_Common/spreadsheet/spreadsheet"),
|
||||
pillsFor(kind, at.name),
|
||||
master ? null : pillsFor(kind, at.name),
|
||||
]);
|
||||
if (mine !== seq) return;
|
||||
if (pills.notice) showNotice([pills.notice]);
|
||||
if (pills?.notice) showNotice([pills.notice]);
|
||||
const links: LinkOptions =
|
||||
pills?.links ??
|
||||
({
|
||||
항목: toPills(pillItems ?? matState?.combo?.담은자재 ?? []),
|
||||
모드: "자재",
|
||||
자재찾기: searchMaterials,
|
||||
// 알약 막대에서 더하기 · 빼기 — 옆 판 표를 같은 목록으로
|
||||
onItemsChange: (list) =>
|
||||
matPanel?.setItems(list.flatMap((i) => (i.자재 ? [{ ...i.자재 }] : []))),
|
||||
} satisfies LinkOptions);
|
||||
const handle: SpreadsheetHandle = createSpreadsheet(
|
||||
host,
|
||||
doc as Parameters<typeof createSpreadsheet>[1],
|
||||
@@ -188,16 +218,16 @@ export function createMain(isAdmin: boolean, onSaved?: () => void): MainHandle {
|
||||
readOnly,
|
||||
tabs: false, // 시트 하나 · 탭 줄 없음
|
||||
onChange: () => (dirty = !readOnly),
|
||||
links: pills.links,
|
||||
links,
|
||||
},
|
||||
);
|
||||
editor = handle;
|
||||
sheetHandle = handle;
|
||||
if (at.layer !== "system") return;
|
||||
const label = at.label ?? at.name;
|
||||
if (kind === "area") {
|
||||
const bar = await buildComboBar({
|
||||
column: at.name,
|
||||
label,
|
||||
label: at.label ?? at.name,
|
||||
isAdmin,
|
||||
beforeEdit: async () => true,
|
||||
// 알약만 다시 받음 — 저장 안 한 시트 고침은 그대로 옮김
|
||||
@@ -213,23 +243,38 @@ export function createMain(isAdmin: boolean, onSaved?: () => void): MainHandle {
|
||||
if (mine === seq) comboSlot.replaceChildren(...(bar ? [bar] : []));
|
||||
return;
|
||||
}
|
||||
const panel = await buildMaterialPanel({
|
||||
if (matPanel || !matState) return;
|
||||
matPanel = buildMaterialPanel({
|
||||
column: at.name,
|
||||
label,
|
||||
label: at.label ?? at.name,
|
||||
isAdmin,
|
||||
links: () => handle.getDoc().시트.flatMap((s) => s.연결 ?? []),
|
||||
state: matState,
|
||||
links: () => sheetHandle?.getDoc().시트.flatMap((s) => s.연결 ?? []) ?? [],
|
||||
// 옆 판 표에서 고침 — 알약 막대만 이 목록으로 다시 만듦(저장 안 한 시트 고침은 그대로)
|
||||
onChange: (items) => {
|
||||
const kept = sheetHandle?.getDoc();
|
||||
if (!kept) return;
|
||||
editor?.destroy();
|
||||
editor = null;
|
||||
void mountSheet(kept, kind, at, readOnly, items);
|
||||
},
|
||||
goTo: (code) =>
|
||||
host
|
||||
.querySelector<HTMLElement>(`.ss-link.is-linked[data-key="${CSS.escape(code)}"]`)
|
||||
?.click(),
|
||||
unlink: (codes) => {
|
||||
const book = handle.getDoc();
|
||||
const book = sheetHandle?.getDoc();
|
||||
if (!book) return;
|
||||
for (const s of book.시트) {
|
||||
const rest = (s.연결 ?? []).filter((l) => !codes.includes(l.자재?.코드 ?? l.키));
|
||||
if (rest.length) s.연결 = rest;
|
||||
else delete s.연결;
|
||||
}
|
||||
handle.setDoc(book);
|
||||
sheetHandle?.setDoc(book);
|
||||
dirty = !readOnly;
|
||||
},
|
||||
});
|
||||
if (mine === seq) host.append(panel);
|
||||
host.append(matPanel.root);
|
||||
} catch {
|
||||
empty(L("M02_NoSpreadsheet"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user