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

# 客戶端後台 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().current()!),
        { ...filters, sort_by: newSortBy, sort_order: newSortOrder },
        { preserveState: true, replace: true }
    );
};
```

---

## 6. 分頁規範

### 6.1 統一分頁元件

使用 `@/Components/shared/Pagination` 元件：

```tsx
import Pagination from "@/Components/shared/Pagination";
import { SearchableSelect } from "@/Components/ui/searchable-select";

// 在表格下方（底部工具列）
<div className="mt-4 flex flex-col sm:flex-row items-center justify-between gap-4">
  <div className="flex items-center gap-4">
    <div className="flex items-center gap-2 text-sm text-gray-500">
      <span>每頁顯示</span>
      <SearchableSelect
        value={perPage}
        onValueChange={handlePerPageChange}
        options={[
          { label: "10", value: "10" },
          { label: "20", value: "20" },
          { label: "50", value: "50" },
          { label: "100", value: "100" }
        ]}
        className="w-[90px] h-8" // ✅ 統一使用 90px 寬度，避免「100」顯示不全
        showSearch={false}
      />
      <span>筆</span>
    </div>
    {/* 總筆數顯示：統一放在每頁顯示右側，使用 text-gray-500 */}
    <span className="text-sm text-gray-500">共 {data.total} 筆紀錄</span>
  </div>
  <Pagination links={data.links} />
</div>
```

### 6.2 每頁筆數狀態管理

```tsx
const [perPage, setPerPage] = useState<string>(filters.per_page || "10");

const handlePerPageChange = (value: string) => {
  setPerPage(value);
  router.get(
    route('resource.index'),
    { per_page: value },
    { preserveState: false, replace: true, preserveScroll: true }
  );
};
```

---

## 7. Badge 與狀態顯示

### 7.1 基本 Badge

```tsx
import { Badge } from "@/Components/ui/badge";

// Outline 樣式（最常用）
<Badge variant="outline">{item.category?.name || '-'}</Badge>

// 預設樣式（主題色背景）
<Badge variant="default">啟用中</Badge>

// 錯誤樣式
<Badge variant="destructive">停用</Badge>
```

### 7.2 角色顯示（特殊樣式）

```tsx
<div className="flex flex-wrap gap-2">
  {user.roles.map(role => (
    <div
      key={role.id}
      className={cn(
        "inline-flex items-center px-2.5 py-1 rounded-md border",
        role.name === 'super-admin'
          ? "bg-purple-50 border-purple-200"
          : "bg-gray-50 border-gray-200"
      )}
    >
      <div className="flex items-center gap-1.5">
        {role.name === 'super-admin' && <Shield className="h-3.5 w-3.5 text-purple-600" />}
        <span className={cn(
          "text-sm font-medium",
          role.name === 'super-admin' ? "text-purple-700" : "text-gray-900"
        )}>
          {role.display_name}
        </span>
      </div>
    </div>
  ))}
</div>
```

---

## 8. 頁面佈局規範

### 8.1 頁面結構

```tsx
export default function ResourceIndex() {
  return (
    <AuthenticatedLayout
      breadcrumbs={[
        { label: '分類名稱', href: '#' },
        { label: '頁面名稱', href: route('resource.index'), isPage: true },
      ]}
    >
      <Head title="頁面標題" />
      
      <div className="container mx-auto p-6 max-w-7xl">
        {/* 頁面頭部 */}
        {/* 主要內容 */}
        {/* 分頁元件 */}
      </div>
    </AuthenticatedLayout>
  );
}
```

### 8.2 標準頁面頭部

```tsx
<div className="flex items-center justify-between mb-6">
  <div>
    <h1 className="text-2xl font-bold text-grey-0 flex items-center gap-2">
      <IconComponent className="h-6 w-6 text-[#01ab83]" />
      頁面標題
    </h1>
    <p className="text-gray-500 mt-1">
      頁面說明文字
    </p>
  </div>
  <Can permission="resource.create">
    <Link href={route('resource.create')}>
      <Button className="button-filled-primary">
        <Plus className="h-4 w-4 mr-2" />
        新增項目
      </Button>
    </Link>
  </Can>
