diff --git a/B01_Dashboard/B01_Dashboard_UI_Shell.ts b/B01_Dashboard/B01_Dashboard_UI_Shell.ts index 69836fcd5..83b1edd6c 100644 --- a/B01_Dashboard/B01_Dashboard_UI_Shell.ts +++ b/B01_Dashboard/B01_Dashboard_UI_Shell.ts @@ -25,7 +25,7 @@ import { openCompanyPicker } from "./B01_Dashboard_UI_CompanyPick"; import { buildKnowledgeChat } from "./B01_Dashboard_UI_Knowledge_Chat"; import { ADMIN_COMPANY_KEY, pickAdminCompany } from "./B01_Dashboard_UI_Legacy"; import { buildNav, viewHref, type DashboardNav } from "./B01_Dashboard_UI_Nav"; -import { roleOf, viewFor, VIEWS, type ViewCtx } from "./B01_Dashboard_UI_Views"; +import { roleOf, VIEW_HELP, viewFor, VIEWS, type ViewCtx } from "./B01_Dashboard_UI_Views"; import "./B01_Dashboard_UI_Style_Shell.css"; function iconButton(name: IconName, label: string, onClick?: () => void): HTMLButtonElement { @@ -305,6 +305,8 @@ export async function mountDashboardShell( const host = document.createElement("div"); host.className = "b01-shell__page"; host.dataset.view = view.key; + const topic = VIEW_HELP[view.key]; + if (topic) host.dataset.helpTopic = topic; main.replaceChildren(host); void Promise.resolve(view.mount(host, ctx)).then((done) => { if (typeof done !== "function") return; diff --git a/B01_Dashboard/B01_Dashboard_UI_Style_Shell.css b/B01_Dashboard/B01_Dashboard_UI_Style_Shell.css index d519e1dda..9c69176fc 100644 --- a/B01_Dashboard/B01_Dashboard_UI_Style_Shell.css +++ b/B01_Dashboard/B01_Dashboard_UI_Style_Shell.css @@ -199,6 +199,10 @@ html:has(.b01-shell) .app-header { .b01-shell__menu-touch { cursor: default; } +.b01-shell__menu-touch > span:nth-child(2), +.b01-shell__menu-touch button { + white-space: nowrap; +} .b01-shell__menu-touch > span:nth-child(2) { flex: 1; } diff --git a/B01_Dashboard/B01_Dashboard_UI_Views.ts b/B01_Dashboard/B01_Dashboard_UI_Views.ts index 82cf3cc62..2569388ce 100644 --- a/B01_Dashboard/B01_Dashboard_UI_Views.ts +++ b/B01_Dashboard/B01_Dashboard_UI_Views.ts @@ -79,6 +79,25 @@ export const VIEWS: View[] = [ { key: "me", title: "내 정보", group: "hidden", roles: ALL, icon: "userCircle", mount: mountMe }, ]; +/** 머리 [?] 가 먼저 여는 가이드 절(`A05_EduDetail/guide/B01/<절>.md`) — 없으면 B01 설명서 */ +export const VIEW_HELP: Record = { + home: "01_홈", + projects: "02_프로젝트_목록", + assigned: "03_받은_예정노선", + temp: "04_임시_보관함", + people: "05_회사와_팀원", + project: "06_프로젝트_상세", + company: "07_회사_정보", + "company-defaults": "08_회사_설계_기본값", + "sys-overview": "09_시스템_관리", + "sys-companies": "09_시스템_관리", + "sys-users": "09_시스템_관리", + "sys-defaults": "09_시스템_관리", + "sys-logs": "09_시스템_관리", + chat: "11_지식_챗봇", + me: "12_내_정보", +}; + /** 역할 가르기 — 옛 B01 갈래(시스템 관리자 · 회사 관리자 · 그 밖) + 산림청 */ export function roleOf(me: DashboardUser, forest: boolean): ViewRole { if (me.role === "SYSTEM_ADMIN" || me.role === "ADMIN") return me.role; diff --git a/resources/tester/test_550_1_b01_guide_topics.py b/resources/tester/test_550_1_b01_guide_topics.py new file mode 100644 index 000000000..6a6b3cd12 --- /dev/null +++ b/resources/tester/test_550_1_b01_guide_topics.py @@ -0,0 +1,33 @@ +"""550-1 · B01 대시보드 가이드 — 세부 페이지 [?] 절 · 가이드 글 사이 링크 · 장면 그림이 실제 파일과 맞음.""" + +import json +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +GUIDE = ROOT / "A05_EduDetail" / "guide" +VIEWS = ROOT / "B01_Dashboard" / "B01_Dashboard_UI_Views.ts" + + +def test_view_help_topics_have_files(): + block = VIEWS.read_text(encoding="utf-8").split("VIEW_HELP")[1].split("};")[0] + topics = re.findall(r':\s*"([^"]+)"', block) + assert len(topics) >= 10 + for topic in topics: + assert (GUIDE / "B01" / f"{topic}.md").is_file(), topic + + +def test_guide_links_to_b01_resolve(): + for md in GUIDE.glob("*/*.md"): + for page, key in re.findall(r"\]\((B01)/([^)#]+)\)", md.read_text(encoding="utf-8")): + assert (GUIDE / page / f"{key}.md").is_file(), f"{md.name} → {page}/{key}" + + +def test_b01_images_match_scenes(): + names = { + s["name"] for s in json.loads((GUIDE / "B01" / "_scenes.json").read_text(encoding="utf-8")) + } + used = set() + for md in (GUIDE / "B01").glob("*.md"): + used |= set(re.findall(r"\]\((B01_[\w]+)\.webp\)", md.read_text(encoding="utf-8"))) + assert used <= names, used - names