Merge remote-tracking branch 'origin/sub_laptop_6' into main_laptop_1

This commit is contained in:
2026-10-16 21:48:16 +09:00
14 changed files with 34 additions and 25 deletions
@@ -1,7 +1,7 @@
/* =============================================================================
* M02_MasterTemplete_DetailNav.ts
* 상세 머리 제목과 [?] 사이 [도면][일위대가][면적산출] 묶음(105-4) — 같은 열의 다른 상세로 옮겨 감.
* 지금 상세 단추 = 숨김(113-2) · 단추 크기 = 머리 [다시 열기]와 같은 보통 · 그 열 문서가 없으면 그 단추 꺼짐(번호 목록에 번호 없음).
* 지금 상세 단추 = 숨김(113-2) · 단추 크기 = 머리 [새로고침]와 같은 보통 · 그 열 문서가 없으면 그 단추 꺼짐(번호 목록에 번호 없음).
* 보임 조건 · 옮겨 가기(confirmLeave → open)는 부른 쪽(`UI_Main`) 몫.
* ========================================================================== */
@@ -3,7 +3,7 @@
* M02 한 화면의 뿌리 셋(마스터 · 회사 · 개인 · PLAN 27-1) — 진입 해시
* `#/m02-master-template?open=구조물조합|옵션|도면&root=master|company|personal&owner=…&company=…&pre=…`
* (옛 `?layer=` 도 root 로 읽음 · `pre` = 일위대가 사전조합 층 · 134-3) · 고칠 권한(`accessFor` — 서버 `M02_Layer_Access` 와 짝 · 27-1c) ·
* 회사 없음 안내 · 회사용 읽기만일 때 [개인용으로 복사] 막대.
* 회사 없음 안내 · 회사용 읽기만일 때 [개인용 복사] 막대.
* ========================================================================== */
import { ROUTES } from "@config/config_frontend";
@@ -201,7 +201,7 @@ export interface ReadOnlyBar {
setColumn: (column: string | null) => void;
}
/** 회사용을 읽기만 볼 때 — 안내 + [개인용으로 복사](고른 열 하나를 내 개인 층으로) */
/** 회사용을 읽기만 볼 때 — 안내 + [개인용 복사](고른 열 하나를 내 개인 층으로) */
export function buildReadOnlyBar(user: SessionUser | null): ReadOnlyBar {
let column: string | null = null;
const copy = createButton({ label: L("M02_CopyToPersonal"), variant: "filled", size: "sm" });
@@ -2,7 +2,7 @@
* M02_MasterTemplete_Practice.ts
* 좌측 「실무문서」 컨테이너(구조물 조합 밖 · 도면 양식 아래 · 마스터 층 · 시스템 관리자만 · PLAN 39-2) —
* 줄 하나를 누르면 메인 칸에 옛 마스터 일위대가 조합(`일위대가조합.json` 통째) 목록 · 상세(읽기만) ·
* [통째 지우기](물음 두 번 → `DELETE /api/m02/practice` · 서버도 시스템 관리자 · 쓰는 곳 0 확인).
* [전체 삭제](물음 두 번 → `DELETE /api/m02/practice` · 서버도 시스템 관리자 · 쓰는 곳 0 확인).
* 읽기 = 마스터 조합 길(`Labor_Api`) · 복사 원본 고르기는 목록 화면(ItemList)의 실무문서 탭 몫.
* ========================================================================== */
@@ -255,7 +255,7 @@ export function createMain(
nav.update(back.hidden ? null : sel); // 보임 조건 = 뒤로 단추와 같음
const kind = sel && sel.kind in HELP_TITLE ? (sel.kind as keyof typeof HELP_TITLE) : null;
const help = kind && helpParts(HELP_TITLE[kind], detailHelp(kind)); // 열 때마다 새로 — 상자는 닫힘
help?.button.classList.remove("ui-btn--sm"); // 머리 [?] = [다시 열기]와 같은 보통 크기(113-2 · 카드 [?]는 작게)
help?.button.classList.remove("ui-btn--sm"); // 머리 [?] = [새로고침]와 같은 보통 크기(113-2 · 카드 [?]는 작게)
helpSlot.replaceChildren(...(help ? [help.button, help.box] : []));
};
@@ -72,7 +72,7 @@ export async function renderM02MasterTemplate(root: HTMLElement): Promise<Mounte
}
setActiveLayer(layer, owner);
const isAdmin = accessFor(layer, owner, user).canWrite; // 이 층을 고칠 수 있나(하위 부품의 「관리자」)
// 자기 회사용을 읽기만 볼 때 — 안내 + [개인용으로 복사]
// 자기 회사용을 읽기만 볼 때 — 안내 + [개인용 복사]
const readOnlyBar = layer === "company" && !owner && !isAdmin ? buildReadOnlyBar(user) : null;
// 고른 층의 나무 — 고르기 모달과 같은 폴더 나무 · 좌측 「구조물 조합」 줄 아래
// 「〇〇로 복사…」(30-2 · 39-6) — 쓸 수 있는 층 전부(마스터 · 볼 회사 · 내 개인 · 지금 보는 층은 뺌) ·
@@ -159,7 +159,10 @@ Promise.resolve(
out.second = { count: snap.snapOldStations(again), toasts: [...toasts] };
// ④ 계획고 편집 따라감 — 한꺼번에 여럿 옮김(사라진 둘 · 새 둘)
const alignment = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Alignment.ts");
const pivot = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Pivot.ts");
const alignment = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Alignment.ts", {
"./B05_Profile_UI_Profile_Pivot": pivot,
});
const gone = [
{ id: "pipe-80.17", chainage_m: 80.17 },
{ id: "pipe-263.31", chainage_m: 263.31 },
@@ -20,7 +20,10 @@ function loadTs(file, stubs = {}) {
return box.exports;
}
const alignment = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Alignment.ts");
const pivot = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Pivot.ts");
const alignment = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Alignment.ts", {
"./B05_Profile_UI_Profile_Pivot": pivot,
});
const ground = loadTs("B05_Profile/logic/B05_Profile_Grade_Ground.ts", {
"./B05_Profile_UI_Profile_Alignment": alignment,
});
+4 -1
View File
@@ -21,7 +21,10 @@ function loadTs(file, stubs = {}) {
}
const curve = loadTs("B05_Profile/logic/B05_Profile_UI_RouteEdit_Curve.ts");
const alignment = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Alignment.ts");
const pivot = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Pivot.ts");
const alignment = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Alignment.ts", {
"./B05_Profile_UI_Profile_Pivot": pivot,
});
const carry = loadTs("B05_Profile/logic/B05_Profile_Grade_Carry.ts", {
"./B05_Profile_UI_Profile_Alignment": alignment,
});
+1 -1
View File
@@ -100,7 +100,7 @@ def _text(name: str) -> str:
def test_단추_셋_묶음(run):
assert run["labels"] == ["도면", "일위대가", "면적산출"]
assert "ui-btn-group" in run["className"]
assert run["sizes"] == [None, None, None] # 113-2 — [다시 열기]와 같은 보통 크기
assert run["sizes"] == [None, None, None] # 113-2 — [새로고침]와 같은 보통 크기
def test_지금_것_숨김_없는_문서_꺼짐(run):
@@ -124,7 +124,7 @@
btn(im, "취소")?.click();
await W(300);
// ⑦ 버림
btn($(".m02-main__head"), "다시 열기").click();
btn($(".m02-main__head"), "새로고침").click();
await W(600);
btn($(".ui-confirm"), "확인")?.click();
await W(4500);
@@ -1,7 +1,7 @@
/* 44-6 일위대가 단가산출 로직 표 — `orca eval --expression "$(cat 이 파일)"` 한 번에 돎 · JSON 돌려줌.
*
* 시작 화면: M02 마스터 템플릿(시스템 관리자) · 좌측 일위대가 줄 「SU-000003」 이 보이는 상태 ·
* 첫 로직 줄(GF000219 돌쌓기)에 고르기 입력이 있음 · 저장은 안 함(끝에 [다시 열기] → 확인 → 버림).
* 첫 로직 줄(GF000219 돌쌓기)에 고르기 입력이 있음 · 저장은 안 함(끝에 [새로고침] → 확인 → 버림).
* 기대 수치:
* heads[2] = "별칭" · alias.inputs = 줄 수 · foot.totalBorder ≥ 1.5(2px · 화면 배율) · foot.totalBg ≠ 다른 금액 칸 배경
* opt.row = true · opt.colspan = 3(구분 ~ 별칭 · 나머지 칸은 위 줄 rowspan 2 · 113-1) · opt.nowrap = "nowrap" · opt.ellipsis = "ellipsis" ·
@@ -87,7 +87,7 @@
await W(3500);
out.alias.kept = $("[data-labor-alias]", first())?.value;
// 버림
btn($(".m02-main__head"), "다시 열기").click();
btn($(".m02-main__head"), "새로고침").click();
await W(600);
btn($(".ui-confirm"), "확인")?.click();
await W(4500);
@@ -115,7 +115,7 @@
await until(() => document.querySelector(".ui-item-picker"));
await w(600);
const modal = document.querySelector(".ui-item-picker");
const ctl = ["닫기", "개인용으로 복사", "관리 화면에서 고치기", "해제", "적용"];
const ctl = ["닫기", "개인용 복사", "관리 화면 편집", "해제", "적용"];
const leaf = [...modal.querySelectorAll("button")].find((b) => {
const t = b.textContent.trim();
return t && !/^[▸▾]/.test(t) && !ctl.includes(t);
@@ -68,7 +68,7 @@
await w(600);
const modal = document.querySelector(".ui-item-picker");
// 끝 항목 = ▸ · ▾ 로 안 시작하고 모달 조작 단추가 아닌 첫 단추
const ctl = ["닫기", "개인용으로 복사", "관리 화면에서 고치기", "해제", "적용"];
const ctl = ["닫기", "개인용 복사", "관리 화면 편집", "해제", "적용"];
const leaf = [...modal.querySelectorAll("button")].find((b) => {
const t = b.textContent.trim();
return t && !/^[▸▾]/.test(t) && !ctl.includes(t);
+10 -10
View File
@@ -11,11 +11,11 @@ export const ui_locales_m2 = {
M02_KindTable: ["구조물 조합", "Structure combos"],
M02_KindDrawing: ["도면 양식", "Drawing templates"],
M02_NoTemplates: ["양식 없음", "None"],
M02_New: ["새로", "New"],
M02_Copy: ["본떠 만들기", "Copy as new"],
M02_New: ["신규", "New"],
M02_Copy: ["복제", "Duplicate"],
M02_Delete: ["지우기", "Delete"],
M02_NewTitle: ["새 양식", "New template"],
M02_CopyTitle: ["본떠 만들기", "Copy as new"],
M02_CopyTitle: ["복제", "Duplicate"],
M02_Kind: ["종류", "Kind"],
M02_Name: ["이름", "Name"],
M02_Create: ["만들기", "Create"],
@@ -31,7 +31,7 @@ export const ui_locales_m2 = {
M02_Created: ["만들었음", "Created"],
M02_Saved: ["저장했음", "Saved"],
M02_Save: ["저장", "Save"],
M02_Reload: ["다시 열기", "Reopen"],
M02_Reload: ["새로고침", "Refresh"],
M02_PickTemplate: ["왼쪽에서 양식을 고를 것", "Pick a template on the left"],
M02_ReadOnlyLayer: ["이 층은 여기서 고치지 못함", "This layer is read-only here"],
@@ -61,7 +61,7 @@ export const ui_locales_m2 = {
"회사용은 복사해서 씀 — 고치려면 고른 열을 개인용으로 복사",
"Company templates are read-only — copy the selected column to your own to edit",
],
M02_CopyToPersonal: ["개인용으로 복사", "Copy to personal"],
M02_CopyToPersonal: ["개인용 복사", "Copy to personal"],
M02_CopyPickFirst: ["복사할 열을 구조물 조합에서 먼저 고름", "Pick a column in the table first"],
M02_CopyDone: ["개인용으로 복사함 — 「개인」 뿌리에서 고침", "Copied — edit it in your own"],
M02_CopyFailed: ["복사 안 됨 — {value}", "Copy failed — {value}"],
@@ -69,7 +69,7 @@ export const ui_locales_m2 = {
"회사부터 등록해야 구조물 조합을 만들 수 있음",
"Register a company first to manage structure templates",
],
M02_NeedCompanyGo: ["회사 등록하러 가기", "Go to company registration"],
M02_NeedCompanyGo: ["회사 등록", "Company registration"],
M02_PickerTitle: ["구조물 조합 선택", "Select structure combo"],
M02_PickerSearch: ["항목 찾기", "Search items"],
M02_PickerSystem: ["마스터", "Master"],
@@ -88,8 +88,8 @@ export const ui_locales_m2 = {
M02_PickerNone: ["없음", "None"],
M02_PickerApply: ["적용", "Apply"],
M02_PickerClear: ["해제", "Clear"],
M02_PickerCopy: ["개인용으로 복사", "Copy to personal"],
M02_PickerManage: ["관리 화면에서 고치기", "Edit in manager"],
M02_PickerCopy: ["개인용 복사", "Copy to personal"],
M02_PickerManage: ["관리 화면 편집", "Edit in manager"],
M02_PickerCopied: ["개인용으로 복사했음", "Copied to personal"],
M02_PickerCopyFailed: ["복사 못 함 — {value}", "Copy failed — {value}"],
M02_TreeTitle: ["구조물 라이브러리", "Structure library"],
@@ -176,8 +176,8 @@ export const ui_locales_m2 = {
M02_PracticeOrder: ["차례", "No."],
M02_PracticeMemo: ["메모", "Memo"],
M02_PracticeNoLogic: ["단가산출 로직 없음", "No logics"],
M02_PracticeBack: ["목록으로", "Back to list"],
M02_PracticeDrop: ["통째 지우기", "Delete all"],
M02_PracticeBack: ["목록", "List"],
M02_PracticeDrop: ["전체 삭제", "Delete all"],
M02_PracticeDropAsk: ["실무문서 {n}개를 모두 지울까요?", "Delete all {n} practice documents?"],
M02_PracticeDropAsk2: [
"되돌릴 수 없음 — 정말 통째로 지울까요?",