</div>
```

---

## 9. 權限控制規範

### 9.1 使用 Can 元件

**所有**涉及權限的 UI 元素都必須使用 `<Can>` 元件包裹：

```tsx
import { Can } from "@/Components/Permission/Can";

<Can permission="resource.create">
  {/* 新增按鈕 */}
</Can>

<Can permission="resource.edit">
  {/* 編輯按鈕 */}
</Can>

<Can permission="resource.delete">
  {/* 刪除按鈕 */}
</Can>
```

### 9.2 權限命名規範

遵循 `resource.action` 格式：

- `resource.view`：查看列表/詳情
- `resource.create`：新增
- `resource.edit`：編輯
- `resource.delete`：刪除

### 9.3 多權限判斷

```tsx
// 滿足任一權限即可
<Can permission={['products.edit', 'products.delete']}>
  <div>管理操作</div>
</Can>

// 必須滿足所有權限
import { CanAll } from "@/Components/Permission/Can";

<CanAll permissions={['products.edit', 'products.delete']}>
  <button>完整管理</button>
</CanAll>
```

---

## 10. 通知訊息規範

### 10.1 使用 Toast 通知

使用 `sonner` 的 `toast` 進行通知：

```tsx
import { toast } from 'sonner';

// 成功訊息
toast.success('操作成功');

// 錯誤訊息
toast.error('操作失敗');

// 資訊訊息
toast.info('提示訊息');

// 警告訊息
toast.warning('警告訊息');
```

### 10.2 常見操作的 Toast 訊息

```tsx
// 新增成功
router.post(route('resource.store'), data, {
  onSuccess: () => toast.success('新增成功'),
  onError: () => toast.error('新增失敗，請檢查輸入內容'),
});

// 更新成功
router.put(route('resource.update', id), data, {
  onSuccess: () => toast.success('更新成功'),
  onError: () => toast.error('更新失敗'),
});

// 刪除成功
router.delete(route('resource.destroy', id), {
  onSuccess: () => toast.success('已刪除'),
  onError: () => toast.error('刪除失敗，請檢查權限'),
});
```

---

## 11. 表單規範

### 11.1 表單容器

```tsx
<div className="bg-white rounded-xl border border-gray-200 p-6 space-y-6">
  <form onSubmit={handleSubmit}>
    {/* 表單欄位 */}
  </form>
</div>
```

### 11.2 表單欄位

```tsx
<div>
  <label className="block text-sm font-medium text-slate-700 mb-2">
    欄位名稱 <span className="text-red-500">*</span>
  </label>
  <input
    type="text"
    value={data.field}
    onChange={(e) => setData("field", e.target.value)}
    placeholder="請輸入..."
    className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-main focus:border-primary-main"
  />
  {errors.field && <p className="mt-1 text-sm text-red-500">{errors.field}</p>}
</div>
```

### 11.3 下拉選單

使用 `SearchableSelect` 元件：

```tsx
import { SearchableSelect } from "@/Components/ui/searchable-select";

<SearchableSelect
  value={data.category_id}
  onValueChange={(value) => setData("category_id", value)}
  options={categories.map(cat => ({ label: cat.name, value: String(cat.id) }))}
  placeholder="請選擇分類"
  searchThreshold={10}  // 超過 10 個選項才顯示搜尋框
/>
```

---

## 11.4 對話框 (Dialog) 滾動與佈局

當對話框內容可能超出螢幕高度時（如長表單或詳細資料），**請勿使用 `ScrollArea`**，應直接在 `DialogContent` 使用原生的 `overflow-y-auto`。

**原因**：`ScrollArea` 在 Flex 佈局計算高度時容易失效或導致雙重滾動條。以及與原生捲動行為不一致。

```tsx
// ❌ 錯誤：使用 ScrollArea 或固定高度計算
<DialogContent className="max-w-3xl">
  <ScrollArea className="h-[500px]">
    {/* 內容 */}
  </ScrollArea>
