From 45b614ca55f8a3dce32b032a1833b7eed1df7770 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 2 Oct 2026 07:43:09 +0900 Subject: [PATCH] =?UTF-8?q?fix(M01):=20=ED=9D=90=EB=A6=84=EB=8F=84=20?= =?UTF-8?q?=ED=8C=A8=EB=84=90=20=EA=B8=80=20=C2=B7=20=EC=99=BC=EC=AA=BD=20?= =?UTF-8?q?=EA=B3=A0=EC=B9=A8=20=E2=86=92=20=EC=98=A4=EB=A5=B8=EC=AA=BD=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=EA=B0=90=20=C2=B7=20=EC=8B=9C=ED=97=98?= =?UTF-8?q?=ED=8C=90=20=EA=B8=B8=20=EC=A7=80=EC=9B=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 패널 위 글 「이 로직의 계산 방법을 흐름도로 보여 줌」(「옛 식」 · 「정본」 말 지움) - 왼쪽 편집기로 고친 뒤 잠시 쉬면 고친 줄을 서버 변환(POST /api/m01/logic/flow {file, key, row})으로 다시 받아 오른쪽에 넣음 · 마지막 물음만 받음 · 길이 아직 없으면 아무 일 없음 - 다른 로직 받기에서 흐름도 시험판(DF-) cfGet 길 지움 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt --- M01_MasterData/M01_MasterData_UI_LogicLab.ts | 30 +++++++++++++++--- .../M01_MasterData_UI_LogicLab_Flow.ts | 31 ++++++++++++++++--- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/M01_MasterData/M01_MasterData_UI_LogicLab.ts b/M01_MasterData/M01_MasterData_UI_LogicLab.ts index 58be791ee..3143cfa60 100644 --- a/M01_MasterData/M01_MasterData_UI_LogicLab.ts +++ b/M01_MasterData/M01_MasterData_UI_LogicLab.ts @@ -43,6 +43,7 @@ import { emptyFlow, examplesOf, fetchFlow, + fetchFlowFor, sameShape, withExamples, mountLogicFlow, @@ -78,9 +79,11 @@ interface Opened extends Draft { values: Record; /** 흐름 로직의 왼쪽 「지금 내용」 자리 — 흐름도 왼쪽 보기가 그림 */ flowHost: HTMLElement; + /** 오른쪽 흐름도의 기준(예시값만 다른지 견줄 때) */ + flowBase?: CfLogic; } -/** 줄의 흐름(있으면 흐름도가 정본) */ +/** 줄의 흐름(있으면 흐름도로 셈) */ const flowOf = (row: LogicRow | null): CfLogic | null => (row?.흐름 as CfLogic | undefined) ?? null; const OWN_FILE = "로직_자체.json"; @@ -165,6 +168,7 @@ export async function mountM01LogicLab( if (opened.origKey !== null && now === opened.original) delete drafts[opened.id]; else drafts[opened.id] = pick(opened, clone(opened.row)); persist(); + if (!flowOf(opened.row)) scheduleFlow(opened); // 왼쪽 편집기로 고침 — 오른쪽 흐름도도 따라감 const inputs = JSON.stringify(opened.row.입력 ?? []); if (inputs !== calcInputs) drawCalc(); if (isOwnLogic(opened.row)) { @@ -241,6 +245,22 @@ export async function mountM01LogicLab( flowNow?.redrawLeft(); }; + /** 왼쪽 고침 뒤 잠시 쉬면 고친 줄을 서버가 흐름도로 바꿔 줌 → 오른쪽에 넣음 · 마지막 물음만 받음 */ + let flowTimer: number | undefined; + let flowSeq = 0; + const scheduleFlow = (o: Opened): void => { + window.clearTimeout(flowTimer); + flowTimer = window.setTimeout(() => { + const seq = ++flowSeq; + if (!o.row) return; + void fetchFlowFor(o.row, o.origKey ?? "", o.file).then((got) => { + if (!got || seq !== flowSeq || opened !== o || !flowNow || flowOf(o.row)) return; + o.flowBase = withExamples(got.flow, o.values); + flowNow.set(clone(o.flowBase)); + }); + }, 700); + }; + /** 오른쪽 흐름도 — 줄의 흐름 · 없으면 서버 변환(흐름 받기) · 그것도 없으면 빈 흐름 */ const startFlow = async (o: Opened): Promise => { const row = o.row; @@ -249,7 +269,7 @@ export async function mountM01LogicLab( const own = flowOf(row); const got = own || o.origKey === null ? null : await fetchFlow(o.origKey, o.file); // 시험 입력 한 벌 — 열 때 왼쪽 시험 계산 입력을 흐름 변수 예시값으로 - let base = withExamples( + o.flowBase = withExamples( own ?? got?.flow ?? (await emptyFlow(row.이름, row.결과단위)), o.values, ); @@ -257,7 +277,7 @@ export async function mountM01LogicLab( try { const made = await mountLogicFlow({ split, - flow: clone(base), + flow: clone(o.flowBase), title: row.이름 || row.키, readOnly: !admin, notes: got?.notes ?? [], @@ -266,9 +286,9 @@ export async function mountM01LogicLab( onChange: (lg) => { if (!o.row || opened !== o) return; const was = flowOf(o.row); - if (sameShape(lg, was ?? base)) { + if (sameShape(lg, was ?? o.flowBase ?? lg)) { // 예시값만 바뀜 — 고친 것 아님 · 왼쪽 시험 계산 입력에 같은 이름으로 넣고 다시 셈 - base = lg; + o.flowBase = lg; syncLeft(o, examplesOf(lg)); return; } diff --git a/M01_MasterData/M01_MasterData_UI_LogicLab_Flow.ts b/M01_MasterData/M01_MasterData_UI_LogicLab_Flow.ts index 55b7c00be..4642527d9 100644 --- a/M01_MasterData/M01_MasterData_UI_LogicLab_Flow.ts +++ b/M01_MasterData/M01_MasterData_UI_LogicLab_Flow.ts @@ -18,8 +18,8 @@ const TEXT = { New: ["신규 로직", "New logic"], Waiting: ["흐름도 불러오는 중", "Loading the flow diagram"], Converted: [ - "옛 식을 흐름도로 바꿔 보임 — 저장하면 흐름도가 정본", - "Converted from the old formula — saving makes the flow the master", + "이 로직의 계산 방법을 흐름도로 보여 줌", + "Shows how this logic is calculated as a flow diagram", ], Failed: ["흐름도를 못 띄움 — {v}", "Couldn't open the flow diagram — {v}"], } as const; @@ -122,7 +122,7 @@ export interface FlowAnswer { notes: string[]; } -/** 마스터 로직의 흐름 — 줄에 흐름이 있으면 그대로 · 없으면 서버가 옛 식을 바꿔 줌 · 길이 없거나 못 받으면 null */ +/** 마스터 로직의 흐름 — 줄에 흐름이 있으면 그대로 · 없으면 서버가 계산 방법을 흐름도로 바꿔 줌 · 못 받으면 null */ export async function fetchFlow(key: string, file = ""): Promise { try { const q = new URLSearchParams({ key, ...(file ? { file } : {}) }); @@ -135,6 +135,27 @@ export async function fetchFlow(key: string, file = ""): Promise { + try { + const r = await fetch(`${API_BASE_URL}/m01/logic/flow`, { + method: "POST", + credentials: "include", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ file, key, row }), + }); + if (!r.ok) return null; + const got = (await r.json()) as Partial; + return got.flow ? { flow: got.flow, converted: !!got.converted, notes: got.notes ?? [] } : null; + } catch { + return null; + } +} + const loadFlowMods = () => Promise.all([ import("../M02_MasterTemplete/DataFlow/M02_DF_Panel"), @@ -223,9 +244,9 @@ export async function mountLogicFlow(opts: LogicFlowOpts): Promise { let logicSig = ""; let gone = false; - /** 다른 로직 — 흐름도 저장본(DF-) · 마스터 로직(흐름 받기) */ + /** 다른 로직 — 마스터 로직(흐름 받기) */ const getLogic = async (key: string): Promise => - key.startsWith("DF-") ? M.api.cfGet(key) : ((await fetchFlow(key))?.flow ?? null); + (await fetchFlow(key))?.flow ?? null; const left = (): void => { const at = opts.leftHost();