feat(M01): 「로직 쉽게 보기·만들기」·「로직 흐름 보기」 컨테이너와 계산 코드 걷음 · 정본 로직 화면 복사본 「로직 개선 시험」 컨테이너 더함
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
@@ -18,7 +18,6 @@ import {
|
||||
} from "@ui/ui_template_elements";
|
||||
import {
|
||||
ApiError,
|
||||
copyLogic,
|
||||
fetchLogic,
|
||||
fetchLogicFiles,
|
||||
fetchLogics,
|
||||
@@ -33,10 +32,7 @@ import {
|
||||
} from "./M01_MasterData_UI_Logic_Api";
|
||||
import { buildCalc } from "./M01_MasterData_UI_Logic_Calc";
|
||||
import { buildEditor } from "./M01_MasterData_UI_Logic_Edit";
|
||||
import { render as renderCView } from "./M01_MasterData_UI_Logic_CView";
|
||||
import { isOwnLogic, mountFlow } from "./M01_MasterData_UI_Logic_Flow";
|
||||
import { buildList, logicId, type ListItem, type ListMark } from "./M01_MasterData_UI_Logic_List";
|
||||
import { openLogicWizard } from "./M01_MasterData_UI_Logic_Wizard";
|
||||
import { openPicker } from "./M01_MasterData_UI_Logic_Pick";
|
||||
import type { SideHandle } from "./M01_MasterData_UI_Side";
|
||||
import { tx } from "./M01_MasterData_UI_Logic_Text";
|
||||
@@ -62,15 +58,12 @@ interface Opened extends Draft {
|
||||
values: Record<string, string>;
|
||||
}
|
||||
|
||||
/** 세 컨테이너(옛 호표 화면 · 쉽게 보기·만들기 · 흐름 보기)가 서로의 저장 안 한 것을 덮지 않게 캐시 칸을 가름 */
|
||||
export type LogicMode = "classic" | "easy" | "cview";
|
||||
const SUFFIX: Record<LogicMode, string> = { classic: "", easy: "_easy", cview: "_cview" };
|
||||
const cacheKey = (mode: LogicMode): string => `m01_logic_drafts${SUFFIX[mode]}`;
|
||||
const cacheKey = "m01_logic_drafts";
|
||||
const clone = <T>(v: T): T => JSON.parse(JSON.stringify(v)) as T;
|
||||
|
||||
function loadDrafts(mode: LogicMode): Record<string, Draft> {
|
||||
function loadDrafts(): Record<string, Draft> {
|
||||
try {
|
||||
return JSON.parse(sessionStorage.getItem(cacheKey(mode)) ?? "{}") as Record<string, Draft>;
|
||||
return JSON.parse(sessionStorage.getItem(cacheKey) ?? "{}") as Record<string, Draft>;
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
@@ -85,20 +78,11 @@ export async function mountM01Logic(
|
||||
host: HTMLElement,
|
||||
side: SideHandle,
|
||||
openKey?: string,
|
||||
mode: LogicMode = "classic",
|
||||
): Promise<LogicHandle> {
|
||||
let drafts = loadDrafts(mode);
|
||||
let drafts = loadDrafts();
|
||||
let files: LogicFile[] = [];
|
||||
let opened: Opened | null = null;
|
||||
let calcInputs = "";
|
||||
/** 옛 컨테이너 = 호표 표 + 시험 계산 칸 · 쉽게 보기·만들기 컨테이너 = 흐름 그림 */
|
||||
const easy = mode === "easy";
|
||||
const view: "flow" | "advanced" | "cview" = easy
|
||||
? "flow"
|
||||
: mode === "cview"
|
||||
? "cview"
|
||||
: "advanced";
|
||||
|
||||
const editor = el("div", { className: "m01-logic__editor" });
|
||||
const errors = el("div", { className: "m01-logic__reasons", attrs: { hidden: "" } });
|
||||
const calc = el("aside", { className: "m01-logic__calc" });
|
||||
@@ -111,7 +95,7 @@ export async function mountM01Logic(
|
||||
|
||||
const persist = (): void => {
|
||||
try {
|
||||
sessionStorage.setItem(cacheKey(mode), JSON.stringify(drafts));
|
||||
sessionStorage.setItem(cacheKey, JSON.stringify(drafts));
|
||||
} catch {
|
||||
/* 캐시가 막혀도 화면 안의 고친 것은 남음 */
|
||||
}
|
||||
@@ -146,7 +130,6 @@ export async function mountM01Logic(
|
||||
if (opened.origKey !== null && now === opened.original) delete drafts[opened.id];
|
||||
else drafts[opened.id] = pick(opened, clone(opened.row));
|
||||
persist();
|
||||
if (view !== "advanced") return; // 흐름 그림은 제 안에서 다시 셈 — 다시 그리면 펼친 상자가 접힘
|
||||
const inputs = JSON.stringify(opened.row.입력 ?? []);
|
||||
if (inputs !== calcInputs) drawCalc();
|
||||
};
|
||||
@@ -160,7 +143,7 @@ export async function mountM01Logic(
|
||||
});
|
||||
|
||||
const drawCalc = (): void => {
|
||||
if (!opened?.row || view !== "advanced") {
|
||||
if (!opened?.row) {
|
||||
calc.replaceChildren();
|
||||
return;
|
||||
}
|
||||
@@ -180,12 +163,12 @@ export async function mountM01Logic(
|
||||
};
|
||||
|
||||
const drawEditor = (): void => {
|
||||
// 고른 로직이 없으면 목록 · 있으면 흐름 그림(기본) 또는 고급 = 호표 표 + 시험 계산
|
||||
// 고른 로직이 없으면 목록 · 있으면 호표 표 + 시험 계산
|
||||
const listing = !opened;
|
||||
list.root.hidden = !listing || !loaded;
|
||||
waiting.hidden = loaded;
|
||||
editor.hidden = back.hidden = listing;
|
||||
calc.hidden = listing || view !== "advanced";
|
||||
calc.hidden = listing;
|
||||
if (!opened) return;
|
||||
if (!opened.row) {
|
||||
editor.replaceChildren(
|
||||
@@ -194,28 +177,6 @@ export async function mountM01Logic(
|
||||
return;
|
||||
}
|
||||
const current = opened;
|
||||
const shown = current.row as LogicRow;
|
||||
if (view === "cview") {
|
||||
renderCView(editor, current.origKey ?? current.id);
|
||||
return;
|
||||
}
|
||||
if (view === "flow") {
|
||||
mountFlow(editor, {
|
||||
one: {
|
||||
file: current.file,
|
||||
version: current.version,
|
||||
logic: shown,
|
||||
blocked: false,
|
||||
reasons: current.reasons,
|
||||
prices: current.prices,
|
||||
},
|
||||
row: shown,
|
||||
editable: isOwnLogic(current.file, shown),
|
||||
onChange: touch,
|
||||
onCopy: () => void onCopy(current),
|
||||
});
|
||||
return;
|
||||
}
|
||||
buildEditor(editor, {
|
||||
row: current.row as LogicRow,
|
||||
file: current.file,
|
||||
@@ -286,12 +247,8 @@ export async function mountM01Logic(
|
||||
// 왼쪽 「전체」 + 구분 → 상세구분 거름 — 고르면 목록으로 돌아와 그 범위만
|
||||
filterHost.replaceChildren(
|
||||
side.filter({
|
||||
id: easy ? "쉬움|" : mode === "cview" ? "흐름|" : "로직|",
|
||||
store: easy
|
||||
? "m01.filter.로직쉬움"
|
||||
: mode === "cview"
|
||||
? "m01.filter.로직흐름"
|
||||
: "m01.filter.로직",
|
||||
id: "로직|",
|
||||
store: "m01.filter.로직",
|
||||
subs,
|
||||
total: list.total(),
|
||||
subLabel: L("M01_LaborSub"),
|
||||
@@ -306,15 +263,6 @@ export async function mountM01Logic(
|
||||
persist();
|
||||
};
|
||||
|
||||
/** 「새로 만들기」 모달 — 저장·본떠 저장이 끝나면 그 새 자체 로직을 흐름 그림으로 바로 엶 */
|
||||
const openWizard = (): void =>
|
||||
openLogicWizard({
|
||||
onSaved: (key) =>
|
||||
void reload()
|
||||
.then(() => open(logicId("자체", key), "자체", key))
|
||||
.catch(failed),
|
||||
});
|
||||
|
||||
/** 옛 컨테이너의 「새로 만들기」 — 빈 줄 하나를 초안으로 세움(저장 때 서버가 키를 줌) */
|
||||
const onDraftNew = (): void => {
|
||||
const f = files.find((x) => x.file === opened?.file) ?? files[0];
|
||||
@@ -351,23 +299,6 @@ export async function mountM01Logic(
|
||||
persist();
|
||||
show(next);
|
||||
};
|
||||
const onNew = easy ? openWizard : onDraftNew;
|
||||
|
||||
/** 「본떠 만들기」 — 정본을 자체 파일로 복제하고(계약 5장) 그 새 로직을 바로 엶 */
|
||||
const onCopy = async (from: Opened): Promise<void> => {
|
||||
if (!from.origKey) return;
|
||||
showLoadingOverlay();
|
||||
try {
|
||||
const made = await copyLogic(from.origKey);
|
||||
await reload();
|
||||
showToast(tx("Edit_Copied", { v: made.key }), "success");
|
||||
await open(logicId("자체", made.key), "자체", made.key);
|
||||
} catch (error) {
|
||||
failed(error);
|
||||
} finally {
|
||||
hideLoadingOverlay();
|
||||
}
|
||||
};
|
||||
|
||||
const onDelete = async (): Promise<void> => {
|
||||
if (!opened?.row) return;
|
||||
@@ -457,20 +388,11 @@ export async function mountM01Logic(
|
||||
children: [
|
||||
back,
|
||||
el("h2", { text: tx("Title") }),
|
||||
// 흐름 보기 = 읽기 + 시험 계산만 — 만들기·지우기·저장 단추 없음
|
||||
...(mode === "cview"
|
||||
? []
|
||||
: [
|
||||
pending,
|
||||
createButton({ label: tx("Bar_New"), variant: "ghost", onClick: onNew }),
|
||||
createButton({
|
||||
label: tx("Bar_Delete"),
|
||||
variant: "danger",
|
||||
onClick: () => void onDelete(),
|
||||
}),
|
||||
discardButton,
|
||||
saveButton,
|
||||
]),
|
||||
pending,
|
||||
createButton({ label: tx("Bar_New"), variant: "ghost", onClick: onDraftNew }),
|
||||
createButton({ label: tx("Bar_Delete"), variant: "danger", onClick: () => void onDelete() }),
|
||||
discardButton,
|
||||
saveButton,
|
||||
],
|
||||
});
|
||||
host.replaceChildren(
|
||||
@@ -485,9 +407,7 @@ export async function mountM01Logic(
|
||||
],
|
||||
}),
|
||||
);
|
||||
({ classic: side.logicHost, easy: side.easyHost, cview: side.cviewHost })[mode].replaceChildren(
|
||||
filterHost,
|
||||
);
|
||||
side.logicHost.replaceChildren(filterHost);
|
||||
show(null);
|
||||
showLoadingOverlay();
|
||||
const openKeyOn = async (key: string): Promise<void> => {
|
||||
|
||||
Reference in New Issue
Block a user