</DialogContent>

// ✅ 正確：直接使用 overflow-y-auto 與 max-h
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
  <DialogHeader>...</DialogHeader>
  <form className="p-6">
    {/* 內容會自動滾動 */}
  </form>
  <DialogFooter>...</DialogFooter>
</DialogContent>
```

---

## 11.5 輸入框尺寸 (Input Sizes)

為確保介面整齊與統一，所有表單輸入元件標準高度應為 **`h-9`** (36px)，與標準按鈕尺寸對齊。

- **Input**: 預設即為 `h-9` (由 `py-1` 與 `text-sm` 組合而成)
- **Select / SearchableSelect**: 必須確保 Trigger 按鈕高度為 `h-9`
- **禁止使用**: 除非有特殊設計需求，否則避免使用 `h-10` (40px) 或其他非標準高度。

## 11.6 日期輸入框樣式 (Date Input Style)

日期輸入框應採用「**左側裝飾圖示 + 右側原生操作**」的配置，以保持視覺一致性並保留瀏覽器原生便利性。

**樣式規格**：
1.  **容器**: 使用 `relative` 定位。
2.  **圖標**: 使用 `Calendar` 圖標，放置於絕對位置 `absolute left-2.5 top-2.5`，顏色 `text-gray-400`，並設定 `pointer-events-none` 避免干擾點擊。
3.  **輸入框**: 設定 `pl-9` (左內距) 以避開圖示，並使用原生 `type="date"` 或 `type="datetime-local"`。

```tsx
import { Calendar } from "lucide-react";

## 11.7 金額與數字輸入規範

所有涉及金額（單價、成本、總價）的輸入框，應遵循以下規範以確保操作體驗一致：

1.  **HTML 屬性**：
    *   `type="number"`
    *   `min="0"` (除非業務邏輯允許負數)
    *   `step="any"` (設置為 `any` 可允許任意小數，且瀏覽器預設按上下鍵時會增減 **1** 並保留小數部分，例如 37.2 -> 38.2)
    *   **步進值 (Step)**: 金額與數量輸入框均應設定 `step="any"`，以支援小數點輸入（除非業務邏輯強制整數）。
    *   `placeholder="0"`
2.  **樣式類別**：
    *   預設靠左對齊 (不需要 `text-right`)，亦可依版面需求調整。

### 9.2 對齊方式 (Alignment)

依據欄位所在的情境區分對齊方式：

- **明細列表/表格 (Details/Table)**：金額與數量欄位一律 **靠右對齊 (text-right)**。
    - 包含：採購單明細、庫存盤點表、調撥單明細等 Table 內的輸入框。
- **一般表單/新增欄位 (Form/Input)**：金額與數量欄位一律 **靠左對齊 (text-left)**。
    - 包含：商品資料設定、新增表單中的獨立欄位。亦可依版面需求調整。
3.  **行為邏輯**：
    *   輸入時允許輸入小數點。
    *   鍵盤上下鍵調整時，瀏覽器會預設增減 1 (搭配 `step="any"`)。

```tsx
<Input
  type="number"
  min="0"
  step="any"
  value={price}
  onChange={(e) => setPrice(parseFloat(e.target.value) || 0)}
  placeholder="0"
/>
```

<div className="relative">
  <Calendar className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400 pointer-events-none" />
  <Input
    type="date"
    className="pl-9 block w-full"
    value={date}
    onChange={(e) => setDate(e.target.value)}
  />
</div>
```

## 11.7 搜尋選單樣式 (SearchableSelect Style)

`SearchableSelect` 元件在表單或篩選列中使用時，高度必須設定為 `h-9` 以與輸入框對齊。

```tsx
<SearchableSelect
  className="h-9" // 確保高度一致
  // ...other props
