feat(M02): 서고 구분 · 상세구분 · 연결 화면(51-12)
- 서고 목록표에 구분 · 상세구분 칸(키 뒤) - 단가승격 상세 정보 칸 = 구분 · 상세구분 · 연결(원 로직 키 · 출처) 읽기만 - 사전조합 상세 정보 칸 = 구분 · 상세구분 고르기(로직 구분 목록 logic-lib/subs · 구분 바꾸면 상세구분 = 그 첫 줄) - 새 사전조합 처음 값 = 로직 구분 목록 첫 줄 - 로직 고르기 창 구분 · 상세구분 거름에 승격 · 사전조합 줄도 걸림 - 화면 시나리오 51-12 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,142 @@
|
||||
/* 51-12 — 서고(단가승격 · 사전조합) 구분 · 상세구분 · 연결
|
||||
* 시작 화면: M02 마스터 템플릿 · 마스터 층(#/m02-master-template?open=일위대가조합&root=master) · 시스템 관리자
|
||||
* 부르는 법: orca eval --expression "$(cat 이 파일)" (async 식 · JSON 글을 돌려줌)
|
||||
* 쓰기 없음 — GET · calc 밖 요청은 가짜 400(새로 만들기 POST 본문만 잡음) · 마스터 SP-000001 · SC-000001 읽기만
|
||||
* 기대 수치:
|
||||
* - heads = ["", "키", "구분", "상세구분", "이름", ...] · listSP = SP-000001 줄 구분 · 상세구분 빈 것 아님
|
||||
* - promote = { 구분, 상세구분, 연결 } 셋 다 글 있음 · readOnly = true
|
||||
* - precombo = { selects: 2, 구분 = 문서 값 · subOptions ≥ 1 } · switched.상세구분 = 바꾼 구분의 첫 상세구분 · back = 처음 값
|
||||
* - created = { 구분: 목록 첫 구분, 상세구분: 그 첫 상세구분 }
|
||||
* - picker = 구분(SP-000001 것) 고르면 SP-000001 줄 보임 · 다른 구분이면 안 보임
|
||||
*/
|
||||
(async () => {
|
||||
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
const until = async (fn, ms = 6000) => {
|
||||
for (let t = 0; t < ms; t += 100) {
|
||||
const v = fn();
|
||||
if (v) return v;
|
||||
await wait(100);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const json = (body, status = 200) =>
|
||||
new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" } });
|
||||
const real = window.fetch;
|
||||
let posted = null;
|
||||
window.fetch = async (input, init = {}) => {
|
||||
const url = String(input?.url ?? input);
|
||||
const method = (init.method ?? "GET").toUpperCase();
|
||||
if (method === "POST" && url.includes("/library/precombo")) {
|
||||
posted = JSON.parse(init.body);
|
||||
return json({ detail: "시험 — 쓰기 막음" }, 400);
|
||||
}
|
||||
if (method !== "GET" && !url.includes("/calc"))
|
||||
return json({ detail: "시험 — 쓰기 막음" }, 400);
|
||||
return real(input, init);
|
||||
};
|
||||
const sec = [...document.querySelectorAll(".m02-side__lists > section")].find(
|
||||
(s) => s.querySelector(".ui-collapsible__title")?.textContent?.trim() === "일위대가 조합",
|
||||
);
|
||||
const items = [...sec.querySelectorAll("[data-precombo]")];
|
||||
const list = (kind) => document.querySelector(`[data-lib-list="${kind}"]`);
|
||||
const edit = () => document.querySelector(".m02-main__host [data-labor]");
|
||||
const field = (name) => edit()?.querySelector(`[data-labor-field="${name}"]`);
|
||||
const btn = (root, text) =>
|
||||
[...(root?.querySelectorAll("button") ?? [])].find((b) => b.textContent === text);
|
||||
const subs = await (
|
||||
await real("/api/m02/logic-lib/subs", { credentials: "include" })
|
||||
)
|
||||
.json()
|
||||
.then((d) => d.subs);
|
||||
try {
|
||||
// ① 승격 목록 · 상세
|
||||
items[0].click();
|
||||
const spRow = await until(() => list("promote")?.querySelector('[data-lib-key="SP-000001"]'));
|
||||
const heads = [...list("promote").querySelectorAll("thead th")].map((th) => th.textContent);
|
||||
const cells = [...spRow.querySelectorAll("td")].map((td) => td.textContent);
|
||||
const listSP = { 구분: cells[2], 상세구분: cells[3] };
|
||||
spRow.querySelector("td:nth-child(5)").click();
|
||||
await until(() => field("연결"));
|
||||
const val = (name) => field(name)?.querySelector("input")?.value ?? null;
|
||||
const promote = {
|
||||
구분: val("구분"),
|
||||
상세구분: val("상세구분"),
|
||||
연결: val("연결"),
|
||||
readOnly: ["구분", "상세구분", "연결"].every(
|
||||
(n) => field(n)?.querySelector("input")?.readOnly,
|
||||
),
|
||||
};
|
||||
// ② 사전조합 상세 — 고르기 · 바꿔 보고 되돌림
|
||||
items[1].click();
|
||||
(
|
||||
await until(() =>
|
||||
list("precombo")?.querySelector('[data-lib-key="SC-000001"] td:nth-child(5)'),
|
||||
)
|
||||
).click();
|
||||
const subSel = await until(() => field("구분")?.querySelector("select"));
|
||||
await until(() => subSel.options.length > 1, 4000);
|
||||
const detSel = field("상세구분").querySelector("select");
|
||||
const start = { 구분: subSel.value, 상세구분: detSel.value };
|
||||
const precombo = {
|
||||
selects: edit().querySelectorAll(".m01lab__info select").length,
|
||||
...start,
|
||||
subOptions: subSel.options.length,
|
||||
};
|
||||
const other = subs.find((s) => s.name !== start.구분 && s.details.length);
|
||||
subSel.value = other.name;
|
||||
subSel.dispatchEvent(new Event("change"));
|
||||
const switched = {
|
||||
구분: field("구분").querySelector("select").value,
|
||||
상세구분: field("상세구분").querySelector("select").value,
|
||||
want: other.details[0],
|
||||
};
|
||||
const s2 = field("구분").querySelector("select");
|
||||
s2.value = start.구분;
|
||||
s2.dispatchEvent(new Event("change"));
|
||||
const d2 = field("상세구분").querySelector("select");
|
||||
d2.value = start.상세구분;
|
||||
d2.dispatchEvent(new Event("change"));
|
||||
const back = {
|
||||
구분: field("구분").querySelector("select").value,
|
||||
상세구분: field("상세구분").querySelector("select").value,
|
||||
};
|
||||
// ③ 고르기 창 — 구분 거름에 승격 줄
|
||||
btn(edit(), "로직 추가").click();
|
||||
const pick = await until(() => document.querySelector(".m01-logic__pick"));
|
||||
await until(() => pick.querySelector('[data-pick="SP-000001"]'), 20000);
|
||||
const [fSub, fDet] = pick.querySelectorAll(".ui-find__filters select");
|
||||
const pickerWith = async (s, d) => {
|
||||
fSub.value = s;
|
||||
fSub.dispatchEvent(new Event("change"));
|
||||
if (d != null) {
|
||||
fDet.value = d;
|
||||
fDet.dispatchEvent(new Event("change"));
|
||||
}
|
||||
await wait(100);
|
||||
return !!pick.querySelector('[data-pick="SP-000001"]');
|
||||
};
|
||||
const picker = {
|
||||
same: await pickerWith(promote.구분, promote.상세구분),
|
||||
other: await pickerWith(subs.find((s) => s.name !== promote.구분)?.name ?? "", null),
|
||||
};
|
||||
pick.closest(".m01-logic__backdrop")?.remove();
|
||||
// ④ 새 사전조합 처음 값(POST 본문만 잡음)
|
||||
items[1].click();
|
||||
await until(() => list("precombo"));
|
||||
btn(document.querySelector(".m02-main__slot"), "생성").click();
|
||||
const box = await until(() => document.querySelector(".m02-modal"));
|
||||
box.querySelector("input").value = "시험 51-12";
|
||||
btn(box, "생성").click();
|
||||
await until(() => posted);
|
||||
btn(document.querySelector(".m02-modal"), "닫기")?.click();
|
||||
document.querySelector(".m02-modal")?.closest(".ui-modal__backdrop, .ui-modal")?.remove();
|
||||
const created = {
|
||||
구분: posted?.문서?.구분,
|
||||
상세구분: posted?.문서?.상세구분,
|
||||
want: [subs[0].name, subs[0].details[0] ?? null],
|
||||
};
|
||||
return JSON.stringify({ heads, listSP, promote, precombo, switched, back, picker, created });
|
||||
} finally {
|
||||
window.fetch = real;
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user