/* ============================================================================= * B01_Dashboard_UI_Nav.ts * 대시보드 왼쪽 메뉴(546) — 묶음(작업 · 회사 관리 · 시스템 관리 · 도움) · 권한 없는 항목 · 빈 묶음 숨김 * 항목 = 주소 링크(뒤로 가기 · 새 탭) · 지금 위치 강조 · 오른쪽 수 배지 · 작은 화면 = 아이콘 레일(CSS) * 도움 묶음 페이지(가이드 · 챗봇)도 여기 — 창을 여는 일만 함 * ========================================================================== */ import { ROUTES } from "@config/config_frontend"; import { createButton } from "@ui/ui_template_elements"; import { icon } from "@ui/ui_template_icons"; import { createHelpButton } from "@ui/ui_template_help"; import type { View, ViewGroup } from "./B01_Dashboard_UI_Views"; const GROUPS: [ViewGroup, string][] = [ ["work", "작업"], ["company", "회사 관리"], ["system", "시스템 관리"], ["help", "도움"], ]; export const viewHref = (key: string, params: Record = {}): string => `#/${ROUTES.B01_ACCOUNT}?${new URLSearchParams({ view: key, ...params })}`; export interface DashboardNav { root: HTMLElement; setActive: (key: string) => void; /** 수 배지 — 0 이하면 숨김 · alert = 빨강(처리할 일) */ setBadge: (key: string, count: number, alert?: boolean) => void; } /** views = 이 역할에 보이는 것만 받음 */ export function buildNav(views: View[]): DashboardNav { const root = document.createElement("nav"); root.className = "b01-shell__nav"; root.setAttribute("aria-label", "대시보드 메뉴"); const items = new Map(); for (const [group, label] of GROUPS) { const inGroup = views.filter((v) => v.group === group); if (!inGroup.length) continue; const head = document.createElement("div"); head.className = "b01-shell__nav-group"; head.textContent = label; root.append(head); for (const view of inGroup) { const a = document.createElement("a"); a.className = "b01-shell__nav-item"; a.href = viewHref(view.key); a.title = view.title; a.dataset.view = view.key; const text = document.createElement("span"); text.className = "b01-shell__nav-label"; text.textContent = view.title; const badge = document.createElement("span"); badge.className = "b01-shell__badge"; badge.hidden = true; a.append(icon(view.icon ?? "home"), text, badge); root.append(a); items.set(view.key, a); } } return { root, setActive: (key) => { for (const [k, a] of items) { a.classList.toggle("is-active", k === key); if (k === key) a.setAttribute("aria-current", "page"); else a.removeAttribute("aria-current"); } }, setBadge: (key, count, alert = false) => { const badge = items.get(key)?.querySelector(".b01-shell__badge"); if (!badge) return; badge.hidden = count <= 0; badge.textContent = String(count); badge.classList.toggle("is-alert", alert); }, }; } function helpCard(text: string, action: HTMLElement): HTMLElement { const card = document.createElement("div"); card.className = "b01-shell__card b01-shell__help"; const p = document.createElement("p"); p.textContent = text; card.append(p, action); return card; } /** 사용자 가이드 — 위 머리 [?] 와 같은 창을 엶 */ export function mountGuide(host: HTMLElement): void { const button = createHelpButton(); host.append(helpCard("대시보드 사용법을 그림과 함께 봅니다.", button)); button.click(); } /** 지식 챗봇 — 오른쪽 아래 둥근 단추 창을 엶 */ export function mountChat(host: HTMLElement): void { const toggle = (): HTMLButtonElement | null => document.querySelector(".b01-knowledge-chat__toggle"); const open = (): void => { const t = toggle(); if (t?.getAttribute("aria-expanded") !== "true") t?.click(); }; const button = createButton({ label: "챗봇 열기", variant: "filled", onClick: open }); host.append(helpCard("임도 · 사방 법령과 기술 자료를 물어봅니다.", button)); open(); }