docs: separate documentation and specs into initial commit
Establish baseline for project documentation including BMAD specs, PRD, and system architecture notes.
This commit is contained in:
465
_bmad-output/implementation-artifacts/1-8-contact-page.story.md
Normal file
465
_bmad-output/implementation-artifacts/1-8-contact-page.story.md
Normal file
@@ -0,0 +1,465 @@
|
||||
# Story 1-8: Contact Page with Form (聯絡頁面與表單)
|
||||
|
||||
**Status:** ready-for-dev
|
||||
|
||||
**Epic:** Epic 1 - Webflow to Payload CMS + Astro Migration
|
||||
|
||||
**Priority:** P1 (High - Key conversion page)
|
||||
|
||||
**Estimated Time:** 6-8 hours
|
||||
|
||||
**Dependencies:** Story 1.4 (Global Layout Components)
|
||||
|
||||
---
|
||||
|
||||
## Story
|
||||
|
||||
**作為** 潛在客戶,
|
||||
**我想要** 透過表單聯絡恩群數位,
|
||||
**這樣** 我就能詢問他們的服務並開始合作。
|
||||
|
||||
## Context
|
||||
|
||||
這是一個關鍵的轉換頁面!聯絡表單是潛在客戶與恩群數位建立關係的第一步。我們需要建立一個功能完整、視覺吸引且用戶友善的聯絡頁面,表單提交透過 Cloudflare Worker 處理並發送 Email 通知。
|
||||
|
||||
**Story Source:**
|
||||
- `docs/prd/05-epic-stories.md` - Story 1.8
|
||||
- `docs/prd/epic-1-stories-1.3-1.17-tasks.md` - Story 1.8 詳細任務
|
||||
|
||||
**聯絡資訊:**
|
||||
- 電話: 02-55700527
|
||||
- Email: enchuntaiwan@gmail.com
|
||||
- Facebook: (需從原網站取得連結)
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
### AC1 - Contact Page in Payload CMS
|
||||
在 Payload CMS 中建立 contact-us 頁面,包含:
|
||||
- Hero section: 標題「聯絡我們」
|
||||
- 表單區塊標題和說明文字
|
||||
- 聯絡資訊區塊內容
|
||||
- CTA 區塊內容
|
||||
|
||||
### AC2 - Contact Form Fields
|
||||
表單包含以下欄位:
|
||||
- **Name** (姓名) - 必填
|
||||
- **Email** (電子郵件) - 必填,需驗證格式
|
||||
- **Phone** (電話) - 選填
|
||||
- **Message** (訊息內容) - 必填,textarea
|
||||
- **Service Interest** (感興趣的服務) - 下拉選單
|
||||
|
||||
### AC3 - Client-Side Validation
|
||||
- 即時欄位驗證
|
||||
- Email 格式驗證
|
||||
- 必填欄位檢查
|
||||
- 錯誤訊息顯示
|
||||
- Submit 按鈕 loading state
|
||||
|
||||
### AC4 - Form Submission Logic
|
||||
- API route: `/api/contact`
|
||||
- Cloudflare Worker 處理表單提交
|
||||
- Email 發送 (使用 Resend)
|
||||
- Spam protection:
|
||||
- Honeypot field
|
||||
- Rate limiting (每 IP 每小時最多 3 次)
|
||||
- 錯誤處理和重試機制
|
||||
|
||||
### AC5 - Success/Error Display
|
||||
- 成功訊息:「感謝您的留言,我們會盡快回覆您!」
|
||||
- 錯誤訊息:「發生錯誤,請稍後再試或直接撥打 02-55700527」
|
||||
- 訊息顯示後表單重置
|
||||
|
||||
### AC6 - Contact Info Display
|
||||
顯示以下聯絡資訊:
|
||||
- 電話: 02-55700527 (可點擊 tel: 連結)
|
||||
- Email: enchuntaiwan@gmail.com (可點擊 mailto: 連結)
|
||||
- Facebook 連結 (圖示 + 文字)
|
||||
|
||||
### AC7 - CTA Section
|
||||
- 醒目的行動呼籲區塊
|
||||
- 標題和描述文字
|
||||
- 主要 CTA 按鈕設計
|
||||
|
||||
### AC8 - Visual Fidelity
|
||||
視覺保真度 ≥ 95% 與 Webflow 原網站相比
|
||||
|
||||
## Tasks / Subtasks
|
||||
|
||||
### Task 1.8.1: Create Contact Page in Payload CMS (1h)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] 在 Pages collection 建立 contact-us 頁面
|
||||
- [ ] Slug: `contact-us`
|
||||
- [ ] Hero section:
|
||||
- Title: "聯絡我們"
|
||||
- Description: 可選的副標題
|
||||
- [ ] Contact form section:
|
||||
- 表單標題
|
||||
- 表單說明文字
|
||||
- [ ] Contact info section:
|
||||
- Phone: 02-55700527
|
||||
- Email: enchuntaiwan@gmail.com
|
||||
- Facebook link
|
||||
- [ ] CTA section 內容
|
||||
- [ ] SEO meta tags 配置
|
||||
|
||||
**Definition of Done:**
|
||||
- [ ] contact-us 頁面在 Payload admin 可編輯
|
||||
- [ ] 所有 sections 可編輯
|
||||
- [ ] Preview 功能正常
|
||||
|
||||
---
|
||||
|
||||
### Task 1.8.2: Create contact-us.astro Route (1h)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] 建立 `apps/frontend/src/pages/contact-us.astro`
|
||||
- [ ] 使用 MainLayout (來自 Story 1.4)
|
||||
- [ ] 從 Payload API 載入頁面內容
|
||||
- [ ] SEO meta tags 動態生成
|
||||
- [ ] 錯誤處理 (404 或 API 失敗)
|
||||
|
||||
**Definition of Done:**
|
||||
- [ ] /contact-us 路由正常運作
|
||||
- [ ] 從 Payload 成功載入內容
|
||||
- [ ] 錯誤處理正常
|
||||
|
||||
---
|
||||
|
||||
### Task 1.8.3: Implement Contact Form Component (2h)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] 建立 `ContactForm.astro` 組件
|
||||
- [ ] 表單欄位實作:
|
||||
- Name input (text, required)
|
||||
- Email input (email, required)
|
||||
- Phone input (tel, optional)
|
||||
- Message textarea (required)
|
||||
- Service Interest select (dropdown)
|
||||
- [ ] Client-side validation:
|
||||
- HTML5 validation attributes
|
||||
- JavaScript 即時驗證
|
||||
- 錯誤訊息顯示
|
||||
- [ ] Submit 按鈕:
|
||||
- Loading state (提交中)
|
||||
- Disabled state (驗證失敗或提交中)
|
||||
- [ ] 成功/錯誤訊息顯示區塊
|
||||
|
||||
**Service Interest 下拉選單選項:**
|
||||
- Google 商家關鍵字
|
||||
- Google Ads
|
||||
- 社群代操
|
||||
- 論壇行銷
|
||||
- 網紅行銷
|
||||
- 形象影片
|
||||
- 網站設計
|
||||
- 其他
|
||||
|
||||
**Definition of Done:**
|
||||
- [ ] ContactForm 組件完成
|
||||
- [ ] 所有欄位正常運作
|
||||
- [ ] 驗證功能正常
|
||||
- [ ] UI/UX 流暢
|
||||
|
||||
---
|
||||
|
||||
### Task 1.8.4: Implement Form Submission Logic (2h)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] 建立 API route `/api/contact`
|
||||
- [ ] 接收 POST 請求:
|
||||
- Body: { name, email, phone, message, serviceInterest }
|
||||
- [ ] Server-side validation:
|
||||
- 檢查必填欄位
|
||||
- Email 格式驗證
|
||||
- Honeypot 檢查 (spam detection)
|
||||
- [ ] Rate limiting:
|
||||
- 每個 IP 每小時最多 3 次提交
|
||||
- [ ] Email 發送 (使用 Resend):
|
||||
- To: enchuntaiwan@gmail.com
|
||||
- Subject: 「聯絡表單: {name}」
|
||||
- Body: 包含所有表單資料
|
||||
- [ ] 錯誤處理:
|
||||
- 驗證錯誤 → 400
|
||||
- Rate limit → 429
|
||||
- Email 發送失敗 → 500
|
||||
- [ ] 成功回應 → 200
|
||||
|
||||
**Email 格式範例:**
|
||||
```markdown
|
||||
# 新的聯絡表單提交
|
||||
|
||||
**姓名:** {name}
|
||||
**Email:** {email}
|
||||
**電話:** {phone}
|
||||
**感興趣的服務:** {serviceInterest}
|
||||
|
||||
**訊息內容:**
|
||||
{message}
|
||||
|
||||
---
|
||||
提交時間: {timestamp}
|
||||
IP: {ip}
|
||||
```
|
||||
|
||||
**Definition of Done:**
|
||||
- [ ] API route 正常運作
|
||||
- [ ] Email 成功發送
|
||||
- [ ] Spam protection 運作
|
||||
- [ ] Rate limiting 運作
|
||||
- [ ] 錯誤處理完善
|
||||
|
||||
---
|
||||
|
||||
### Task 1.8.5: Implement Contact Info Display (0.5h)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] 建立 `ContactInfo.astro` 組件
|
||||
- [ ] Phone 顯示:
|
||||
- 圖示 (電話)
|
||||
- 文字: 02-55700527
|
||||
- 可點擊 tel:02-55700527
|
||||
- [ ] Email 顯示:
|
||||
- 圖示 (信封)
|
||||
- 文字: enchuntaiwan@gmail.com
|
||||
- 可點擊 mailto:enchuntaiwan@gmail.com
|
||||
- [ ] Facebook 顯示:
|
||||
- 圖示 (Facebook)
|
||||
- 連結文字: 「恩群數位 Facebook」
|
||||
|
||||
**Definition of Done:**
|
||||
- [ ] ContactInfo 組件完成
|
||||
- [ ] 所有連結可點擊
|
||||
- [ ] 響應式佈局
|
||||
|
||||
---
|
||||
|
||||
### Task 1.8.6: Implement CTA Section (0.5h)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] 使用共用 CTASection 組件 (來自 Story 1.5)
|
||||
- [ ] 標題和描述從 Payload CMS 載入
|
||||
- [ ] CTA 按鈕設計一致
|
||||
|
||||
**Definition of Done:**
|
||||
- [ ] CTA section 正常顯示
|
||||
- [ ] 與其他頁面設計一致
|
||||
|
||||
---
|
||||
|
||||
### Task 1.8.7: Testing and Validation (1h)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] 表單驗證測試:
|
||||
- 必填欄位空白時顯示錯誤
|
||||
- Email 格式錯誤時顯示錯誤
|
||||
- 選填欄位空白可正常提交
|
||||
- [ ] 表單提交測試:
|
||||
- 成功提交顯示成功訊息
|
||||
- 表單重置
|
||||
- Email 收到正確內容
|
||||
- [ ] Spam protection 測試:
|
||||
- Honeypot 填寫被拒絕
|
||||
- Rate limiting 運作
|
||||
- [ ] 錯誤處理測試:
|
||||
- API 錯誤顯示錯誤訊息
|
||||
- 網路錯誤顯示錯誤訊息
|
||||
- [ ] 視覺保真度測試:
|
||||
- 與 Webflow 原網站對比
|
||||
- 保真度 ≥ 95%
|
||||
|
||||
**Definition of Done:**
|
||||
- [ ] 所有測試通過
|
||||
- [ ] 無阻塞性問題
|
||||
- [ ] 視覺符合預期
|
||||
|
||||
## Dev Technical Guidance
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
apps/frontend/src/
|
||||
├── pages/
|
||||
│ └── contact-us.astro ← Create this
|
||||
├── components/
|
||||
│ └── contact/
|
||||
│ ├── ContactForm.astro ← Create this
|
||||
│ └── ContactInfo.astro ← Create this
|
||||
├── styles/
|
||||
│ └── contact.css ← Create this (optional)
|
||||
|
||||
apps/backend/src/
|
||||
├── routes/
|
||||
│ └── contact.ts ← Create this (API route)
|
||||
└── email/
|
||||
└── contact-template.ts ← Create this (email template)
|
||||
```
|
||||
|
||||
### ContactForm.astro 組件架構
|
||||
|
||||
```typescript
|
||||
// apps/frontend/src/components/contact/ContactForm.astro
|
||||
|
||||
interface Props {
|
||||
services?: string[]
|
||||
}
|
||||
|
||||
const { services = defaultServices } = Astro.props
|
||||
|
||||
// Form state
|
||||
let formState = 'idle' // 'idle' | 'submitting' | 'success' | 'error'
|
||||
let errors = {}
|
||||
|
||||
// Honeypot field (hidden from users, visible to bots)
|
||||
<input
|
||||
type="text"
|
||||
name="website_url"
|
||||
class="hidden"
|
||||
tabindex="-1"
|
||||
autocomplete="off"
|
||||
/>
|
||||
```
|
||||
|
||||
### API Route 實作
|
||||
|
||||
```typescript
|
||||
// apps/backend/src/routes/contact.ts
|
||||
|
||||
import type { APIRoute } from 'astro'
|
||||
import Resend from 'resend'
|
||||
|
||||
const resend = new Resend(import.meta.env.RESEND_API_KEY)
|
||||
|
||||
const rateLimiter = new Map<string, { count: number; resetTime: number }>()
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
const body = await request.json()
|
||||
const ip = request.headers.get('x-forwarded-for') || 'unknown'
|
||||
|
||||
// Rate limiting
|
||||
// Validation
|
||||
// Honeypot check
|
||||
// Email sending
|
||||
// Response
|
||||
}
|
||||
```
|
||||
|
||||
### 環境變數
|
||||
|
||||
```bash
|
||||
# .env
|
||||
RESEND_API_KEY=re_xxxxxxxxxxxxx
|
||||
CONTACT_TO_EMAIL=enchuntaiwan@gmail.com
|
||||
CONTACT_FROM_EMAIL=noreply@enchun.tw
|
||||
```
|
||||
|
||||
### Design Tokens
|
||||
|
||||
參考 Webflow 原網站的聯絡頁面設計:
|
||||
- 表單欄位樣式
|
||||
- 按鈕樣式
|
||||
- 間距和佈局
|
||||
- 響應式斷點
|
||||
|
||||
## Dev Notes
|
||||
|
||||
### Architecture Patterns
|
||||
- Astro SSR mode for API routes
|
||||
- Resend for email delivery (已選擇的服務)
|
||||
- Honeypot + rate limiting for spam protection
|
||||
- Client-side validation for better UX
|
||||
|
||||
### Source Tree Components
|
||||
- `apps/frontend/src/pages/contact-us.astro` - Main contact page
|
||||
- `apps/frontend/src/components/contact/` - Contact-specific components
|
||||
- `apps/backend/src/routes/contact.ts` - Form submission API
|
||||
|
||||
### Testing Standards
|
||||
- Manual form validation testing
|
||||
- Email receiving test
|
||||
- Spam protection testing
|
||||
- Visual fidelity comparison (95%+)
|
||||
|
||||
### References
|
||||
- [Source: docs/prd/05-epic-stories.md#Story-1.8](docs/prd/05-epic-stories.md) - Story requirements
|
||||
- [Source: docs/prd/epic-1-stories-1.3-1.17-tasks.md#Story-1.8](docs/prd/epic-1-stories-1.3-1.17-tasks.md) - Detailed tasks
|
||||
- [Source: research/www.enchun.tw/contact-us.html](../../research/www.enchun.tw/contact-us.html) - Original Webflow page
|
||||
|
||||
### Previous Story Intelligence
|
||||
|
||||
**From Story 1.4 (Global Layout):**
|
||||
- MainLayout 組件已建立
|
||||
- Header 和 Footer 可用
|
||||
- 響應式導航已完成
|
||||
|
||||
**From Story 1.5 (Homepage):**
|
||||
- CTASection 組件可重用
|
||||
- 表單樣式模式可參考
|
||||
|
||||
### Technology Constraints
|
||||
- Astro 4.x SSR mode
|
||||
- Payload CMS 3.x for content
|
||||
- Resend for email delivery
|
||||
- TypeScript strict mode
|
||||
|
||||
### Known Issues to Avoid
|
||||
- ⚠️ 不要在 client-side 暴露 API keys
|
||||
- ⚠️ 不要忘記 rate limiting (容易被濫用)
|
||||
- ⚠️ 不要使用同步 email 發送 (會阻塞 response)
|
||||
- ⚠️ 不要忽視 spam protection (會收到大量垃圾訊息)
|
||||
- ⚠️ 不要在錯誤訊息中暴露系統資訊
|
||||
|
||||
### Service Interest Dropdown Options
|
||||
|
||||
根據原網站的服務項目,下拉選單應包含:
|
||||
1. Google 商家關鍵字
|
||||
2. Google Ads
|
||||
3. 社群代操
|
||||
4. 論壇行銷
|
||||
5. 網紅行銷
|
||||
6. 形象影片
|
||||
7. 網站設計
|
||||
8. 其他
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|------------|
|
||||
| Email service downtime | Low | High | Implement fallback logging |
|
||||
| Spam abuse | Medium | Medium | Honeypot + rate limiting + reCAPTCHA (optional) |
|
||||
| Form UX issues | Low | Medium | Thorough testing, clear error messages |
|
||||
| Visual inconsistencies | Low | Low | Use design tokens from Webflow |
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- [ ] contact-us.astro 頁面建立完成
|
||||
- [ ] ContactForm 組件完成
|
||||
- [ ] ContactInfo 組件完成
|
||||
- [ ] API route `/api/contact` 正常運作
|
||||
- [ ] Email 發送功能正常
|
||||
- [ ] Spam protection 運作
|
||||
- [ ] 所有測試通過
|
||||
- [ ] 視覺保真度 ≥ 95%
|
||||
- [ ] Code follows existing patterns
|
||||
- [ ] No linting errors
|
||||
- [ ] sprint-status.yaml updated to mark story as ready-for-dev
|
||||
|
||||
## Dev Agent Record
|
||||
|
||||
### Agent Model Used
|
||||
*To be filled by Dev Agent*
|
||||
|
||||
### Debug Log References
|
||||
*To be filled by Dev Agent*
|
||||
|
||||
### Completion Notes
|
||||
*To be filled by Dev Agent*
|
||||
|
||||
### File List
|
||||
*To be filled by Dev Agent*
|
||||
|
||||
## Change Log
|
||||
|
||||
| Date | Action | Author |
|
||||
|------|--------|--------|
|
||||
| 2026-01-31 | Story created with comprehensive context | SM Agent (Bob) |
|
||||
Reference in New Issue
Block a user