/>
```

## 11.8 篩選列規範 (Filter Bar Norms)

列表頁面的篩選區域（Filter Bar）應遵循以下規範以節省空間並保持層級清晰：

1.  **標籤文字 (Labels)**: 使用 **`text-xs`** (`12px`) 大小，顏色建議使用 `text-gray-500` 或 `text-grey-2`。這與一般表單 (`text-sm`) 不同，目的是降低篩選列的視覺權重。
2.  **輸入元件高度**: 統一使用 **`h-9`** (`36px`)。
3.  **佈局**:
    - **容器內距**: 統一使用 **`p-5`** (`20px`)。
    - **Grid 間距**: 建議使用 **`gap-4`** (`16px`) 或 `gap-6` (`24px`)，但同一專案內需統一。本專案推薦 **`gap-4`**。
    - **垂直間距**: Label 與 Input 之間使用 **`space-y-1`** (`4px`)。
    - **排版**: 建議使用 Grid 系統 (`grid-cols-12`) 進行排版。

```tsx
<div className="space-y-1">
  <Label className="text-xs font-medium text-grey-2">關鍵字搜尋</Label>
  <Input className="h-9" placeholder="..." />
</div>
```

4.  **操作按鈕區 (Action Bar)**:
    - **位置**: 位於篩選列最下方。
    - **樣式**: 統一使用 `flex items-center justify-end border-t border-grey-4 pt-5 gap-3`。
    - **說明**: `border-grey-4` 為標準通用邊框色，`pt-5` 與容器 padding (`p-5`) 呼應，維持視覺平衡。

5.  **收合模式 (Collapsible Mode)**:
    - **目的**: 節省垂直空間，預設隱藏較佔空間與低頻使用的篩選器（如日期區間）。
    - **實作**:
        - 預設狀態：若無相關篩選值，則預設為 **收合 (Collapsed)**。
        - 切換按鈕：位於 Action Bar 左側 (`mr-auto`)。
        - 樣式：Ghost Button + `ChevronDown`/`ChevronUp` Icon + 提示圓點 (Indicator)。
    - **邏輯**: 若載入頁面時已有被隱藏的篩選值 (e.g. `date_start`)，則強制 **展開 (Expanded)** 或顯示提示。

---

## 12. 檢查清單

在開發或審查頁面時，請確認以下項目：

### ✅ 按鈕
- [ ] 使用 `button-filled-*` 或 `button-outlined-*` 類別
- [ ] 主要操作使用 `button-filled-primary`
- [ ] 編輯操作使用 `button-outlined-primary`
- [ ] 刪除操作使用 `button-outlined-error`
- [ ] 按鈕尺寸正確（sm/default/lg）
- [ ] 包含適當的圖標

### ✅ 圖標
- [ ] 全部使用 `lucide-react`
- [ ] 尺寸正確（h-3/h-4/h-5/h-6）
- [ ] 顏色與上下文一致

### ✅ 表格
- [ ] 使用 `@/Components/ui/table` 元件
- [ ] 有 `bg-white rounded-xl border` 容器
- [ ] 標題列有 `bg-gray-50` 背景
- [ ] 序號欄固定寬度並置中
- [ ] 操作欄使用 `flex justify-center gap-2`
- [ ] 空狀態訊息置中顯示

### ✅ 分頁
- [ ] 使用 `@/Components/shared/Pagination`
- [ ] 有每頁筆數選擇器（10/20/50/100）

### ✅ 權限
- [ ] 所有操作按鈕都用 `<Can>` 包裹
- [ ] 權限命名符合 `resource.action` 格式

### ✅ 通知
- [ ] 使用 `toast` 提供操作反饋
- [ ] 成功/錯誤訊息明確

### ✅ 整體
- [ ] 頁面有標準頭部（標題 + 圖標 + 說明 + 新增按鈕）
- [ ] 容器寬度使用 `max-w-7xl`
- [ ] 使用正確的佈局（`AuthenticatedLayout`）

---

## 13. 常見錯誤與修正

### ❌ 錯誤：自定義按鈕樣式

```tsx
// ❌ 錯誤
<Button className="bg-green-500 text-white hover:bg-green-600">
  新增
