feat(M01): 로직 상세 오른쪽 흐름도 패널 잇기 · 신규 로직
- 로직을 열면 오른쪽에 흐름도 패널(mountFlowPanel) — 줄의 흐름 · 없으면 흐름 받기(/api/m01/logic/flow) · 없으면 빈 흐름 · 변환 알림 한 줄 - 패널 고침 → 줄의 흐름으로 drafts(캐시) · [저장] 한 번 · [고친 것 버리기] 함께 · 흐름 로직이면 왼쪽 보기(buildFlowView) 다시 - 패널 너비 = 패널이 알린 px(두 칸 끌기 막대는 숨김) - 「신규 로직」 — 기본정보 칸(이름 · 원문번호 · 결과 단위 · 출처) + 빈 흐름 → 로직_자체 새 줄 · 저장 뒤 새 키 열기 - 보기만 = 시스템 관리자가 아니면 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
/* =============================================================================
|
||||
* M01_MasterData_UI_LogicLab_Basic.ts
|
||||
* 「신규 로직」 기본정보 칸 — 이름 · 원문번호 · 결과 단위 · 출처(구분 = 자체). 확인하면 값을 돌려줌 · 취소면 null
|
||||
* 저장은 하지 않음 — 부른 쪽이 빈 흐름도와 함께 캐시(drafts)에 새 줄로 넣고 [저장] 때 서버가 GX 새 키를 줌.
|
||||
* ========================================================================== */
|
||||
|
||||
import { currentLanguageIndex } from "@ui/ui_template_locale";
|
||||
import { createButton, createInputField, createSelectField, el } from "@ui/ui_template_elements";
|
||||
import { openModal } from "@ui/ui_template_modal";
|
||||
|
||||
const TEXT = {
|
||||
Title: ["신규 로직", "New logic"],
|
||||
Name: ["이름", "Name"],
|
||||
NamePh: ["예: 돌쌓기 메쌓기(인력)", "e.g. dry stone wall (labour)"],
|
||||
Number: ["원문번호", "Source number"],
|
||||
NumberPh: ["예: 13-4-1 · 없으면 비움", "e.g. 13-4-1 · may be empty"],
|
||||
Unit: ["결과 단위", "Result unit"],
|
||||
Source: ["출처", "Source"],
|
||||
Ok: ["만들기", "Create"],
|
||||
Cancel: ["취소", "Cancel"],
|
||||
Close: ["닫기", "Close"],
|
||||
NeedName: ["이름을 넣어 주세요", "Enter a name"],
|
||||
} as const;
|
||||
|
||||
const tb = (key: keyof typeof TEXT): string => TEXT[key][currentLanguageIndex === 1 ? 1 : 0];
|
||||
|
||||
export interface BasicInfo {
|
||||
이름: string;
|
||||
원문번호: string;
|
||||
결과단위: string;
|
||||
출처: string;
|
||||
}
|
||||
|
||||
/** 기본정보 묻기 — units = 결과 단위 후보(마스터 로직의 결과단위) */
|
||||
export function askBasicInfo(units: string[]): Promise<BasicInfo | null> {
|
||||
return new Promise((resolve) => {
|
||||
let done = false;
|
||||
const finish = (v: BasicInfo | null): void => {
|
||||
if (done) return;
|
||||
done = true;
|
||||
resolve(v);
|
||||
};
|
||||
const name = createInputField({ label: tb("Name"), placeholder: tb("NamePh"), compact: true });
|
||||
const number = createInputField({
|
||||
label: tb("Number"),
|
||||
placeholder: tb("NumberPh"),
|
||||
compact: true,
|
||||
});
|
||||
const unit = createSelectField({
|
||||
label: tb("Unit"),
|
||||
options: units.map((u) => ({ value: u, text: u })),
|
||||
value: units[0] ?? "원",
|
||||
compact: true,
|
||||
});
|
||||
const source = createInputField({ label: tb("Source"), value: "자체", compact: true });
|
||||
const why = el("p", { className: "m01-logic__muted", attrs: { role: "alert" } });
|
||||
const modal = openModal({
|
||||
title: tb("Title"),
|
||||
closeLabel: tb("Close"),
|
||||
beforeClose: () => {
|
||||
finish(null);
|
||||
return true;
|
||||
},
|
||||
mount: (body, close) => {
|
||||
const ok = (): void => {
|
||||
const 이름 = name.input.value.trim();
|
||||
if (!이름) {
|
||||
why.textContent = tb("NeedName");
|
||||
return void name.input.focus();
|
||||
}
|
||||
finish({
|
||||
이름,
|
||||
원문번호: number.input.value.trim(),
|
||||
결과단위: unit.select.value,
|
||||
출처: source.input.value.trim() || "자체",
|
||||
});
|
||||
close();
|
||||
};
|
||||
name.input.addEventListener("keydown", (ev) => ev.key === "Enter" && ok());
|
||||
body.append(
|
||||
name.root,
|
||||
number.root,
|
||||
unit.root,
|
||||
source.root,
|
||||
why,
|
||||
el("div", {
|
||||
className: "m01-logic__bar",
|
||||
children: [
|
||||
createButton({ label: tb("Ok"), size: "sm", onClick: ok }),
|
||||
createButton({
|
||||
label: tb("Cancel"),
|
||||
variant: "ghost",
|
||||
size: "sm",
|
||||
onClick: () => {
|
||||
finish(null);
|
||||
modal.close();
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user