跳至主要内容

US-003: 基礎 Layout 與導航

User Story

作為 已登入的用戶 我想要 看到一個清晰、一致的系統 Layout 與導航結構 以便 快速訪問各個功能模組並了解當前所在位置


驗收標準 (Acceptance Criteria)

Scenario 1: 登入後看到完整的 Layout 結構

  • Given 我是已登入的店員
  • When 我成功登入並進入首頁
  • Then 我應看到完整的 Layout 結構:
    • Header: 包含 Logo、Application Launcher、全螢幕切換、主題切換、通知鈴鐺(Phase 2)、用戶選單
    • Main Content: 中央內容區域(Applet 容器)
    • Footer: 頁尾資訊(版權、版本號)

Scenario 2: Header 中顯示用戶資訊與角色

  • Given 我是已登入的店員
  • And 我的姓名是「王小明」,角色是「店員」
  • When 我查看 Header 右上角
  • Then 我應看到我的姓名「王小明」
  • And 我應看到角色標籤「店員」(Badge 樣式)
  • And 我應看到用戶頭像(預設為姓名首字母圓形 Avatar)

Scenario 3: 點擊用戶選單顯示下拉選項

  • Given 我在任意頁面
  • When 我點擊 Header 右上角的用戶頭像/姓名
  • Then 應顯示下拉選單,包含:
    • 個人設定
    • 登出
  • And 當我點擊「登出」時,系統應登出並重定向至登入頁面

