feat(B01): 대시보드 새 틀 — 셸 · 왼쪽 메뉴 · 세부 페이지 등록부 (546-1)

- 셸 = 위 머리 48(AISLO · 회사 이름 — 시스템 관리자 회사 전환 · 찾기 자리 · [?] · 알림 자리 · 사용자 메뉴) + 왼쪽 메뉴 210 + 챗봇 단추
- 등록부 B01_Dashboard_UI_Views.ts — { key, title, group, roles, icon, mount(host, ctx) } · 주소 ?view= · 같은 주소 안 이동 = 페이지만 갈아 끼움(MountedPage)
- 역할 넷(일반 · 회사 관리자 · 산림청 · 시스템 관리자) · 권한 없는 묶음 · 항목 숨김 · 권한 없는 열쇠 = 홈
- 열쇠마다 옛 구역 부품 그대로 얹음(B01_Dashboard_UI_Legacy.ts) — 기능 빠짐 0
- 옛 ☰ 서랍 · 구역 접기 · 회사 알약 걷음 — 테마 · 언어 · 터치 · 설정 · 로그아웃은 사용자 메뉴로
- 아이콘 = Tabler(MIT) 선 아이콘 몇 개(ui_template_icons.ts · 라이센스 글 함께)
- 시험 — test_b01_settings_cards 옛 한 페이지 단정(buildPage · 접힘 · 알약) 지움 · 셸 회사 고르기 단정으로 바꿈 · test_546_b01_shell 새로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 18:05:56 +09:00
co-authored by Claude Opus 5.5
parent 392cb92987
commit e505f4014d
10 changed files with 1320 additions and 395 deletions
+78
View File
@@ -0,0 +1,78 @@
/* =============================================================================
* ui_template_icons.ts
* 선 아이콘 한 묶음 — Tabler Icons(https://tabler.io/icons) 에서 필요한 것만 옮김 · npm 묶음 안 들임.
* 쓰기 = `icon("home")` → <span class="ui-icon"><svg …/></span> · 색 = 글 색(currentColor) · 크기 = 인자.
*
* MIT License
*
* Copyright (c) 2020-2024 Paweł Kuna
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ========================================================================== */
/** 아이콘 이름 → <path> 묶음(24 × 24 · 선 2) */
const PATHS = {
home: "M5 12l-2 0l9 -9l9 9l-2 0|M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7|M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6",
folder: "M5 4h4l3 3h7a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-11a2 2 0 0 1 2 -2",
route:
"M3 19a2 2 0 1 0 4 0a2 2 0 0 0 -4 0|M19 7a2 2 0 1 0 0 -4a2 2 0 0 0 0 4z|M11 19h5.5a3.5 3.5 0 0 0 0 -7h-8a3.5 3.5 0 0 1 0 -7h4.5",
archive:
"M3 6a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z|M5 8v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-10|M10 12l4 0",
users:
"M5 7a4 4 0 1 0 8 0a4 4 0 1 0 -8 0|M3 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2|M16 3.13a4 4 0 0 1 0 7.75|M21 21v-2a4 4 0 0 0 -3 -3.85",
building:
"M3 21l18 0|M9 8l1 0|M9 12l1 0|M9 16l1 0|M14 8l1 0|M14 12l1 0|M14 16l1 0|M5 21v-16a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v16",
adjustments:
"M4 10a2 2 0 1 0 4 0a2 2 0 0 0 -4 0|M6 4v4|M6 12v8|M10 16a2 2 0 1 0 4 0a2 2 0 0 0 -4 0|M12 4v10|M12 18v2|M16 7a2 2 0 1 0 4 0a2 2 0 0 0 -4 0|M18 4v1|M18 9v11",
gauge: "M10 13a2 2 0 1 0 4 0a2 2 0 1 0 -4 0|M13.45 11.55l2.05 -2.05|M6.4 20a9 9 0 1 1 11.2 0z",
user: "M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0|M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2",
userCircle:
"M3 12a9 9 0 1 0 18 0a9 9 0 1 0 -18 0|M9 10a3 3 0 1 0 6 0a3 3 0 1 0 -6 0|M6.168 18.849a4 4 0 0 1 3.832 -2.849h4a4 4 0 0 1 3.834 2.855",
database:
"M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0|M4 6v6a8 3 0 0 0 16 0v-6|M4 12v6a8 3 0 0 0 16 0v-6",
fileText:
"M14 3v4a1 1 0 0 0 1 1h4|M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z|M9 9l1 0|M9 13l6 0|M9 17l6 0",
book: "M3 19a9 9 0 0 1 9 0a9 9 0 0 1 9 0|M3 6a9 9 0 0 1 9 0a9 9 0 0 1 9 0|M3 6l0 13|M12 6l0 13|M21 6l0 13",
chatbot:
"M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12z|M9.5 9h.01|M14.5 9h.01|M9.5 13a3.5 3.5 0 0 0 5 0",
bell: "M10 5a2 2 0 1 1 4 0a7 7 0 0 1 4 6v3a4 4 0 0 0 2 3h-16a4 4 0 0 0 2 -3v-3a7 7 0 0 1 4 -6|M9 17v1a3 3 0 0 0 6 0v-1",
search: "M3 10a7 7 0 1 0 14 0a7 7 0 1 0 -14 0|M21 21l-6 -6",
chevronDown: "M6 9l6 6l6 -6",
logout:
"M14 8v-2a2 2 0 0 0 -2 -2h-7a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2 -2v-2|M9 12h12l-3 -3|M18 15l3 -3",
sun: "M8 12a4 4 0 1 0 8 0a4 4 0 1 0 -8 0|M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7",
moon: "M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z",
world:
"M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0|M3.6 9h16.8|M3.6 15h16.8|M11.5 3a17 17 0 0 0 0 18|M12.5 3a17 17 0 0 1 0 18",
hand: "M8 13v-7.5a1.5 1.5 0 0 1 3 0v6.5|M11 5.5v-2a1.5 1.5 0 1 1 3 0v8.5|M14 5.5a1.5 1.5 0 0 1 3 0v6.5|M17 7.5a1.5 1.5 0 0 1 3 0v8.5a6 6 0 0 1 -6 6h-2h.208a6 6 0 0 1 -5.012 -2.7a69.74 69.74 0 0 1 -.196 -.3c-.312 -.479 -1.407 -2.388 -3.286 -5.728a1.5 1.5 0 0 1 .536 -2.022a1.867 1.867 0 0 1 2.28 .28l1.47 1.47",
} as const;
export type IconName = keyof typeof PATHS;
export function icon(name: IconName, size = 18): HTMLSpanElement {
const span = document.createElement("span");
span.className = "ui-icon";
span.setAttribute("aria-hidden", "true");
const paths = PATHS[name]
.split("|")
.map((d) => `<path d="${d}"/>`)
.join("");
span.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${paths}</svg>`;
return span;
}