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

512 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 變數覆寫預設值。
```tsx
// ✅ 正確:使用 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)
```css
--grey-0: #1a1a1a; /* 深黑 - 標題文字 */
--grey-1: #4a4a4a; /* 深灰 - 主要內文 */
--grey-2: #6b6b6b; /* 中灰 - 次要內文、Placeholder */
--grey-3: #9e9e9e; /* 淺灰 - 禁用文字、輔助說明 */
--grey-4: #e0e0e0; /* 極淺灰 - 邊框、分隔線 */
--grey-5: #fff; /* 白色 - 背景、按鈕文字 */
```
### 2.3 狀態色 (State Colors)
```css
--other-success: #01ab83; /* 成功 - 同主題色 */
--other-error: #dc2626; /* 錯誤 - 刪除、警示 */
--other-warning: #f59e0b; /* 警告 - 提醒、注意 */
--other-info: #3b82f6; /* 資訊 - 說明、提示 */
```
---
## 3. 按鈕規範
### 3.1 按鈕樣式類別
專案在 `resources/css/app.css` 中定義了統一的按鈕樣式,**必須**使用這些類別:
#### Filled 按鈕(實心按鈕)— 用於主要操作
```tsx
// ✅ 主要操作按鈕(綠色主題色)- 新增、儲存、確認
<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 按鈕(邊框按鈕)— 用於次要操作
```tsx
// ✅ 編輯按鈕(表格操作列)
<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 按鈕(文字按鈕)
```tsx
<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 常見操作按鈕模式
#### 頁面頂部新增按鈕
```tsx
<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>
```
#### 表格操作列檢視按鈕
```tsx
<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>
```
#### 表格操作列編輯按鈕
```tsx
<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>
```
#### 表格操作列刪除按鈕(帶確認對話框)
```tsx
<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" />`
- **文字**:清楚說明返回目的地,例如「返回倉庫管理」、「返回列表」
```tsx
<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 頁面佈局規範(新增/編輯頁面)
### 標準結構
新增/編輯頁面(如:商品新增、採購單建立)應遵循以下標準結構:
```tsx
<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 圖標使用範例
```tsx
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 表格容器
```tsx
<div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">
<Table>
{/* 表格內容 */}
</Table>
</div>
```
### 5.2 表格標題列
```tsx
<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 表格主體
```tsx
<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_by``sort_order` 參數。
```tsx
// 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