# 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) |