</Button>

// ✅ 正確
<Button className="button-filled-primary">
  <Plus className="h-4 w-4 mr-2" />
  新增
</Button>
```

### ❌ 錯誤：混用圖標庫

```tsx
// ❌ 錯誤
import { FaEdit } from 'react-icons/fa';
<FaEdit />

// ✅ 正確
import { Pencil } from 'lucide-react';
<Pencil className="h-4 w-4" />
```

### ❌ 錯誤：操作欄未置中

```tsx
// ❌ 錯誤
<TableCell>
  <Button>編輯</Button>
  <Button>刪除</Button>
</TableCell>

// ✅ 正確
<TableCell className="text-center">
  <div className="flex items-center justify-center gap-2">
    <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>
  </div>
</TableCell>
```

### ❌ 錯誤：缺少權限控制

```tsx
// ❌ 錯誤
<Button onClick={handleDelete}>刪除</Button>

// ✅ 正確
<Can permission="resource.delete">
  <Button 
    variant="outline" 
    size="sm" 
    className="button-outlined-error"
    onClick={handleDelete}
  >
    <Trash2 className="h-4 w-4" />
  </Button>
</Can>
```

---

## 14. 參考範例

以下頁面展示了完整的 UI 統一性實踐：

- **使用者管理**：`resources/js/Pages/Admin/User/Index.tsx`
- **角色管理**：`resources/js/Pages/Admin/Role/Index.tsx`
- **產品管理**：`resources/js/Pages/Product/Index.tsx`
- **倉庫管理**：`resources/js/Pages/Warehouse/Index.tsx`

---

## 總結

遵循本規範可確保：

1. ✅ **視覺一致性**：所有頁面看起來像同一個系統
2. ✅ **維護效率**：使用統一組件，修改一處即可影響全局
3. ✅ **開發速度**：有明確的模式可循，減少決策時間
4. ✅ **使用者體驗**：一致的互動模式降低學習成本
5. ✅ **安全性**：統一的權限控制確保資料安全

當你在開發或審查 Star ERP 客戶端後台的 UI 時，請務必參考此規範！

---

## 15. 批次匯入彈窗規範 (Batch Import Dialog)

為了確保系統中所有批次匯入功能（如：商品、庫存、客戶）的體驗一致，必須遵循以下 UI 結構與樣式。

### 15.1 標題結構

- **樣式**：保持簡潔，僅使用文字標題，不帶額外圖示。
- **文字**：統一為「匯入XXXX資料」。

```tsx
<DialogHeader>
    <DialogTitle>匯入商品資料</DialogTitle>
    <DialogDescription>
        請先下載範本，填寫完畢後上傳檔案進行批次處理。
    </DialogDescription>
