fix(M01): 일위대가 조합 — 첫 펼침 불러오는 중 · 늦은 답 버림 · 열어 둔 상세 기억 · 로직 상세 이동 전 확인
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
@@ -65,21 +65,47 @@ export function mountM01Combo(
|
||||
let items: ComboSummary[] = [];
|
||||
let logics = new Map<string, LogicSummary>();
|
||||
let pick = { sub: "", detail: "" };
|
||||
/** 화면을 바꾼 횟수 — 늦게 온 답은 이 값이 달라졌으면 버림 */
|
||||
let nav = 0;
|
||||
/** 열어 둔 상세 — 다른 화면에 갔다 와도 그대로 돌아오게 기억 */
|
||||
let current: { combo: Combo; version: string } | null = null;
|
||||
let logicsReady: Promise<void> = Promise.resolve();
|
||||
const filterHost = el("div");
|
||||
side.comboHost.replaceChildren(filterHost);
|
||||
const view = el("div", { className: "m01-logic__editor" });
|
||||
host.replaceChildren(view);
|
||||
|
||||
const load = async (): Promise<void> => {
|
||||
/** 목록만 받음 — 로직 이름표(1300여 개)는 따로 받아 목록을 붙잡지 않음 */
|
||||
const load = async (redraw = true): Promise<void> => {
|
||||
const my = nav;
|
||||
try {
|
||||
const [combos, all] = await Promise.all([fetchCombos(), fetchLogics()]);
|
||||
items = combos;
|
||||
logics = new Map(all.map((x) => [x.키, x]));
|
||||
items = await fetchCombos();
|
||||
} catch (error) {
|
||||
showToast(error instanceof Error ? error.message : tc("Load_Failed"), "error");
|
||||
}
|
||||
drawFilter();
|
||||
drawList();
|
||||
if (redraw && my === nav) drawList();
|
||||
};
|
||||
|
||||
const loadLogics = (): void => {
|
||||
logicsReady = fetchLogics()
|
||||
.then((all) => {
|
||||
logics = new Map(all.map((x) => [x.키, x]));
|
||||
})
|
||||
.catch((error) =>
|
||||
showToast(error instanceof Error ? error.message : tc("Load_Failed"), "error"),
|
||||
);
|
||||
};
|
||||
|
||||
const show = async (): Promise<void> => {
|
||||
if (!logics.size) loadLogics();
|
||||
if (!items.length && !current) {
|
||||
view.replaceChildren(el("p", { className: "m01-logic__muted", text: tc("Loading") }));
|
||||
}
|
||||
await load(false);
|
||||
if (!current) drawList();
|
||||
await logicsReady;
|
||||
if (current) draw(current.combo, current.version);
|
||||
};
|
||||
|
||||
/* --- 왼쪽 거름 --- */
|
||||
@@ -124,6 +150,8 @@ export function mountM01Combo(
|
||||
});
|
||||
|
||||
const drawList = (): void => {
|
||||
nav++;
|
||||
current = null;
|
||||
const shown = items.filter(
|
||||
(x) => (!pick.sub || x.구분 === pick.sub) && (!pick.detail || x.상세구분 === pick.detail),
|
||||
);
|
||||
@@ -169,7 +197,10 @@ export function mountM01Combo(
|
||||
|
||||
const open = async (key: string): Promise<void> => {
|
||||
try {
|
||||
const my = ++nav;
|
||||
const one = await fetchCombo(key);
|
||||
await logicsReady;
|
||||
if (my !== nav) return;
|
||||
draw(one.combo, one.version);
|
||||
} catch (error) {
|
||||
showToast(error instanceof Error ? error.message : tc("Load_Failed"), "error");
|
||||
@@ -178,6 +209,9 @@ export function mountM01Combo(
|
||||
|
||||
/* --- 상세 --- */
|
||||
const draw = (combo: Combo, version: string): void => {
|
||||
nav++;
|
||||
current = { combo, version };
|
||||
const saved = JSON.stringify(combo);
|
||||
const field = (label: string, name: "이름" | "구분" | "상세구분" | "단위" | "비고") => {
|
||||
const f = createInputField({ type: "text", label });
|
||||
f.input.value = combo[name] ?? "";
|
||||
@@ -208,7 +242,10 @@ export function mountM01Combo(
|
||||
text: info?.이름 ?? row.로직,
|
||||
attrs: { type: "button", title: tc("Open") },
|
||||
});
|
||||
name.addEventListener("click", () => onOpenLogic(row.로직));
|
||||
name.addEventListener("click", async () => {
|
||||
if (JSON.stringify(combo) !== saved && !(await showConfirmDialog(tc("LeaveAsk")))) return;
|
||||
onOpenLogic(row.로직);
|
||||
});
|
||||
const btns = el("div", {
|
||||
className: "m01-logic__chips",
|
||||
children: [
|
||||
@@ -274,11 +311,16 @@ export function mountM01Combo(
|
||||
variant: "filled",
|
||||
onClick: async () => {
|
||||
if (!combo.이름.trim()) return showToast(tc("NeedName"), "error");
|
||||
const my = nav;
|
||||
try {
|
||||
const key = combo.키 ? combo.키 : (await newCombo(combo)).key;
|
||||
if (combo.키) await editCombo(combo.키, version, combo);
|
||||
showToast(tc("Saved"), "success");
|
||||
await load();
|
||||
await load(false);
|
||||
if (my !== nav) {
|
||||
if (current?.combo === combo) current = null; // 저장 사이에 나감 — 옛 판본을 붙들지 않음
|
||||
return;
|
||||
}
|
||||
await open(key);
|
||||
} catch (error) {
|
||||
showToast(reason(error), "error");
|
||||
@@ -335,5 +377,5 @@ export function mountM01Combo(
|
||||
drawTable();
|
||||
};
|
||||
|
||||
return { show: load };
|
||||
return { show };
|
||||
}
|
||||
|
||||
@@ -18,6 +18,11 @@ const TEXT = {
|
||||
Saved: ["저장함", "Saved"],
|
||||
Deleted: ["지움", "Deleted"],
|
||||
Stale: ["그 사이 조합 파일이 바뀜 — 다시 열어 주세요", "The file changed — reopen it"],
|
||||
Loading: ["불러오는 중…", "Loading…"],
|
||||
LeaveAsk: [
|
||||
"저장 안 한 고침이 있음 — 로직 상세로 가면 이 화면에서는 그대로 두고 옮겨 감. 갈까요?",
|
||||
"Unsaved edits — go to the logic anyway?",
|
||||
],
|
||||
NeedName: ["이름을 적어야 저장됨", "A name is required"],
|
||||
Load_Failed: ["불러오지 못함", "Load failed"],
|
||||
Head_Key: ["키", "Key"],
|
||||
|
||||
@@ -21,3 +21,7 @@ ORCA · 13-4-1 + 12-4 + 12-17-1 + 9-16-1 로 견본 조합을 끝까지 씀. 흔
|
||||
- 「돌아오기」 = 컨테이너 제목을 접었다 펴야 함 · 그때 목록을 새로 받아 열어 둔 상세가 목록으로 바뀜(저장 안 한 고침은 사라짐).
|
||||
- 로직 상세로 갈 때 저장 안 한 조합 고침을 묻지 않음.
|
||||
- PLAN.md 에 4-3·4-4 줄이 이 창 판에 없어 체크·검증완료를 적지 못함.
|
||||
|
||||
## 고친 뒤
|
||||
|
||||
- 넷 모두 고침 · ORCA 재확인: 펼치자마자 「불러오는 중」(0.1초) · 목록은 로직 이름표를 기다리지 않음 · 저장 직후 나가면 늦은 답 버림 · 다녀오면 열어 둔 상세(저장 안 한 고침 포함) 그대로 · 로직 상세로 갈 때 저장 안 한 고침이 있으면 확인창.
|
||||
|
||||
Reference in New Issue
Block a user