'use client'; import Link from 'next/link'; import { FoxIconButton } from '@fox/core/components/fox-icon-button'; import type { SidebarMenu, SidebarMenuChild, SidebarMenuItem, } from '@/lib/domain/sidebar-menu'; import { useSidebarNav, type SidebarNavState } from '../_hooks/use-sidebar-nav'; import styles from './admin-sidebar.module.scss'; import { SidebarMenuIcon } from './sidebar-menu-icon'; interface AdminSidebarProps { /** 서버가 계정 권한에 맞춰 구성해 준 메뉴. 이 컴포넌트는 라우트 목록을 알지 못한다. */ menu: SidebarMenu; } /** * Basic 레이아웃 사이드바 — 시안 snb-area(3041:11860)를 옮겼다. 섹션별 GNB로 이루어지고, * GNB는 1depth > 2depth > 3depth 중첩이다. * * **메뉴 데이터를 소유하지 않는다.** 권한마다 항목이 달라 매 요청 서버가 조회해 넘겨주며 * (`fetchSidebarMenu`), 여기서는 받은 트리를 그리고 펼침·활성 상태만 판정한다. 그래서 새 화면이 * 생겨도 이 파일은 손대지 않는다. * */ export function AdminSidebar({ menu }: AdminSidebarProps) { const nav = useSidebarNav(menu); return ( ); } /** 1depth — 자식이 있으면 펼침 버튼, 없으면 링크다. */ function SidebarItem({ item, nav, }: { item: SidebarMenuItem; nav: SidebarNavState; }) { const hasChildren = (item.children?.length ?? 0) > 0; const isActive = nav.containsActive(item); const isOpen = nav.isItemOpen(item); const rowClass = `${styles.itemButton} ${isActive ? styles.itemButtonActive : ''}`; const inner = ( <> {item.label} ); return (
  • {hasChildren ? ( ) : ( {inner} )} {hasChildren && isOpen && ( )}
  • ); } /** 2depth — 자식이 있으면 펼침 버튼(별·캐럿 표시), 없으면 링크다. */ function SidebarChild({ child, nav, }: { child: SidebarMenuChild; nav: SidebarNavState; }) { const hasChildren = (child.children?.length ?? 0) > 0; const isOpen = nav.isChildOpen(child); const isActive = nav.isActive(child.href); const rowClass = `${styles.childRow} ${ isActive || (hasChildren && isOpen) ? styles.childRowActive : '' }`; // 시안은 자식이 없는 항목도 별·캐럿 자리를 비워 두고 투명하게만 만든다 — 라벨 시작 // 위치를 줄마다 맞추기 위해서다. const mark = (