Scenario 4: Application Launcher 根據角色動態顯示 Applet

  • Given 我是已登入的店員(ROLE_STAFF
  • When 我點擊 Header 的 Application Launcher 按鈕(Grid3x3 圖示)
  • Then 應顯示 Modal 應用啟動器
  • And 我應看到以下 Applet:
    • Home(首頁)
    • Orders(訂單管理)
    • Customers(客戶管理)
    • Calendar(行事曆)
    • Messages(訊息)
  • And 我不應看到「Settings」(需要更高權限)

Scenario 5: 從 Application Launcher 導航至 Applet

  • Given Application Launcher 已開啟
  • When 我點擊「Orders」Applet
  • Then 系統應導航至訂單列表頁面(/orders
  • And Application Launcher 應自動關閉
  • And URL 應更新為 /orders

Scenario 6: Application Launcher 搜尋功能

  • Given Application Launcher 已開啟
  • When 我在搜尋框輸入「訂單」
  • Then 應僅顯示符合搜尋條件的 Applet(如 Orders)
  • And 其他不符合的 Applet 應被過濾

Scenario 7: Application Launcher 分類過濾

  • Given Application Launcher 已開啟
  • When 我點擊「Sales & Marketing」分類按鈕
  • Then 應僅顯示該分類的 Applet(如 Orders、Customers)
  • And 其他分類的 Applet 應被過濾

Scenario 8: 麵包屑導航顯示當前位置(Phase 2)

  • Given 我在訂單詳情頁面(訂單編號 ABC-20251031-0001
  • When 我查看 Main Content 頂部
  • Then 我應看到麵包屑導航:
    首頁 > 訂單管理 > ABC-20251031-0001
  • And 每個層級都應可點擊(除了當前頁面)

Scenario 9: 通知鈴鐺顯示未讀數量

  • Given 我有 3 則未讀通知
  • When 我查看 Header 右上角
  • Then 通知鈴鐺圖示右上角應顯示紅色 Badge「3」
  • When 我點擊通知鈴鐺
  • Then 應顯示通知列表下拉選單(Phase 2 功能)

Scenario 10: Logo 點擊返回首頁

  • Given 我在任意頁面(如訂單管理)
  • When 我點擊 Header 左側的 Logo
  • Then 系統應導航至首頁(/

業務規則 (Business Rules)

  1. Layout 結構固定

    • 所有已登入頁面都使用相同的 Layout 結構
    • 登入頁面、404 頁面、403 頁面不使用此 Layout
  2. Application Launcher 顯示規則

    • 根據用戶角色動態顯示可用 Applet(參考 US-002)
    • 無權限的 Applet 完全隱藏(而非顯示為禁用狀態)
    • 支援搜尋和分類過濾
  3. Applet 分類

    • sales-marketing(銷售行銷)
    • operations(營運管理)
    • finance(財務會計)
    • utilities(工具程式)
    • other(其他)
  4. 響應式設計斷點

    • 手機 (< 768px): 精簡 Header
    • 平板 (768px - 1023px): 自適應 Header
    • 桌面 (≥ 1024px): 完整 Header 工具列
  5. Logo 與品牌

    • Logo 使用租戶上傳的圖片(如有)
    • 預設 Logo: 系統預設的花店圖示
    • 顯示租戶名稱(如「花店 ABC」)

UI/UX 需求 (UI/UX Requirements)

Layout 結構

┌──────────────────────────────────────────────────────────┐
│ Header │
│ [Logo] 花店 ABC [⊞] [⛶] [🌙] [🔔] 王小明 [店員] ▼ │
├──────────────────────────────────────────────────────────┤
│ │
│ Main Content Area │
│ (Applet 容器) │
│ │
├──────────────────────────────────────────────────────────┤
│ Footer: © 2025 AppFuse · v1.0.0 │
└──────────────────────────────────────────────────────────┘

Header 工具列說明:
[⊞] Application Launcher - 開啟應用程式啟動器
[⛶] 全螢幕切換 - 進入/退出全螢幕模式
[🌙] 主題切換 - 深色/淺色模式
[🔔] 通知鈴鐺 - Phase 2 功能

Header 規格

實作位置: src/layouts/header.tsx

高度: 56px(h-14

左側區域:

  • Logo(40x40px w-10 h-10)+ 租戶名稱「花店 ABC」
  • 點擊 Logo 返回首頁 /

右側區域(從左到右):

  • Application Launcher - Grid3x3 圖示,開啟應用程式啟動器
  • 全螢幕切換 - Maximize/Minimize 圖示,切換全螢幕模式
  • 主題切換 - ThemeToggle 組件(深色/淺色模式)
  • 通知鈴鐺 - Bell 圖示(Phase 2,目前 disabled)
  • 用戶選單 - UserMenu 組件(頭像 + 下拉選單)

樣式:

  • 背景: bg-base-100
  • 邊框: 底部 1px 邊框 border-base-300
  • 固定在頁面頂部(sticky top-0 z-30
  • 按鈕懸停: hover:bg-base-200

Application Launcher 規格

實作位置: src/components/application-launcher/application-launcher.tsx

觸發方式:

  • Header 右側 Grid3x3 圖示按鈕
  • 點擊開啟 Modal

Modal 結構:

┌─────────────────────────────────────┐
│ 🔍 搜尋框 [X] 關閉 │
│─────────────────────────────────────│
│ [All] [Sales] [Ops] [Finance] ... │
│─────────────────────────────────────│
│ │
│ Sales & Marketing │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 🛒 │ │ 👥 │ │ │ │
│ │Order│ │Custo│ │ │ │
│ └─────┘ └─────┘ └─────┘ │
│ │
│ Operations │
│ ┌─────┐ ┌─────┐ │
│ │ 📦 │ │ 🚚 │ │
│ │Produ│ │Deliv│ │
│ └─────┘ └─────┘ │
│ │
└─────────────────────────────────────┘

Applet 卡片樣式:

  • 大小: 56x56px(w-14 h-14
  • 圖示背景: DaisyUI 語義色(bg-primarybg-success 等)
  • 圖示: 白色 lucide-react 圖標
  • 懸停效果: scale-110 變換
  • 標籤: 下方顯示名稱(line-clamp-2

Modal 樣式:

  • 背景遮罩: bg-base-content/30 backdrop-blur-sm
  • 內容背景: bg-base-200/95 backdrop-blur-xl
  • 邊框: border-base-300
  • 陰影: shadow-2xl

鍵盤操作:

  • ESC 鍵關閉 Modal
  • 點擊背景遮罩關閉

Main Content 規格

Padding: 24px(桌面)、16px(手機)

最大寬度: 根據內容自適應,但不超過 1920px

最小高度: calc(100vh - Header高度 - Footer高度)

高度: 48px

內容:

  • 版權資訊: © 2025 AppFuse
  • 版本號: v1.0.0
  • 連結: 隱私政策、使用條款(Phase 2)

樣式:

  • 背景: bg-base-200
  • 文字: text-base-content/60 (60% 透明度)
  • 文字大小: text-sm

用戶選單下拉

寬度: 200px

選單項目:

┌────────────────┐
│ 個人設定 │
│ ───────────── │
│ 登出 │
└────────────────┘

樣式:

  • 背景: bg-base-100
  • 邊框: border border-base-300
  • 陰影: shadow-lg
  • 圓角: rounded-lg

通知下拉(Phase 2)

寬度: 360px

高度: 最多顯示 5 則通知,其餘可滾動

通知項目結構:

[圖示] 通知標題
通知內容摘要
時間戳(如「2 分鐘前」)

技術規格 (Technical Specifications)

組件結構

src/layouts/
├── main-layout.tsx # 主 Layout(Header + Main + Footer)
├── header.tsx # Header 組件(含 Application Launcher 觸發)
├── user-menu.tsx # 用戶選單下拉
├── menu-config.ts # 角色層級定義
└── (NotificationBell) # 通知鈴鐺(Phase 2)

src/components/
├── theme-toggle.tsx # 主題切換組件
└── application-launcher/
├── application-launcher.tsx # 應用程式啟動器 Modal
└── index.ts

src/config/
└── applet-registry.ts # Applet 註冊與角色過濾

路由配置

// src/routes/index.tsx
import { MainLayout } from '@/layouts/MainLayout';

const routes = [
{
path: '/',
element: <MainLayout />, // 套用 MainLayout
children: [
{ index: true, element: <HomePage /> },
{ path: 'orders', element: <OrderListPage /> },
{ path: 'orders/:id', element: <OrderDetailPage /> },
{ path: 'customers', element: <CustomerListPage /> },
// ...其他路由
]
},
// 不使用 MainLayout 的路由
{ path: '/login', element: <LoginPage /> },
{ path: '/403', element: <ForbiddenPage /> },
{ path: '/404', element: <NotFoundPage /> },
];

前端組件

  • 使用框架組件:

    • @appfuse/appfuse-webDropdown 組件(用戶選單)
    • lucide-react 的圖示組件(Home, ShoppingCart, Users, Package, Settings, Bell, Grid3x3, Maximize, Minimize, ChevronLeft, ChevronRight, X)
  • 自定義組件:

    • main-layout.tsx - 主 Layout(整合 Header + Outlet + Footer)
    • header.tsx - Header 組件(Logo + Application Launcher + 工具列)
    • user-menu.tsx - 用戶選單下拉
    • theme-toggle.tsx - 主題切換(深色/淺色)
    • application-launcher.tsx - 應用程式啟動器 Modal
    • applet-registry.ts - Applet 配置與角色過濾

狀態管理

Application Launcher 狀態(在 header.tsx 內部管理):

const [isLauncherOpen, setIsLauncherOpen] = useState(false);

// 開啟 Application Launcher
<button onClick={() => setIsLauncherOpen(true)}>
<Grid3x3 />
</button>

// 渲染 Modal
<ApplicationLauncher
isOpen={isLauncherOpen}
onClose={() => setIsLauncherOpen(false)}
/>

用戶資訊:

  • 從 Redux Store 讀取(src/features/iam/me-slice.ts
  • useAppSelector(selectCurrentUser) 獲取當前用戶

選單配置src/layouts/menu-config.ts):

export interface MenuItem {
id: string;
label: string;
icon: LucideIcon;
path: string;
requiredRole?: RoleName; // 最低所需角色
children?: MenuItem[]; // 子選單(Phase 2)
}

export const menuItems: MenuItem[] = [
{ id: 'home', label: '首頁', icon: Home, path: '/' },
{ id: 'orders', label: '訂單管理', icon: ShoppingCart, path: '/orders', requiredRole: 'ROLE_SALES' },
{ id: 'customers', label: '客戶管理', icon: Users, path: '/customers', requiredRole: 'ROLE_SALES' },
{ id: 'products', label: '商品管理', icon: Package, path: '/products', requiredRole: 'ROLE_OWNER' },
{ id: 'reports', label: '報表分析', icon: FileText, path: '/reports', requiredRole: 'ROLE_OWNER' },
{ id: 'settings', label: '系統設定', icon: Settings, path: '/settings', requiredRole: 'ROLE_ADMIN' },
];

響應式斷點

使用 TailwindCSS 斷點:

const breakpoints = {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
};

Application Launcher 顯示邏輯:

  • 所有斷點:透過 Header 的 Grid3x3 按鈕觸發 Modal

SBE 場景 (Specification by Example)

詳細的測試場景與範例數據:


估算 (Estimation)

  • Story Points: 5 點
  • 預估工時: 2-3 天
  • 複雜度: 中等

工作拆分

  1. Layout 組件開發 (1 天)

    • 創建 MainLayoutHeaderFooter 組件
    • 實作響應式設計(桌面、平板、手機)
  2. Application Launcher 開發 (1 天)

    • 實作 Application Launcher Modal 組件
    • 實作 Applet 註冊配置(applet-registry.ts
    • 整合角色權限過濾
    • 實作搜尋和分類過濾功能
  3. 用戶選單與通知 (0.5 天)

    • 實作用戶選單下拉(個人設定、登出)
    • 實作登出邏輯(清除 Token + 重定向)
    • 實作通知鈴鐺(僅 UI,Phase 2 實作通知列表)
  4. 測試與優化 (0.5 天)

    • 單元測試(Application Launcher 過濾邏輯)
    • 響應式測試(桌面、平板、手機)
    • 無障礙性測試(鍵盤導航、螢幕閱讀器)

依賴 (Dependencies)

前置條件

  • US-001: 用戶登入 - 需要用戶資訊(姓名、角色)
  • US-002: 角色權限控制 - 需要動態顯示選單

並行開發

  • 無(US-001 和 US-002 必須先完成)

測試策略 (Testing Strategy)

單元測試

  • 測試 Application Launcher 過濾邏輯(filterAppletsByRole 函數)
  • 測試搜尋邏輯(searchApplets 函數)
  • 測試分類過濾邏輯

整合測試

  • 測試登出流程(點擊「登出」→ 清除 Token → 重定向至登入頁面)

E2E 測試

  • 測試完整 Layout 結構顯示
  • 測試 Application Launcher 開啟與關閉
  • 測試 Applet 導航(點擊 Applet → 導航至對應頁面)
  • 測試 Logo 點擊返回首頁

響應式測試

  • 測試桌面版(≥ 1024px)- 完整 Header 工具列
  • 測試平板版(768px - 1023px)- 自適應 Header
  • 測試手機版(< 768px)- 精簡 Header

無障礙性測試

  • 測試鍵盤導航(Tab 鍵切換焦點)
  • 測試螢幕閱讀器(選單項目有正確的 aria-label

完成定義 (Definition of Done)

  • 所有 Layout 組件實作完成(MainLayout、Header、Footer)
  • Application Launcher 實作完成(含角色過濾、搜尋、分類)
  • 響應式設計實作完成(桌面、平板、手機)
  • Applet 註冊配置實作完成(applet-registry.ts
  • 用戶選單下拉實作完成(個人設定、登出)
  • 通知鈴鐺 UI 實作完成(Phase 2 功能預留)
  • 單元測試通過
  • E2E 測試通過(至少涵蓋 Scenario 1、4、5)
  • 響應式測試通過(桌面、平板、手機)
  • 無障礙性測試通過(WCAG 2.1 AA)
  • Code Review 通過
  • Storybook Stories 已創建(展示各組件)
  • 產品經理驗收通過

備註 (Notes)

設計決策

  • 為何使用 Application Launcher 而非固定 Sidebar? Modal 方式更節省螢幕空間,適合多 Applet 的應用程式架構。
  • 為何使用搜尋和分類過濾? 當 Applet 數量增加時,用戶能快速找到需要的應用。
  • 為何 Applet 無權限時完全隱藏? 避免混淆用戶,簡化 UI。

未來優化

  • 支援 Applet 收藏功能(用戶自定義常用應用)
  • 支援 Applet 拖放排序(管理者自定義顯示順序)
  • 支援麵包屑導航(顯示層級路徑)
  • 支援全域搜尋功能(Header 中央搜尋框)

最後更新: 2025-12-20