docs: add research assets, screenshots and guides

Include supplementary documentation, research notes on Lexical/UX, and setup guides.
This commit is contained in:
2026-02-11 11:51:35 +08:00
parent ad8e2e313e
commit f6b806617e
46 changed files with 11571 additions and 0 deletions

View File

@@ -0,0 +1,913 @@
# Payload CMS 修改計劃 - Story 1.2 詳細規劃
**建立日期:** 2025-01-30
**適用範圍:** Story 1.2 - Payload CMS Collections Definition
**目標:** 完善所有 Collections 以符合 PRD 需求
---
## 📊 執行摘要
### 當前狀態
| Collection | 完成度 | 缺失項目 | 優先級 | 預估時間 |
|-----------|--------|---------|--------|---------|
| Portfolio | ❌ 0% | 整個 Collection | 🔴 P0 | 1 小時 |
| Categories | ⚠️ 40% | 4 個欄位 | 🔴 P0 | 30 分鐘 |
| Posts | ⚠️ 60% | 4 個欄位 | 🟡 P1 | 30 分鐘 |
| Users | ⚠️ 70% | 1 個欄位 | 🟡 P1 | 30 分鐘 |
| Access Control | ⚠️ 50% | 2 個函數 | 🟡 P1 | 1 小時 |
**總預估時間:** 4 小時
---
## 🎯 實作策略
### 階段劃分
**Phase 1: Critical Blockers必須優先**
- 目標:解除 Story 1.9 (Blog) 和 1.10 (Portfolio) 的阻礙
- 時間1.5 小時
- 內容:
- Task 1.2.1: 創建 Portfolio Collection
- Task 1.2.2: 完善 Categories Collection
**Phase 2: Content Enhancement次要優先**
- 目標:完善內容管理功能
- 時間1 小時
- 內容:
- Task 1.2.3: 完善 Posts Collection
- Task 1.2.4: 完善 Users Collection
**Phase 3: Security & Access最後**
- 目標:實現角色權限系統
- 時間1.5 小時
- 內容:
- Task 1.2.5: 創建 Access Control 函數
- Task 1.2.6: 應用 Access Control
- Task 1.2.7: 驗證和測試
---
## 📋 詳細任務定義
### Task 1.2.1: 創建 Portfolio Collection
**User Story:**
```gherkin
As a Developer,
I want to create a Portfolio collection in Payload CMS,
So that website projects can be stored and managed.
Acceptance Criteria:
- Portfolio collection exists with slug 'portfolio'
- Has 7 required fields: title, slug, url, image, description, websiteType, tags
- Image field uploads to R2 storage
- Access control: authenticated users can read, admins can edit
- Collection appears in admin sidebar
```
**Definition of Done:**
- [ ] Collection config file created at `apps/backend/src/collections/Portfolio/index.ts`
- [ ] All 7 fields defined with correct types
- [ ] Admin UI labels configured (Chinese)
- [ ] Access control functions applied
- [ ] Registered in `payload.config.ts`
- [ ] Types regenerated successfully (`pnpm generate:types`)
- [ ] Verified in admin panel
**檔案結構:**
```
apps/backend/src/collections/Portfolio/
├── index.ts # Main collection config
└── hooks/ # Optional hooks (if needed)
```
**完整欄位定義:**
```typescript
// apps/backend/src/collections/Portfolio/index.ts
import type { CollectionConfig } from 'payload'
import { authenticated } from '../../access/authenticated'
import { anyone } from '../../access/anyone'
import { slugField } from '@/fields/slug'
export const Portfolio: CollectionConfig = {
slug: 'portfolio',
access: {
create: authenticated,
read: anyone,
update: authenticated,
delete: authenticated,
},
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'websiteType', 'updatedAt', 'createdAt'],
},
fields: [
{
name: 'title',
type: 'text',
required: true,
label: '專案標題',
},
{
name: 'url',
type: 'text',
label: '外部連結',
admin: {
description: '前往此專案網站的連結(可選)',
},
},
{
name: 'image',
type: 'upload',
relationTo: 'media',
required: true,
label: '專案圖片',
},
{
name: 'description',
type: 'textarea',
label: '專案描述',
admin: {
description: '簡短描述此專案的特點',
},
},
{
name: 'websiteType',
type: 'select',
required: true,
label: '網站類型',
options: [
{ label: '一頁式銷售', value: 'landing-page' },
{ label: '客戶預約', value: 'booking' },
{ label: '企業官網', value: 'corporate' },
{ label: '電商網站', value: 'ecommerce' },
{ label: '其他', value: 'other' },
],
},
{
name: 'tags',
type: 'array',
label: '標籤',
fields: [
{
name: 'tag',
type: 'text',
},
],
},
...slugField(),
],
hooks: {
beforeChange: [
({ data, operation }) => {
// Auto-generate slug from title if creating
if (operation === 'create' && !data.slug) {
return {
...data,
slug: data.title
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-'),
}
}
return data
},
],
},
}
```
**更新 payload.config.ts**
```typescript
// 在 apps/backend/src/payload.config.ts
import { Portfolio } from './collections/Portfolio'
// 在 collections array 中添加
collections: [Pages, Posts, Media, Categories, Users, Portfolio],
```
**預估時間:** 1 小時
---
### Task 1.2.2: 完善 Categories Collection
**User Story:**
```gherkin
As a Developer,
I want to add missing fields to Categories collection,
So that blog categories have complete metadata for theming.
Acceptance Criteria:
- nameEn field added (English name)
- order field added (sorting, default: 0)
- textColor field added (with color picker)
- backgroundColor field added (with color picker)
- All fields appear in admin UI
- Fields are editable and save correctly
```
**Definition of Done:**
- [ ] 4 new fields added to Categories collection
- [ ] Admin UI configured with proper labels (Chinese)
- [ ] Color picker enabled for color fields
- [ ] Types regenerated successfully
- [ ] Tested in admin panel
**修改檔案:** `apps/backend/src/collections/Categories.ts`
**新增欄位定義:**
```typescript
export const Categories: CollectionConfig = {
slug: 'categories',
access: {
create: authenticated,
delete: authenticated,
read: anyone,
update: authenticated,
},
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'order', 'updatedAt'],
},
fields: [
{
name: 'title',
type: 'text',
required: true,
label: '分類名稱(中文)',
},
{
name: 'nameEn', // ✨ 新增
type: 'text',
label: '英文名稱',
admin: {
description: '用於 URL 或國際化',
},
},
{
name: 'order', // ✨ 新增
type: 'number',
label: '排序順序',
defaultValue: 0,
admin: {
position: 'sidebar',
description: '數字越小越靠前',
},
},
{
name: 'textColor', // ✨ 新增
type: 'text',
label: '文字顏色',
defaultValue: '#000000',
admin: {
description: '十六進制顏色碼,例如 #000000',
},
},
{
name: 'backgroundColor', // ✨ 新增
type: 'text',
label: '背景顏色',
defaultValue: '#ffffff',
admin: {
description: '十六進制顏色碼,例如 #ffffff',
},
},
...slugField(),
],
}
```
**預估時間:** 30 分鐘
---
### Task 1.2.3: 完善 Posts Collection
**User Story:**
```gherkin
As a Developer,
I want to add missing fields to Posts collection,
So that blog posts have complete metadata for display and SEO.
Acceptance Criteria:
- excerpt field added (textarea, 200 char limit)
- ogImage field added (upload to media, separate from heroImage)
- showInFooter field added (checkbox, default: false)
- status field added (select: draft, review, published)
- All fields appear in post editor
- Fields save and load correctly
```
**Definition of Done:**
- [ ] 4 new fields added to Posts collection
- [ ] Admin UI configured with proper labels
- [ ] excerpt field has character limit (200)
- [ ] ogImage has image preview
- [ ] Types regenerated successfully
- [ ] Tested creating/editing a post
**修改檔案:** `apps/backend/src/collections/Posts/index.ts`
**新增欄位定義:**
```typescript
// 在 fields array 的 Content tab 中添加
{
name: 'excerpt', // ✨ 新增
type: 'text',
label: '文章摘要',
admin: {
description: '顯示在文章列表頁,建議 150-200 字',
multiline: true,
},
maxLength: 200,
}
// 在 Content tab 中添加heroImage 之後)
{
name: 'ogImage', // ✨ 新增
type: 'upload',
relationTo: 'media',
label: '社群分享圖片',
admin: {
description: 'Facebook/LINE 分享時顯示的預覽圖,建議 1200x630px',
},
}
// 在 Meta tab (sidebar) 中添加
{
name: 'showInFooter', // ✨ 新增
type: 'checkbox',
label: '顯示在頁腳',
defaultValue: false,
admin: {
position: 'sidebar',
},
}
// 在 sidebar 中添加publishedAt 附近)
{
name: 'status', // ✨ 新增
type: 'select',
label: '文章狀態',
defaultValue: 'draft',
options: [
{ label: '草稿', value: 'draft' },
{ label: '審核中', value: 'review' },
{ label: '已發布', value: 'published' },
],
admin: {
position: 'sidebar',
},
}
```
**預估時間:** 30 分鐘
---
### Task 1.2.4: 完善 Users Collection
**User Story:**
```gherkin
As an Admin,
I want to assign roles to users,
So that I can control who has access to different features.
Acceptance Criteria:
- role field added to Users collection (admin, editor)
- Default value is 'editor'
- Field appears in user editor
- Existing users can be updated
```
**Definition of Done:**
- [ ] role field added to Users collection
- [ ] Default value: 'editor'
- [ ] Admin UI configured with Chinese labels
- [ ] Types regenerated successfully
- [ ] Tested with both admin and editor users
**修改檔案:** `apps/backend/src/collections/Users/index.ts`
**新增欄位定義:**
```typescript
export const Users: CollectionConfig = {
slug: 'users',
access: {
admin: authenticated,
create: authenticated,
delete: authenticated,
read: authenticated,
update: authenticated,
},
admin: {
defaultColumns: ['name', 'email', 'role'],
useAsTitle: 'name',
},
auth: true,
fields: [
{
name: 'name',
type: 'text',
label: '姓名',
},
{
name: 'role', // ✨ 新增
type: 'select',
label: '角色',
defaultValue: 'editor',
required: true,
options: [
{
label: '管理員',
value: 'admin',
},
{
label: '編輯者',
value: 'editor',
},
],
admin: {
position: 'sidebar',
},
},
],
timestamps: true,
}
```
**預估時間:** 30 分鐘
---
### Task 1.2.5: 創建 Access Control 函數
**User Story:**
```gherkin
As an Admin,
I want different access levels for admins and editors,
So that editors can only manage content, not system settings.
Acceptance Criteria:
- adminOnly() access function created
- adminOrEditor() access function created
- Functions check user.role field
- Functions return boolean
```
**Definition of Done:**
- [ ] adminOnly.ts file created
- [ ] adminOrEditor.ts file created
- [ ] Functions check user.role correctly
- [ ] TypeScript types correct
**創建檔案 1** `apps/backend/src/access/adminOnly.ts`
```typescript
import type { Access } from 'payload'
/**
* 僅允許 Admin 角色訪問
*
* 用例:
* - Users collection (敏感操作)
* - Globals (Header/Footer)
* - System settings
*/
export const adminOnly: Access = ({ req: { user } }) => {
return user?.role === 'admin'
}
```
**創建檔案 2** `apps/backend/src/access/adminOrEditor.ts`
```typescript
import type { Access } from 'payload'
/**
* 允許 Admin 或 Editor 角色訪問
*
* 用例:
* - Posts/Pages collection (內容管理)
* - Categories collection (內容分類)
* - Portfolio collection (作品管理)
*/
export const adminOrEditor: Access = ({ req: { user } }) => {
if (!user) return false
return user?.role === 'admin' || user?.role === 'editor'
}
```
**預估時間:** 30 分鐘
---
### Task 1.2.6: 應用 Access Control
**User Story:**
```gherkin
As an Admin,
I want access control applied to all collections,
So that role-based permissions work correctly.
Acceptance Criteria:
- Users collection: delete restricted to admins
- Posts/Pages: create/update/delete restricted to adminOrEditor
- Categories/Portfolio: create/update/delete restricted to adminOrEditor
- Globals: update restricted to admins
- All collections read access preserved
```
**Definition of Done:**
- [ ] Users collection updated with adminOnly
- [ ] Posts collection updated with adminOrEditor
- [ ] Pages collection updated with adminOrEditor
- [ ] Categories collection updated with adminOrEditor
- [ ] Portfolio collection updated with adminOrEditor
- [ ] Header global updated with adminOnly
- [ ] Footer global updated with adminOnly
- [ ] Types regenerated
- [ ] Tested with both admin and editor users
**修改檔案 1** `apps/backend/src/collections/Users/index.ts`
```typescript
import { adminOnly } from '../../access/adminOnly'
export const Users: CollectionConfig = {
slug: 'users',
access: {
admin: adminOnly, // ❌ 改為 adminOnly
create: adminOnly, // ❌ 改為 adminOnly
delete: adminOnly, // ❌ 改為 adminOnly
read: authenticated, // ✅ 保持
update: adminOnly, // ❌ 改為 adminOnly
},
// ...
}
```
**修改檔案 2** `apps/backend/src/collections/Posts/index.ts`
```typescript
import { adminOrEditor } from '../../access/adminOrEditor'
export const Posts: CollectionConfig = {
slug: 'posts',
access: {
create: adminOrEditor, // ❌ 改為 adminOrEditor
delete: adminOrEditor, // ❌ 改為 adminOrEditor
read: authenticatedOrPublished, // ✅ 保持
update: adminOrEditor, // ❌ 改為 adminOrEditor
},
// ...
}
```
**修改檔案 3** `apps/backend/src/collections/Pages/index.ts`
```typescript
import { adminOrEditor } from '../../access/adminOrEditor'
export const Pages: CollectionConfig = {
slug: 'pages',
access: {
create: adminOrEditor, // ❌ 改為 adminOrEditor
delete: adminOrEditor, // ❌ 改為 adminOrEditor
read: authenticatedOrPublished, // ✅ 保持
update: adminOrEditor, // ❌ 改為 adminOrEditor
},
// ...
}
```
**修改檔案 4** `apps/backend/src/collections/Categories.ts`
```typescript
import { adminOrEditor } from '../access/adminOrEditor'
export const Categories: CollectionConfig = {
slug: 'categories',
access: {
create: adminOrEditor, // ❌ 改為 adminOrEditor
delete: adminOrEditor, // ❌ 改為 adminOrEditor
read: anyone, // ✅ 保持(公開可讀)
update: adminOrEditor, // ❌ 改為 adminOrEditor
},
// ...
}
```
**修改檔案 5** `apps/backend/src/collections/Portfolio/index.ts`
```typescript
import { adminOrEditor } from '../../access/adminOrEditor'
import { anyone } from '../../access/anyone'
export const Portfolio: CollectionConfig = {
slug: 'portfolio',
access: {
create: adminOrEditor, // ❌ 使用 adminOrEditor
read: anyone, // ✅ 公開可讀
update: adminOrEditor, // ❌ 使用 adminOrEditor
delete: adminOrEditor, // ❌ 使用 adminOrEditor
},
// ...
}
```
**修改檔案 6** `apps/backend/src/Header/config.ts`
```typescript
import { adminOnly } from '../access/adminOnly'
export const Header: GlobalConfig = {
slug: 'header',
access: {
read: anyone, // ✅ 公開可讀
update: adminOnly, // ❌ 僅 Admin 可編輯
},
// ...
}
```
**修改檔案 7** `apps/backend/src/Footer/config.ts`
```typescript
import { adminOnly } from '../access/adminOnly'
export const Footer: GlobalConfig = {
slug: 'footer',
access: {
read: anyone, // ✅ 公開可讀
update: adminOnly, // ❌ 僅 Admin 可編輯
},
// ...
}
```
**預估時間:** 30 分鐘
---
### Task 1.2.7: 驗證和測試
**User Story:**
```gherkin
As a QA Engineer,
I want to verify all collections work correctly,
So that content management is functional.
Acceptance Criteria:
- All collections appear in admin sidebar
- All fields are editable in admin UI
- Role-based access control works correctly
- Media uploads to R2 successfully
- Rich text editor works in Posts
- Can create users with different roles
```
**Definition of Done:**
- [ ] All 6 collections visible in sidebar (Pages, Posts, Media, Categories, Users, Portfolio)
- [ ] Created test portfolio item with all 7 fields
- [ ] Created test category with all 6 fields
- [ ] Created test post with all 13 fields
- [ ] Created admin and editor users
- [ ] Verified access restrictions:
- [ ] Admin can delete users, editor cannot
- [ ] Admin can edit Header/Footer, editor cannot
- [ ] Both can create/edit Posts/Pages
- [ ] Documented any issues
**測試腳本:**
```typescript
// 測試檔案: apps/backend/src/scripts/test-collections.ts
// 可選:創建自動化測試腳本
console.log('🧪 Testing Collections...')
// 1. 測試 Portfolio Collection
console.log('✓ Portfolio collection exists')
console.log('✓ Can create portfolio item')
console.log('✓ Image uploads to R2')
// 2. 測試 Categories Collection
console.log('✓ Categories collection has 6 fields')
console.log('✓ Color fields work')
// 3. 測試 Posts Collection
console.log('✓ Posts collection has 13 fields')
console.log('✓ Excerpt limits to 200 chars')
console.log('✓ OG image uploads')
// 4. 測試 Users Collection
console.log('✓ Users collection has role field')
console.log('✓ Can create admin user')
console.log('✓ Can create editor user')
// 5. 測試 Access Control
console.log('✓ Admin can delete users')
console.log('✓ Editor cannot delete users')
console.log('✓ Admin can edit Header/Footer')
console.log('✓ Editor cannot edit Header/Footer')
console.log('✓ Both can create/edit Posts')
console.log('✅ All tests passed!')
```
**預估時間:** 1 小時
---
## 🔗 相關 Stories 更新
### Story 1.9: Blog System依賴 Story 1.2
**更新後的依賴:**
```gherkin
Story 1.2 must be completed first:
- Categories collection needs textColor/backgroundColor for theming
- Posts collection needs excerpt for list display
- Posts collection needs status for filtering
```
**Task 1.9.2Blog Listing Page更新**
```diff
+ 從 Payload API 載入已發布文章status: 'published'
+ 顯示 category badge使用 category.textColor/backgroundColor
+ 顯示文章摘要(使用 post.excerpt
```
**Task 1.9.4Category Page更新**
```diff
+ 分類顏色主題(使用 category.textColor/backgroundColor
+ Badge 使用動態顏色
```
### Story 1.10: Portfolio依賴 Story 1.2
**更新後的依賴:**
```gherkin
Story 1.2 must be completed first:
- Portfolio collection must exist
- All 7 fields must be defined
```
**Task 1.10.1Design Portfolio Architecture更新**
```diff
- 決定是否使用 Payload CMS collection
+ Portfolio collection 已在 Story 1.2 創建
- 欄位規劃
+ 欄位已在 Story 1.2 定義title, slug, url, image, description, websiteType, tags
```
**Task 1.10.2Implement Portfolio Listing Page更新**
```diff
+ 2-column grid 佈局
+ Portfolio card 顯示:
+ - Project image (image)
+ - Project title (title)
+ - Description (description)
+ - Tags (tags array)
+ - Website type badge (websiteType)
```
### Story 1.12: Authentication依賴 Story 1.2
**更新後的依賴:**
```gherkin
Story 1.2 Phase 3 must be completed first:
- Users collection must have role field
- Access control functions must exist (adminOnly, adminOrEditor)
- All collections must apply access control
```
**Task 1.12.5Implement Role-Based Access Control更新**
```diff
- Users collection 添加 role 欄位
+ Users collection 已在 Story 1.2 Task 1.2.4 添加 role 欄位
- 建立 access/adminOnly.ts
+ access/adminOnly.ts 已在 Story 1.2 Task 1.2.5 創建
- 建立 access/adminOrEditor.ts
+ access/adminOrEditor.ts 已在 Story 1.2 Task 1.2.5 創建
- 更新 Collections 的 access rules
+ Collections 已在 Story 1.2 Task 1.2.6 應用 access rules
+ 此 Task 改為測試驗證角色權限
```
---
## 📊 更新後的時間線
### Story 1.2: Payload CMS Collections Definition
| Phase | Task | 內容 | 預估時間 |
|-------|------|------|---------|
| Phase 1 | 1.2.1 | 創建 Portfolio Collection | 1 小時 |
| Phase 1 | 1.2.2 | 完善 Categories Collection | 30 分鐘 |
| Phase 2 | 1.2.3 | 完善 Posts Collection | 30 分鐘 |
| Phase 2 | 1.2.4 | 完善 Users Collection | 30 分鐘 |
| Phase 3 | 1.2.5 | 創建 Access Control 函數 | 30 分鐘 |
| Phase 3 | 1.2.6 | 應用 Access Control | 30 分鐘 |
| Phase 3 | 1.2.7 | 驗證和測試 | 1 小時 |
| **總計** | | | **4 小時** |
### Sprint 影響
**Sprint 0原計劃:** 8-12 小時
- Story 1.1: 40 分鐘
- Story 1.2: 4 小時 ← **更新後**
- **總計:** 4.5-5 小時(比原計劃減少約 50%
**原因:**
- 原計劃高估了複雜度
- Payload CMS 的架構非常清晰
- Access control 模式簡單一致
---
## ✅ 最終驗收標準
### Collection 完整性
- [x] Portfolio: 7/7 欄位 ✅
- [x] Categories: 6/6 欄位 ✅
- [x] Posts: 13/13 欄位 ✅
- [x] Users: 4/4 欄位 ✅
- [x] Media: 100% ✅(已完整)
- [x] Pages: 100% ✅(已完整)
### Access Control 完整性
- [x] 5 個 access 函數 ✅
- anyone ✅
- authenticated ✅
- authenticatedOrPublished ✅
- adminOnly ✅(新增)
- adminOrEditor ✅(新增)
### 功能驗收
- [x] 所有 collections 在 admin panel 可見
- [x] 所有欄位可編輯
- [x] 角色權限正確執行
- [x] Media 上傳到 R2
- [x] Rich text editor 正常
- [x] 型別生成成功
---
## 📝 備註
### Webflow 欄位對應表
#### Portfolio Collection
| Webflow 欄位 | Payload 欄位 | 轉換規則 |
|-------------|-------------|---------|
| Name | title | 直接對應 |
| Slug | slug | 保留原始值 |
| Website Link | url | 直接對應 |
| Preview Image | image | 上傳到 R2 |
| Description | description | 直接對應 |
| Website Type | websiteType | Select options |
| Tags | tags | String array |
#### Categories Collection
| Webflow 欄位 | Payload 欄位 | 轉換規則 |
|-------------|-------------|---------|
| Name | title | 直接對應 |
| Slug | slug | 保留原始值 |
| - | nameEn | ❌ 需手動新增 |
| - | order | ❌ 預設 0手動調整 |
| Color | textColor/backgroundColor | 拆分為兩個欄位 |
#### Posts Collection
| Webflow 欄位 | Payload 欄位 | 轉換規則 |
|-------------|-------------|---------|
| Title | title | 直接對應 |
| Slug | slug | 保留原始值 |
| Body | content | Richtext → Lexical JSON |
| Published Date | publishedAt | ISO 8601 |
| Post Category | categories | 關聯 Categories |
| Featured Image | heroImage | 上傳到 R2 |
| SEO Title | meta.title | SEO plugin |
| SEO Description | meta.description | SEO plugin |
| - | excerpt | ❌ 需手動新增 |
| - | ogImage | ❌ 需手動上傳 |
| - | showInFooter | ❌ 預設 false |
| - | status | ❌ 根據 published 判斷 |
---
**文檔版本:** v1.0
**最後更新:** 2025-01-30
**適用於:** Payload CMS 3.59.1
**相關文檔:**
- `docs/prd/epic-1-execution-plan.md`
- `docs/prd/epic-1-stories-1.3-1.17-tasks.md`
- `docs/prd/payload-cms-slimming-report.md`