fix(B01): 머리 [?] 가 지금 세부 페이지의 가이드 절로 열림 · 사용자 메뉴 「터치 모드」 글자 꺾임 바로잡음 (PLAN 550-1)

- VIEW_HELP(세부 페이지 열쇠 → B01 가이드 절) · 셸 페이지 자리에 data-help-topic
- 터치 모드 이름 · 자동 · 켬 · 끔 단추 nowrap — 메뉴가 넓어져 한 줄
- 지킴 시험 test_550_1 — [?] 절 · B01 링크 · 장면 그림이 파일과 맞음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 00:59:05 +09:00
co-authored by Claude Opus 5.5
parent 5c16da0c5b
commit dba0078f8c
4 changed files with 59 additions and 1 deletions
+3 -1
View File
@@ -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;
@@ -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;
}
+19
View File
@@ -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<string, string> = {
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;
@@ -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