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

This commit is contained in:
2026-10-26 18:19:35 +09:00
4 changed files with 35 additions and 17 deletions
@@ -27,7 +27,7 @@ import {
stageFlow,
stageProgress,
} from "./B01_Dashboard_UI_ProjectList_Parts";
import { rowMenuItems } from "./B01_Dashboard_UI_ProjectList_Views";
import { openMenuAt, rowMenuItems } from "./B01_Dashboard_UI_ProjectList_Views";
/** 인쇄 · 내려받기 화면(B09 초안 길) — 도면 PDF · 수량 · 내역 xlsx 를 받는 곳 */
const PRINT_ROUTE = "b09-print" as RoutePath;
@@ -214,9 +214,7 @@ function head(detail: ProjectDetail, ctx: ProjectViewCtx): HTMLElement {
more.hidden = !items.length;
more.addEventListener("click", (event) => {
event.stopPropagation();
const box = bar.getBoundingClientRect();
const at = more.getBoundingClientRect();
menu.open(at.right - box.left - 120, at.bottom - box.top, items);
openMenuAt(menu, bar, more, items);
});
const work = createButton({
label: names.length ? `작업 열기 — ${names[current] ?? names[0]} ›` : "작업 열기 ›",
@@ -3,7 +3,11 @@
* 프로젝트 목록 보기 셋(PLAN 548-1) — 카드 · 표(줄 끝 ⋯) · 칸반(칸 = 서버 `board` · 완료 칸 접힘).
* ========================================================================== */
import { createMapContextMenu, type MapContextMenuItem } from "@ui/ui_template_context_menu";
import {
createMapContextMenu,
type MapContextMenu,
type MapContextMenuItem,
} from "@ui/ui_template_context_menu";
import { el } from "@ui/ui_template_elements_base";
import type { DashboardUser } from "./B01_Dashboard_Api_Fetch";
import type { OverviewProject } from "./B01_Dashboard_Api_Projects";
@@ -82,6 +86,19 @@ export function cardGrid(projects: OverviewProject[], onOpen: Open): HTMLElement
});
}
/** ⋯ 메뉴 — 오른쪽 끝을 단추 오른쪽에 맞춤(host = position 기준 · 메뉴 element 를 품은 것) */
export function openMenuAt(
menu: MapContextMenu,
host: HTMLElement,
button: HTMLElement,
items: MapContextMenuItem[],
): void {
const box = host.getBoundingClientRect();
const at = button.getBoundingClientRect();
menu.open(at.right - box.left, at.bottom - box.top, items);
menu.element.style.left = `${at.right - box.left - menu.element.offsetWidth}px`;
}
/** 줄 끝 ⋯ — 지금 있는 동작만(수정 · 삭제) */
export function rowMenuItems(project: OverviewProject, me: DashboardUser): MapContextMenuItem[] {
const items: MapContextMenuItem[] = [];
@@ -116,9 +133,7 @@ export function projectRows(
if (!items.length) more.hidden = true;
more.addEventListener("click", (event) => {
event.stopPropagation();
const box = wrap.getBoundingClientRect();
const at = more.getBoundingClientRect();
menu.open(at.right - box.left - 120, at.bottom - box.top, items);
openMenuAt(menu, wrap, more, items);
});
const row = el("tr", {
className: "b01-proj-row",
+4 -2
View File
@@ -12,6 +12,8 @@ import type { IconName } from "@ui/ui_template_icons";
import type { DashboardUser } from "./B01_Dashboard_Api_Fetch";
import { mountLegacy, type LegacyKey } from "./B01_Dashboard_UI_Legacy";
import { mountChat, mountGuide } from "./B01_Dashboard_UI_Nav";
import { mountProjectDetail } from "./B01_Dashboard_UI_ProjectDetail";
import { mountProjectList } from "./B01_Dashboard_UI_ProjectList";
/** 역할 넷 — 산림청 = 세션 is_forest_service 인 일반 사용자(회사 관리자 · 시스템 관리자가 앞섬) */
export type ViewRole = "USER" | "ADMIN" | "FOREST" | "SYSTEM_ADMIN";
@@ -47,8 +49,8 @@ const legacy =
export const VIEWS: View[] = [
// 작업
{ key: "home", title: "홈", group: "work", roles: ALL, icon: "home", mount: legacy("projects") },
{ key: "projects", title: "프로젝트", group: "work", roles: ALL, icon: "folder", mount: legacy("projects") },
{ key: "project", title: "프로젝트 상세", group: "hidden", roles: ALL, mount: legacy("projects") },
{ key: "projects", title: "프로젝트", group: "work", roles: ALL, icon: "folder", mount: mountProjectList },
{ key: "project", title: "프로젝트 상세", group: "hidden", roles: ALL, mount: mountProjectDetail },
{ key: "assigned", title: "받은 예정노선", group: "work", roles: ["USER", "ADMIN", "SYSTEM_ADMIN"], icon: "route", mount: legacy("assigned") },
{ key: "temp", title: "임시 보관함", group: "work", roles: ALL, icon: "archive", mount: legacy("temp") },
// 회사 관리
+10 -7
View File
@@ -24,13 +24,16 @@ def _text(path: Path) -> str:
def _run_views(body: str) -> object:
src = re.sub(r"^import[\s\S]*?;\n", "", _text(B01 / "B01_Dashboard_UI_Views.ts"), flags=re.M)
script = (
"const mountLegacy = () => {}; const mountGuide = () => {}; const mountChat = () => {};\n"
+ src
+ "\n"
+ body
)
text = _text(B01 / "B01_Dashboard_UI_Views.ts")
src = re.sub(r"^import[\s\S]*?;\n", "", text, flags=re.M)
# 가져온 값(mount 함수 등)은 빈 함수로 — 페이지가 늘어도 시험은 그대로
names = {
name.strip()
for block in re.findall(r"^import \{([^}]*)\} from", text, flags=re.M)
for name in block.split(",")
if name.strip() and not name.strip().startswith("type ")
}
script = "".join(f"const {name} = () => {{}};\n" for name in sorted(names)) + src + "\n" + body
result = subprocess.run(
["node", "--experimental-strip-types", "--no-warnings",
"--input-type=module-typescript", "-e", script],