</DialogHeader>
```

### 15.2 分步引導區塊 (Step-by-Step Guide)

匯入流程必須分為三個清晰的步驟區塊：

#### 步驟 1：取得匯入範本
- **容器樣式**：`bg-gray-50 rounded-lg border border-gray-100 p-4 space-y-2`
- **標題圖示**：`<FileSpreadsheet className="w-4 h-4 text-green-600" />`
- **下載按鈕**：`variant="outline" size="sm" className="w-full sm:w-auto button-outlined-primary"`，並明確標註 `.xlsx`。

#### 步驟 2：設定資訊 (選甜)
- **容器樣式**：`space-y-2`
- **標題圖示**：`<Info className="w-4 h-4 text-primary-main" />`
- **欄位樣式**：使用標準 `Input`，標籤文字使用 `text-sm text-gray-700`。
- **預設值**：若有備註欄位，應提供合適的預設值（例如：「Excel 匯入」）。

#### 步驟 3：上傳填寫後的檔案
- **容器樣式**：`space-y-2`
- **標題圖示**：`<FileUp className="w-4 h-4 text-blue-600" />`
- **Input 樣式**：`type="file"`，並開啟 `cursor-pointer`。

### 15.3 規則說明面板 (Accordion Rules)

詳細的填寫說明必須收納於 `Accordion` 中，避免干擾主流程：

- **樣式**：標準灰色邊框，不使用特殊背景色 (如琥珀色)。
- **容器**：`className="w-full border rounded-lg px-2"`
- **觸發文字**：`text-sm text-gray-500`

```tsx
<Accordion type="single" collapsible className="w-full border rounded-lg px-2">
    <AccordionItem value="rules" className="border-b-0">
        <AccordionTrigger className="text-sm text-gray-500 hover:no-underline py-3">
            <div className="flex items-center gap-2">
                <Info className="h-4 w-4" />
                匯入規則與提示
            </div>
        </AccordionTrigger>
        <AccordionContent>
            <div className="text-sm text-gray-600 space-y-2 pb-2 pl-6">
                <ul className="list-disc space-y-1">
                    <li>使用加粗文字標註關鍵欄位：<span className="font-medium text-gray-700">關鍵字</span></li>
                    <li>說明文字簡潔明瞭。</li>
                </ul>
            </div>
        </AccordionContent>
    </AccordionItem>
</Accordion>
```

### 15.4 底部操作 (Footer)

- **取消按鈕**：`variant="outline"`，且為 `button-outlined-primary`。
- **提交按鈕**：`button-filled-primary`，且在處理中時顯示 `Loader2`。

---

## 16. 詳情頁面項目清單規範 (Detail Page Item List Standards)

為了確保詳情頁面（如：採購單詳情、進貨單詳情、銷售匯入詳情）的資訊層級清晰且視覺統一，所有項目清單必須遵循以下規範。

### 16.1 容器結構 (Container Structure)

項目清單應封裝在一個帶有內距的卡片容器中，而不是讓表格直接緊貼外層卡片邊緣。

1. **外層卡片**：`bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden`
2. **標題區塊**：`p-6 border-b border-gray-100 bg-gray-50/30`
3. **內容內距**：標題下方的內容區塊應加上 `p-6`。
4. **表格包裹層**：表格應再包裹一層 `border rounded-lg overflow-hidden`，以確保表格內部的邊角與隔線視覺完整。

```tsx
<div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">
    {/* 標題 */}
    <div className="p-6 border-b border-gray-100 bg-gray-50/30">
        <h2 className="text-lg font-bold text-gray-900">項目清單標題</h2>
    </div>
    
    {/* 內容區塊 */}
    <div className="p-6">
        <div className="border rounded-lg overflow-hidden">
            <Table>
                <TableHeader>
                    <TableRow className="bg-gray-50 hover:bg-gray-50">
                        {/* 標頭欄位 */}
                    </TableRow>
                </TableHeader>
                <TableBody>
                    {/* 表格內容 */}
                </TableBody>
            </Table>
        </div>
        
        {/* 若有分頁，直接放在 p-6 容器內，並加 mt-6 分隔 */}
        <div className="mt-6">
            <Pagination ... />
        </div>
    </div>
</div>
```

### 16.2 表格樣式細節 (Table Styling)

1. **標頭背景**：`TableHeader` 的第一個 `TableRow` 應使用 `bg-gray-50 hover:bg-gray-50` 強化視覺區隔。
2. **文字顏色**：主體文字使用 `text-gray-900`（標題/重要數據）或 `text-gray-500`（輔助/序號）。
3. **數據對齊**：
    *   **數量/序號**：文字置中 (`text-center`) 或依據數據類型對齊。
    *   **金額**：金額欄位必須使用 `text-right` 並視情況加粗 (`font-bold`) 或加上 `text-primary-main` 顏色。
4. **表格隔線**：確保表格具有清晰但不過於突出的水平隔線，提升長列表的可讀性。

