feat(M01): 옛 일위대가 로직 컨테이너는 호표 표 그대로 · 아래에 「로직 쉽게 보기·만들기」 컨테이너를 더해 방식 C + 새로 만들기 모달을 담음

- 일위대가 로직 컨테이너 = 로직을 누르면 옛 호표 표 + 시험 계산 칸이 바로 열림 · 보기 전환 단추와 보기 캐시 값 없앰 · 「새로 만들기」 는 옛 초안 방식
- 새 컨테이너 = 같은 목록·API 를 쓰되 거름 기억과 저장 안 한 것 캐시를 따로 둠 · 로직을 고르면 흐름 그림 · 새로 만들기 모달 · 본떠 만들기 · 상자 고치기

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
2026-09-21 12:12:16 +09:00
co-authored by Claude Sonnet 5
parent 5bef49df88
commit 83752f8698
4 changed files with 91 additions and 38 deletions
+52 -32
View File
@@ -61,14 +61,13 @@ interface Opened extends Draft {
values: Record<string, string>;
}
const CACHE_KEY = "m01_logic_drafts";
/** 기본 보기 = 흐름 그림 · 「고급」 = 옛 호표 표 + 시험 계산 칸 */
const VIEW_KEY = "m01.logic.view";
/** 두 컨테이너(옛 호표 화면 · 쉽게 보기·만들기)가 서로의 저장 안 한 것을 덮지 않게 캐시 칸을 가름 */
const cacheKey = (easy: boolean): string => (easy ? "m01_logic_drafts_easy" : "m01_logic_drafts");
const clone = <T>(v: T): T => JSON.parse(JSON.stringify(v)) as T;
function loadDrafts(): Record<string, Draft> {
function loadDrafts(easy: boolean): Record<string, Draft> {
try {
return JSON.parse(sessionStorage.getItem(CACHE_KEY) ?? "{}") as Record<string, Draft>;
return JSON.parse(sessionStorage.getItem(cacheKey(easy)) ?? "{}") as Record<string, Draft>;
} catch {
return {};
}
@@ -83,13 +82,14 @@ export async function mountM01Logic(
host: HTMLElement,
side: SideHandle,
openKey?: string,
easy = false,
): Promise<LogicHandle> {
let drafts = loadDrafts();
let drafts = loadDrafts(easy);
let files: LogicFile[] = [];
let opened: Opened | null = null;
let calcInputs = "";
let view: "flow" | "advanced" =
sessionStorage.getItem(VIEW_KEY) === "advanced" ? "advanced" : "flow";
/** 옛 컨테이너 = 호표 표 + 시험 계산 칸 · 쉽게 보기·만들기 컨테이너 = 흐름 그림 */
const view: "flow" | "advanced" = easy ? "flow" : "advanced";
const editor = el("div", { className: "m01-logic__editor" });
const errors = el("div", { className: "m01-logic__reasons", attrs: { hidden: "" } });
@@ -103,7 +103,7 @@ export async function mountM01Logic(
const persist = (): void => {
try {
sessionStorage.setItem(CACHE_KEY, JSON.stringify(drafts));
sessionStorage.setItem(cacheKey(easy), JSON.stringify(drafts));
} catch {
/* 캐시가 막혀도 화면 안의 고친 것은 남음 */
}
@@ -176,7 +176,7 @@ export async function mountM01Logic(
const listing = !opened;
list.root.hidden = !listing || !loaded;
waiting.hidden = loaded;
editor.hidden = back.hidden = viewButton.hidden = listing;
editor.hidden = back.hidden = listing;
calc.hidden = listing || view === "flow";
if (!opened) return;
if (!opened.row) {
@@ -274,8 +274,8 @@ export async function mountM01Logic(
// 왼쪽 「전체」 + 구분 → 상세구분 거름 — 고르면 목록으로 돌아와 그 범위만
filterHost.replaceChildren(
side.filter({
id: "로직|",
store: "m01.filter.로직",
id: easy ? "쉬움|" : "로직|",
store: easy ? "m01.filter.로직쉬움" : "m01.filter.로직",
subs,
total: list.total(),
subLabel: L("M01_LaborSub"),
@@ -291,7 +291,7 @@ export async function mountM01Logic(
};
/** 「새로 만들기」 모달 — 저장·본떠 저장이 끝나면 그 새 자체 로직을 흐름 그림으로 바로 엶 */
const onNew = (): void =>
const openWizard = (): void =>
openLogicWizard({
onSaved: (key) =>
void reload()
@@ -299,6 +299,44 @@ export async function mountM01Logic(
.catch(failed),
});
/** 옛 컨테이너의 「새로 만들기」 — 빈 줄 하나를 초안으로 세움(저장 때 서버가 키를 줌) */
const onDraftNew = (): void => {
const f = files.find((x) => x.file === opened?.file) ?? files[0];
if (!f) return;
const id = `new:${Date.now()}`;
// 원문번호는 절 번호만(부문은 구분 칸) · 키는 저장 때 서버가 줌
const row: LogicRow = {
: "",
: `${tx("New_Key")} ${Object.keys(drafts).length + 1}`,
: "",
: "원/",
: "",
: "공용",
: [],
: [],
: [],
: [],
끝수: null,
};
const next: Opened = {
id,
sub: subOf(f),
file: f.file,
version: f.version,
origKey: null,
row,
original: null,
prices: {},
reasons: [],
lines: null,
values: {},
};
drafts[id] = pick(next, clone(row));
persist();
show(next);
};
const onNew = easy ? openWizard : onDraftNew;
/** 「본떠 만들기」 — 정본을 자체 파일로 복제하고(계약 5장) 그 새 로직을 바로 엶 */
const onCopy = async (from: Opened): Promise<void> => {
if (!from.origKey) return;
@@ -392,23 +430,6 @@ export async function mountM01Logic(
}
};
const viewButton = createButton({
label: tx("View_Advanced"),
variant: "ghost",
onClick: () => {
view = view === "flow" ? "advanced" : "flow";
try {
sessionStorage.setItem(VIEW_KEY, view);
} catch {
/* 캐시가 막혀도 이 판 안에서는 바뀐 보기가 남음 */
}
viewButton.textContent = view === "flow" ? tx("View_Advanced") : tx("View_Flow");
editor.replaceChildren();
drawEditor();
drawCalc();
},
});
viewButton.textContent = view === "flow" ? tx("View_Advanced") : tx("View_Flow");
const saveButton = createButton({ label: tx("Bar_Save"), onClick: () => void onSave() });
const discardButton = createButton({
label: tx("Bar_Discard"),
@@ -420,7 +441,6 @@ export async function mountM01Logic(
children: [
back,
el("h2", { text: tx("Title") }),
viewButton,
pending,
createButton({ label: tx("Bar_New"), variant: "ghost", onClick: onNew }),
createButton({ label: tx("Bar_Delete"), variant: "danger", onClick: () => void onDelete() }),
@@ -440,7 +460,7 @@ export async function mountM01Logic(
],
}),
);
side.logicHost.replaceChildren(filterHost);
(easy ? side.easyHost : side.logicHost).replaceChildren(filterHost);
show(null);
showLoadingOverlay();
const openKeyOn = async (key: string): Promise<void> => {