Files
star-erp/.agents/rules/ui-consistency.md
sky121113 deef3baacc
All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 57s
refactor: 重構模組通訊與調整儀表板功能
- 依循跨模組通訊規範,將 Sales 與 Production 模組中對 Inventory 的直接模型關聯改為透過 InventoryServiceInterface 取得
- 於 InventoryService 實作獲取最高庫存價值、即將過期商品等方法,供儀表板使用
- 確保所有跨模組調用皆採用手動水和(Manual Hydration)方式組合資料
- 移除本地已歸檔的 .agent 規範檔案
2026-02-25 11:48:52 +08:00

15 KiB
Raw Blame History

trigger
trigger
always_on

name: 客戶端後台 UI 統一規範 description: 確保 Star ERP 客戶端(租戶端)後台所有頁面的 UI 元件保持統一的樣式與行為

概述

本技能提供 Star ERP 系統客戶端(租戶端)後台的 UI 統一性規範,確保所有頁面使用一致的元件、樣式類別、圖標和佈局模式。

適用範圍:本規範適用於租戶端後台(使用 AuthenticatedLayout 的頁面),不適用於中央管理後台(LandlordLayout)。

核心原則

  1. 使用統一的 UI 組件庫:優先使用 @/Components/ui/ 中的 47 個元件
  2. 遵循既定的樣式類別:使用 app.css 中定義的自定義按鈕類別
  3. 統一的圖標系統:全面使用 lucide-react 圖標
  4. 一致的佈局模式:表格、分頁、操作按鈕等保持相同結構
  5. 權限控制:所有操作按鈕必須使用 <Can> 元件包裹

1. 專案結構

1.1 關鍵目錄

resources/
├── css/
│   └── app.css              # 全域樣式與設計 Token
├── js/
│   ├── Components/
│   │   ├── ui/              # 47 個基礎 UI 元件 (shadcn/ui)
│   │   ├── shared/          # 共用業務元件 (Pagination, BreadcrumbNav 等)
│   │   └── Permission/      # 權限控制元件 (Can, HasRole, CanAll)
│   ├── Layouts/
│   │   ├── AuthenticatedLayout.tsx  # 客戶端後台佈局 ⬅️ 本規範適用
│   │   └── LandlordLayout.tsx       # 中央管理後台佈局
│   └── Pages/               # 頁面元件

1.2 可用 UI 元件清單

accordion, alert, alert-dialog, avatar, badge, breadcrumb, button, 
calendar, card, carousel, chart, checkbox, collapsible, command, 
context-menu, dialog, drawer, dropdown-menu, form, hover-card, 
input, input-otp, label, menubar, navigation-menu, pagination, 
popover, progress, radio-group, resizable, scroll-area, 
searchable-select, select, separator, sheet, sidebar, skeleton, 
slider, sonner, switch, table, tabs, textarea, toggle, toggle-group, 
tooltip

2. 色彩系統

2.1 主題色 (Primary) - 動態租戶品牌色

