feat(720-3): 단가산출 · 일위대가 나무 문서 줄 [이름 바꾸기] — 읽고 이름만 갈아 저장 · 저장 뒤 상세 머리 제목 새 이름
- 열린 문서면 저장 안 한 고침 물음 뒤 새 이름으로 다시 엶 · 서버 새 길 없음 - 새 지킴 시험 test_720_m02_folder_layer(임시 storage 이름 바꾸기 왕복 · 화면 글 대조) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -595,6 +595,10 @@ export function createMain(
|
||||
sheet.recalc();
|
||||
panel.refresh(sheet.getDoc()?.열 ?? []);
|
||||
}
|
||||
// 720-3 — 서고 문서(단가산출 · 일위대가) 이름을 고쳐 저장하면 머리 제목도 새 이름(「키 이름」)
|
||||
const named = (doc as { 이름?: unknown } | null)?.이름;
|
||||
if ((at.kind === "sangeun" || isPrecombo(at.kind)) && typeof named === "string" && sel === at)
|
||||
title.textContent = at.label = named ? `${at.name} ${named}` : at.name;
|
||||
showNotice([]);
|
||||
showToast(L("M02_Saved"), "success");
|
||||
onSaved?.(); // 좌측 목록의 판을 새 판으로
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Delete 가 고른 것 전부에 · 복사는 읽을 수 있는 층 어디서나 → 쓸 수 있는 층에 붙여넣기(새 키).
|
||||
* 문서 누름 = 편집 칸 · 켜진 층 단추 다시 누름 = 목록표(페이지 몫).
|
||||
* 최상위 폴더(720-2) — 층 단추 옆 「+」(맨 위 메뉴) · 폴더 · 문서 줄 [최상위 폴더 만들기] = 같은 `addFolder([])`.
|
||||
* 문서 줄 [이름 바꾸기](720-3) — 읽고 문서 `이름` 만 갈아 저장(서버 새 길 없음) · 열린 문서면 새 이름으로 다시 엶.
|
||||
* ========================================================================== */
|
||||
|
||||
import { isCtrlKey } from "../A00_Common/key_shortcut";
|
||||
@@ -29,6 +30,8 @@ import {
|
||||
listTemplates,
|
||||
newLibrary,
|
||||
pasteLibraryTree,
|
||||
readTemplate,
|
||||
saveTemplate,
|
||||
type Layer,
|
||||
type LibraryKind,
|
||||
type PrecomboTree,
|
||||
@@ -219,6 +222,34 @@ export function buildLibrarySection(opt: LibraryOptions): LibraryHandle {
|
||||
if (got && got.name !== was) await edit({ op: "rename", 경로: path, 이름: got.name });
|
||||
};
|
||||
|
||||
/** 문서 이름 바꾸기(720-3) — 열린 문서면 저장 안 한 고침 물음 → 읽고 `이름` 만 갈아 저장 → 나무 · 상세 새 이름 */
|
||||
const renameDoc = async (row: LibraryRow): Promise<void> => {
|
||||
const at = opt.scope();
|
||||
const was = row.제목 || row.이름;
|
||||
const got = await askText(L("M02_TreeRename"), [
|
||||
{ key: "name", label: L("M02_TreeItemName"), value: was },
|
||||
]);
|
||||
const name = got?.name.trim();
|
||||
if (!name || name === was) return;
|
||||
const cur = opt.getOpen();
|
||||
const shown =
|
||||
cur?.kind === kind &&
|
||||
cur.name === row.이름 &&
|
||||
cur.layer === at.layer &&
|
||||
(cur.owner ?? null) === at.owner;
|
||||
if (shown && !(await opt.confirmLeave())) return;
|
||||
try {
|
||||
const doc = await readTemplate(at.layer, kind, row.이름, null, at.owner);
|
||||
const body = { ...(doc.문서 as Record<string, unknown>), 이름: name };
|
||||
await saveTemplate(at.layer, kind, row.이름, null, doc.판 ?? "", body, at.owner);
|
||||
showToast(L("M02_TreeDone"), "success");
|
||||
await refresh(true);
|
||||
if (shown) await open({ ...row, 제목: name });
|
||||
} catch (error) {
|
||||
showToast(L("M02_ActionFailed").replace("{value}", why(error)), "error");
|
||||
}
|
||||
};
|
||||
|
||||
/* --- 지우기 · 여럿 · 복사 붙여넣기(구조물 조합 나무 꼴) --- */
|
||||
const pickedTargets = (): Target[] =>
|
||||
[...picked].map((id) => targets.get(id)).filter((t): t is Target => !!t);
|
||||
@@ -302,6 +333,7 @@ export function buildLibrarySection(opt: LibraryOptions): LibraryHandle {
|
||||
const off = (): string | undefined => (opt.scope().canWrite ? undefined : tc("ReadOnly"));
|
||||
const single = (): TreeMenuItem[] => [
|
||||
{ label: L("M02_TreeAddTopFolder"), run: () => void addFolder([]), disabledReason: off() },
|
||||
{ label: L("M02_TreeRename"), run: () => void renameDoc(row), disabledReason: off() },
|
||||
{ label: L("M02_TreeCopy").replace("{n}", "1"), run: () => copy([target]) },
|
||||
{
|
||||
label: L("M02_Delete"),
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
"""M02 폴더 · 층 · 단가산출 이름(PLAN 720 · 내역 16) — 임시 storage + 화면 글 대조.
|
||||
|
||||
720-1 단가산출 · 일위대가 층 단추 = 저장 안 한 고침 물음 → 그 층 목록표 ·
|
||||
720-2 최상위 폴더 = 메뉴 줄 · 층 줄 「+」 같은 `addFolder([])` ·
|
||||
720-3 문서 [이름 바꾸기] = 읽고 `이름` 만 갈아 저장(층 목록 제목 새 이름 · 다른 칸 그대로) ·
|
||||
720-4 구조물 조합 층 = 제자리 바꿈(해시 이동 없음).
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from resources.tester.test_m02_precombo_folders import client # noqa: F401 — 같은 임시 층
|
||||
|
||||
M02 = Path(__file__).resolve().parents[2] / "M02_MasterTemplete"
|
||||
ITEM = "/api/m02/layers/personal/templates/{}/{}"
|
||||
|
||||
|
||||
def _text(name: str) -> str:
|
||||
return (M02 / name).read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def _rows(c: TestClient, kind: str) -> dict[str, dict]:
|
||||
rows = c.get("/api/m02/layers/personal/templates").json()["양식"]
|
||||
return {r["이름"]: r for r in rows if r["종류"] == kind}
|
||||
|
||||
|
||||
def _rename(c: TestClient, kind: str, key: str, name: str) -> None:
|
||||
"""화면 `renameDoc` 과 같은 길 — 읽고 `이름` 만 갈아 같은 판으로 저장"""
|
||||
got = c.get(ITEM.format(kind, key)).json()
|
||||
body = {"판": got.get("판") or "", "문서": {**got["문서"], "이름": name}}
|
||||
res = c.put(ITEM.format(kind, key), json=body)
|
||||
assert res.status_code == 200, res.text
|
||||
|
||||
|
||||
def test_일위대가_이름_바꾸기는_이름만(client: TestClient): # noqa: F811
|
||||
before = client.get(ITEM.format("precombo", "SC-000002")).json()["문서"]
|
||||
_rename(client, "precombo", "SC-000002", "나 고침")
|
||||
after = client.get(ITEM.format("precombo", "SC-000002")).json()["문서"]
|
||||
assert after["이름"] == "나 고침"
|
||||
assert {k: v for k, v in after.items() if k != "이름"} == {
|
||||
k: v for k, v in before.items() if k != "이름"
|
||||
}
|
||||
rows = _rows(client, "precombo")
|
||||
assert rows["SC-000002"]["제목"] == "나 고침" and rows["SC-000001"]["제목"] == "가"
|
||||
|
||||
|
||||
def test_단가산출_이름_바꾸기_목록_제목(client: TestClient): # noqa: F811
|
||||
body = {"문서": {"이름": "갑", "단위": "㎡", "바탕": None, "더한줄": []}}
|
||||
res = client.post("/api/m02/layers/personal/library/sangeun", json=body)
|
||||
assert res.status_code == 200, res.text
|
||||
key = res.json()["이름"]
|
||||
_rename(client, "sangeun", key, "을")
|
||||
assert _rows(client, "sangeun")[key]["제목"] == "을"
|
||||
assert client.get(ITEM.format("sangeun", key)).json()["문서"]["단위"] == "㎡"
|
||||
|
||||
|
||||
def test_화면_층_단추_최상위_폴더_이름_바꾸기():
|
||||
page = _text("M02_MasterTemplete_UI_Page.ts")
|
||||
lib = _text("M02_MasterTemplete_UI_Side_Library.ts")
|
||||
# 720-1 — 다른 층 단추 = 물음 뒤 목록표
|
||||
assert "setPre(next), void main.open(precomboList(pre))" in page
|
||||
assert "void main.open(sangeunList(sk))" in page.split("const skBar")[1].split("reselect:")[0]
|
||||
# 720-2 — 층 줄 「+」 = 서고 칸 맨 위 메뉴 · 메뉴 줄 = 같은 addFolder([])
|
||||
assert page.count(" add,\n") == 2
|
||||
assert "onClick: (event) => openTreeMenu(event, menuAt([]))" in lib
|
||||
assert "add.hidden = !at.canWrite;" in lib and "opt.bar(add)" in lib
|
||||
assert lib.count('L("M02_TreeAddTopFolder"), () => void addFolder([])') == 1 # 폴더 줄
|
||||
assert '{ label: L("M02_TreeAddTopFolder"), run: () => void addFolder([])' in lib # 문서 줄
|
||||
tree = _text("M02_MasterTemplete_TreePanel.ts")
|
||||
assert '{ label: L("M02_TreeAddTopFolder"), run: () => void addFolder([]) }' in tree
|
||||
# 720-3 — 문서 줄 [이름 바꾸기] · 저장 뒤 머리 제목
|
||||
assert "run: () => void renameDoc(row)" in lib
|
||||
assert "이름: name }" in lib and "saveTemplate(at.layer, kind, row.이름, null" in lib
|
||||
main = _text("M02_MasterTemplete_UI_Main.ts")
|
||||
assert "title.textContent = at.label = named ? `${at.name} ${named}` : at.name;" in main
|
||||
Reference in New Issue
Block a user