"use client";
/**
* Sidebar — 左側導航
*
* 規格來源:
* - .autoflow/03-design/components.md §3.1(雲端版 Sidebar 變更)
* - .autoflow/03-design/pages.md(頁面總覽)
*
* 設計重點:
* - 使用 Lucide Icon(對齊 design-review.md C1 建議,取代 local-tool 的單字母佔位)
* - 當前路徑用 usePathname() 自動偵測('/' 嚴格比對、其他用 startsWith)
* - active state 採用 `bg-sidebar-accent text-sidebar-accent-foreground`
* — 利用既有的 sidebar-* Design Tokens(globals.css 已定義 Light/Dark 兩套)
* - Desktop 固定寬度 w-60;Mobile 先隱藏(AppShell 負責),未來 F 任務再補 drawer
* - 版本號顯示於底部(暫以 package.json version 等效字串)
*
* 未做(保留給後續任務):
* - Mobile drawer / Sheet(F6 之後視需要)
* - UserMenu — 依 F4 任務規格已改放到 Header 右側(不在 Sidebar 底部)
*/
import Link from "next/link";
import { usePathname } from "next/navigation";
import {
Boxes,
Cable,
LayoutDashboard,
Network,
Play,
Settings,
type LucideIcon,
} from "lucide-react";
import { useT, type TranslateFn } from "@/lib/i18n/context";
import { cn } from "@/lib/utils";
interface NavItem {
href: string;
/** i18n key,由 t() 展開 */
labelKey: string;
icon: LucideIcon;
}
/**
* 導航項目 — 對齊 pages.md 頁面總覽與 components.md §3.1 的 icon 指定。
* 以函式產生讓 test 也能以同一份設定做 assertion(若需要)。
*/
const NAV_ITEMS: readonly NavItem[] = [
{ href: "/", labelKey: "nav.dashboard", icon: LayoutDashboard },
{ href: "/devices", labelKey: "nav.devices", icon: Cable },
{ href: "/models", labelKey: "nav.models", icon: Boxes },
{ href: "/workspace", labelKey: "nav.workspace", icon: Play },
{ href: "/clusters", labelKey: "nav.clusters", icon: Network },
{ href: "/settings", labelKey: "nav.settings", icon: Settings },
] as const;
/**
* 是否為當前 active 導航項目。
* 匯出給 test 使用,避免在測試中重造 active 判定邏輯。
*/
export function isNavActive(itemHref: string, pathname: string): boolean {
if (itemHref === "/") return pathname === "/";
// startsWith 比對,使得 /devices/pair 也能讓 /devices 維持 active 狀態
return pathname === itemHref || pathname.startsWith(`${itemHref}/`);
}
interface SidebarProps {
className?: string;
}
export function Sidebar({ className }: SidebarProps) {
const pathname = usePathname();
const t = useT();
return (
);
}
interface NavItemLinkProps {
item: NavItem;
active: boolean;
t: TranslateFn;
}
function NavItemLink({ item, active, t }: NavItemLinkProps) {
const Icon = item.icon;
const label = t(item.labelKey);
return (
{label}
);
}