注意主題色會根據租戶設定Branding動態改變嚴禁在程式碼中 Hardcode 色碼(如 #01ab83)。 請務必使用 Tailwind Utility Class 或 CSS 變數。

Tailwind Class CSS Variable 說明
*-primary-main --primary-main 主色:與租戶設定一致(預設綠色),用於主要按鈕、連結、強調文字
*-primary-dark --primary-dark 深色:系統自動計算,用於 Hover 狀態
*-primary-light --primary-light 淺色:系統自動計算,用於次要強調
*-primary-lightest --primary-lightest 最淺色系統自動計算用於背景底色、Active 狀態

運作機制 AuthenticatedLayout 會根據後端回傳的 branding 資料,自動注入 CSS 變數覆寫預設值。

// ✅ 正確:使用 Tailwind Class
<div className="text-primary-main">...</div>

// ✅ 正確:使用 CSS 變數 (自定義樣式時)
<div style={{ borderColor: 'var(--primary-main)' }}>...</div>

// ❌ 錯誤:寫死色碼 (會導致租戶無法換色)
<div className="text-[#01ab83]">...</div>

2.2 灰階 (Grey Scale)

--grey-0: #1a1a1a;  /* 深黑 - 標題文字 */
--grey-1: #4a4a4a;  /* 深灰 - 主要內文 */
--grey-2: #6b6b6b;  /* 中灰 - 次要內文、Placeholder */
--grey-3: #9e9e9e;  /* 淺灰 - 禁用文字、輔助說明 */
--grey-4: #e0e0e0;  /* 極淺灰 - 邊框、分隔線 */
--grey-5: #fff;     /* 白色 - 背景、按鈕文字 */

2.3 狀態色 (State Colors)

--other-success: #01ab83;  /* 成功 - 同主題色 */
--other-error: #dc2626;    /* 錯誤 - 刪除、警示 */
--other-warning: #f59e0b;  /* 警告 - 提醒、注意 */
--other-info: #3b82f6;     /* 資訊 - 說明、提示 */

3. 按鈕規範

3.1 按鈕樣式類別

專案在 resources/css/app.css 中定義了統一的按鈕樣式,必須使用這些類別:

Filled 按鈕(實心按鈕)— 用於主要操作

// ✅ 主要操作按鈕(綠色主題色)- 新增、儲存、確認
<Button className="button-filled-primary">
  <Plus className="h-4 w-4 mr-2" />
  新增項目
</Button>

// ✅ 成功操作
<Button className="button-filled-success">確認</Button>

// ✅ 資訊操作(用於系統提示、說明等非業務主流程)
<Button className="button-filled-info">系統資訊</Button>

// ✅ 警告操作
<Button className="button-filled-warning">警告</Button>

// ✅ 錯誤/刪除操作AlertDialog 內確認按鈕)
<Button className="button-filled-error">刪除</Button>

Outlined 按鈕(邊框按鈕)— 用於次要操作

// ✅ 編輯按鈕(表格操作列)
<Button variant="outline" size="sm" className="button-outlined-primary">
  <Pencil className="h-4 w-4" />
</Button>

// ✅ 刪除按鈕(表格操作列)
<Button variant="outline" size="sm" className="button-outlined-error">
  <Trash2 className="h-4 w-4" />
</Button>

Text 按鈕(文字按鈕)

<Button className="button-text-primary">查看更多</Button>

3.2 按鈕大小

Size 高度 使用情境
size="sm" h-8 表格操作列、緊湊佈局
size="default" h-9 一般操作、表單提交
size="lg" h-10 主要 CTA、頁面主操作
size="icon" 9×9 純圖標按鈕

3.3 常見操作按鈕模式

頁面頂部新增按鈕

<Can permission="resource.create">
  <Link href={route('resource.create')}>
    <Button className="button-filled-primary">
      <Plus className="h-4 w-4 mr-2" />
      新增XXX
    </Button>
  </Link>
</Can>

表格操作列檢視按鈕

<Can permission="resource.view">
  <Link href={route('resource.show', item.id)}>
    <Button
      variant="outline"
      size="sm"
      className="button-outlined-primary"
      title="檢視"
    >
      <Eye className="h-4 w-4" />
    </Button>
  </Link>
</Can>

表格操作列編輯按鈕

<Can permission="resource.edit">
  <Link href={route('resource.edit', item.id)}>
    <Button
      variant="outline"
      size="sm"
      className="button-outlined-primary"
      title="編輯"
    >
      <Pencil className="h-4 w-4" />
    </Button>
  </Link>
</Can>

表格操作列刪除按鈕(帶確認對話框)

<Can permission="resource.delete">
  <AlertDialog>
    <AlertDialogTrigger asChild>
      <Button 
        variant="outline" 
        size="sm" 
        className="button-outlined-error"
        title="刪除"
      >
        <Trash2 className="h-4 w-4" />
      </Button>
    </AlertDialogTrigger>
    <AlertDialogContent>
      <AlertDialogHeader>
        <AlertDialogTitle>確認刪除</AlertDialogTitle>
        <AlertDialogDescription>
          確定要刪除「{item.name}」嗎?此操作無法復原。
        </AlertDialogDescription>
      </AlertDialogHeader>
      <AlertDialogFooter>
        <AlertDialogCancel>取消</AlertDialogCancel>
        <AlertDialogAction
          onClick={() => handleDelete(item.id)}
          className="bg-red-600 hover:bg-red-700"
        >
          刪除
        </AlertDialogAction>
      </AlertDialogFooter>
    </AlertDialogContent>
  </AlertDialog>
</Can>

3.4 返回按鈕規範

詳情頁面(如:查看庫存、進貨單詳情)的返回按鈕應統一放置於 頁面標題上方,並採用「圖標 + 文字」的 Outlined 樣式。

樣式規格

  • 位置:標題區域上方 (mb-6),獨立於標題列
  • 樣式variant="outline" + className="gap-2 button-outlined-primary"
  • 圖標<ArrowLeft className="h-4 w-4" />
  • 文字:清楚說明返回目的地,例如「返回倉庫管理」、「返回列表」
<div className="mb-6">
  <Link href={route('resource.index')}>
    <Button
      variant="outline"
      className="gap-2 button-outlined-primary"
    >
      <ArrowLeft className="h-4 w-4" />
      返回列表
    </Button>
  </Link>
</div>

3.5 頁面佈局規範(新增/編輯頁面)

標準結構

新增/編輯頁面(如:商品新增、採購單建立)應遵循以下標準結構:

<AuthenticatedLayout breadcrumbs={...}>
    <Head title="..." />
    
    <div className="container mx-auto p-6 max-w-7xl">
        {/* Header */}
        <div className="mb-6">
            {/* 返回按鈕 */}
            <Link href={route('resource.index')}>
                <Button
                    variant="outline"
                    className="gap-2 button-outlined-primary mb-4"
                >
                    <ArrowLeft className="h-4 w-4" />
                    返回列表
                </Button>
            </Link>

            {/* 頁面標題區塊 */}
            <div className="mb-4">
                <h1 className="text-2xl font-bold text-grey-0 flex items-center gap-2">
                    <Icon className="h-6 w-6 text-primary-main" />
                    頁面標題
                </h1>
                <p className="text-gray-500 mt-1">
                    頁面說明文字
                </p>
            </div>
        </div>

        {/* 表單或內容區塊 */}
        <FormComponent ... />
    </div>
</AuthenticatedLayout>

關鍵規範

  1. 外層容器:使用 className="container mx-auto p-6 max-w-7xl" 確保寬度與間距一致
  2. Header 包裹:使用 <div className="mb-6"> 包裹返回按鈕與標題區塊
  3. 返回按鈕:加上 mb-4 與標題區塊分隔
  4. 標題區塊:使用 <div className="mb-4"> 包裹 h1 和 p 標籤
  5. 標題樣式text-2xl font-bold text-grey-0 flex items-center gap-2
  6. 說明文字text-gray-500 mt-1

範例頁面

  • /resources/js/Pages/PurchaseOrder/Create.tsx(建立採購單)
  • /resources/js/Pages/Product/Create.tsx(新增商品)
  • /resources/js/Pages/Product/Edit.tsx(編輯商品)

4. 圖標規範

4.1 統一使用 lucide-react

統一使用 lucide-react,禁止使用其他圖標庫(如 FontAwesome、Material Icons、react-icons 等)。

4.2 圖標尺寸標準

尺寸 類別 使用情境
小型 h-3 w-3 Badge 內、小文字旁
標準 h-4 w-4 按鈕內、表格操作
標題 h-5 w-5 側邊欄選單
大型 h-6 w-6 頁面標題

4.3 常用操作圖標映射

操作 圖標組件 使用情境
新增 <Plus /> 新增按鈕
編輯 <Pencil /> 編輯按鈕
刪除 <Trash2 /> 刪除按鈕
查看 <Eye /> 查看詳情
搜尋 <Search /> 搜尋欄位
篩選 <Filter /> 篩選功能
下載 <Download /> 下載/匯出
上傳 <Upload /> 上傳/匯入
設定 <Settings /> 設定功能
複製 <Copy /> 複製內容
郵件 <Mail /> Email 顯示
使用者 <Users />, <User /> 使用者管理
權限 <Shield /> 角色/權限
排序 <ArrowUpDown />, <ArrowUp />, <ArrowDown /> 表格排序
儀表板 <LayoutDashboard /> 首頁/總覽
商品 <Package /> 商品管理
倉庫 <Warehouse /> 倉庫管理
廠商 <Truck />, <Contact2 /> 廠商管理
採購 <ShoppingCart /> 採購管理

4.4 圖標使用範例

import { Plus, Pencil, Trash2, Users } from 'lucide-react';

// 頁面標題
<h1 className="text-2xl font-bold text-grey-0 flex items-center gap-2">
  <Users className="h-6 w-6 text-[#01ab83]" />
  使用者管理
</h1>

// 按鈕內圖標(圖標在左,帶文字)
<Button className="button-filled-primary">
  <Plus className="h-4 w-4 mr-2" />
  新增使用者
</Button>

// 純圖標按鈕(表格操作列)
<Button variant="outline" size="sm" className="button-outlined-primary">
  <Pencil className="h-4 w-4" />
</Button>

5. 表格規範

5.1 表格容器

<div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">
  <Table>
    {/* 表格內容 */}
  </Table>
</div>

5.2 表格標題列

<TableHeader className="bg-gray-50">
  <TableRow>
    <TableHead className="w-[50px] text-center">#</TableHead>
    <TableHead>名稱</TableHead>
    <TableHead className="text-center">操作</TableHead>
  </TableRow>
</TableHeader>

關鍵要點

  • 使用 bg-gray-50 背景色
  • 序號欄位固定寬度 w-[50px] 並置中
  • 操作欄位置中顯示

5.3 表格主體

<TableBody>
  {items.length === 0 ? (
    <TableRow>
      <TableCell colSpan={5} className="text-center py-8 text-gray-500">
        無符合條件的資料
      </TableCell>
    </TableRow>
  ) : (
    items.map((item, index) => (
      <TableRow key={item.id}>
        <TableCell className="text-gray-500 font-medium text-center">
          {startIndex + index}
        </TableCell>
        {/* 其他欄位 */}
        <TableCell className="text-center">
          <div className="flex items-center justify-center gap-2">
            {/* 操作按鈕 */}
          </div>
        </TableCell>
      </TableRow>
    ))
  )}
</TableBody>

關鍵要點

  • 空狀態訊息使用置中、灰色文字
  • 序號欄使用 text-gray-500 font-medium text-center
  • 操作欄使用 flex items-center justify-center gap-2 排列按鈕

5.4 欄位排序規範

當表格需要支援排序時,請遵循以下模式:

  1. 圖標邏輯
    • 未排序:ArrowUpDown (class: text-muted-foreground)
    • 升冪 (asc)ArrowUp (class: text-primary)
    • 降冪 (desc)ArrowDown (class: text-primary)
  2. 結構:在 TableHead 內使用 button 元素。
  3. 後端配合:後端 Controller 必須 處理 sort_bysort_order 參數。
// 1. 定義 Helper Component (在元件內部)
const SortIcon = ({ field }: { field: string }) => {
    if (filters.sort_by !== field) {
        return <ArrowUpDown className="h-4 w-4 text-muted-foreground ml-1" />;
    }
    if (filters.sort_order === "asc") {
        return <ArrowUp className="h-4 w-4 text-primary ml-1" />;
    }
    return <ArrowDown className="h-4 w-4 text-primary ml-1" />;
};

// 2. 表格標題應用
<TableHead>
    <button 
        onClick={() => handleSort('created_at')} 
        className="flex items-center hover:text-gray-900"
    >
        建立時間 <SortIcon field="created_at" />
    </button>
</TableHead>

// 3. 排序處理函式 (三態切換:未排序 -> 升冪 -> 降冪 -> 未排序)
const handleSort = (field: string) => {
    let newSortBy: string | undefined = field;
    let newSortOrder: 'asc' | 'desc' | undefined = 'asc';

    if (filters.sort_by === field) {
        if (filters.sort_order === 'asc') {
            newSortOrder = 'desc';
        } else {
            // desc -> reset (回到預設排序)
            newSortBy = undefined;
            newSortOrder = undefined;
        }
    }

    router.get(
        route(route().curr