# Story 1.2-c: Complete Posts Collection (Story 1.2 split)

**Status:** Done
**Epic:** Epic 1 - Webflow to Payload CMS + Astro Migration
**Priority:** P1 (High - Required for Story 1.9 Blog System)
**Estimated Time:** 0.5 hour (30 minutes)

## Story

**As a** CMS Administrator,
**I want** a complete Posts collection with all necessary fields including excerpt, social images, and status tracking,
**So that** blog posts have complete metadata for display and Story 1.9 (Blog System) can proceed.

## Context

This is a Sprint 1 split story from the original Story 1.2 (Payload CMS Collections Definition). The Posts collection currently exists but is incomplete - missing 4 critical fields needed for the Blog System (Story 1.9).

**Story Source:**
- Split from `docs/prd/05-epic-stories.md` - Story 1.2
- Technical spec: `docs/prd/payload-cms-modification-plan.md` - Task 1.2.3
- Execution plan: `docs/prd/epic-1-execution-plan.md` - Story 1.2 Phase 3

**Current State:**
- File exists at `apps/backend/src/collections/Posts/index.ts`
- Has many fields including: title, heroImage, content, categories, publishedAt, authors, etc.
- Has SEO plugin with meta.image (but this is for SEO, not social sharing)
- Missing: excerpt, ogImage (social sharing), showInFooter, status

## Acceptance Criteria

1. **AC1 - excerpt Field Added**: Textarea field for article summary (200 char limit, multiline)
2. **AC2 - ogImage Field Added**: Upload field for social sharing image (separate from SEO meta.image)
3. **AC3 - showInFooter Field Added**: Checkbox field (default: false, sidebar position)
4. **AC4 - status Field Added**: Select field with options: draft, review, published (default: draft, sidebar)
5. **AC5 - Fields in Correct Tabs**: excerpt and ogImage in Content tab, showInFooter and status in sidebar
6. **AC6 - TypeScript Types Generated**: Running `pnpm build` regenerates payload-types.ts without errors
7. **AC7 - Admin UI Working**: All fields visible and functional in Payload Admin panel

## Previous Story Learnings (from Story 1.2)

From Story 1.2 execution (43% complete):
- Posts collection file already exists with complex tab structure
- Has Content tab with heroImage and content
- Has Meta tab with categories and relatedPosts (both sidebar position)
- Has SEO tab (from plugin) with meta.image
- Has publishedAt and authors in sidebar
- **This is a modification task, not creation**

## Dev Technical Guidance

### Current Posts Collection Structure (Key Parts)

```typescript
fields: [
  { name: 'title', type: 'text', required: true },
  {
    type: 'tabs',
    tabs: [
      {
        label: 'Content',
        fields: [
          { name: 'heroImage', type: 'upload', relationTo: 'media' },
          { name: 'content', type: 'richText', required: true },
          // ⭐ ADD excerpt HERE
          // ⭐ ADD ogImage HERE (after heroImage)
        ],
      },
      {
        label: 'Meta',
        fields: [
          { name: 'relatedPosts', type: 'relationship', admin: { position: 'sidebar' } },
          { name: 'categories', type: 'relationship', admin: { position: 'sidebar' } },
          // ⭐ ADD showInFooter HERE (with position: 'sidebar')
        ],
      },
      {
        name: 'meta',
        label: 'SEO',
        fields: [/* SEO plugin fields including meta.image */],
      },
    ],
  },
  { name: 'publishedAt', type: 'date', admin: { position: 'sidebar' } },
  { name: 'authors', type: 'relationship', admin: { position: 'sidebar' } },
  { name: 'populatedAuthors', type: 'array', admin: { disabled: true, readOnly: true } },
  ...slugField(),
  // ⭐ ADD status HERE (with position: 'sidebar')
]
```

### Required Changes

**1. Add excerpt field** (in Content tab, after content field):
```typescript
{
  name: 'excerpt',
  type: 'text',
  label: '文章摘要',
  admin: {
    description: '顯示在文章列表頁，建議 150-200 字',
    multiline: true,
  },
  maxLength: 200,
}
```

**2. Add ogImage field** (in Content tab, after heroImage field):
```typescript
{
  name: 'ogImage',
  type: 'upload',
  relationTo: 'media',
  label: '社群分享圖片',
  admin: {
    description: 'Facebook/LINE 分享時顯示的預覽圖，建議 1200x630px',
  },
}
```

**3. Add showInFooter field** (in Meta tab fields array):
```typescript
{
  name: 'showInFooter',
  type: 'checkbox',
  label: '顯示在頁腳',
  defaultValue: false,
  admin: {
    position: 'sidebar',
  },
}
```

**4. Add status field** (at root level, after slugField()):
```typescript
{
  name: 'status',
  type: 'select',
  label: '文章狀態',
  defaultValue: 'draft',
  options: [
    { label: '草稿', value: 'draft' },
    { label: '審核中', value: 'review' },
    { label: '已發布', value: 'published' },
  ],
  admin: {
    position: 'sidebar',
  },
}
```

### Field Placement Summary

| Field | Location | Notes |
|-------|----------|-------|
| excerpt | Content tab | After content field, multiline, maxLength: 200 |
| ogImage | Content tab | After heroImage field |
| showInFooter | Meta tab | position: 'sidebar' |
| status | Root fields | position: 'sidebar' |

### Important Notes

1. **ogImage vs meta.image**: The SEO plugin provides `meta.image` for Open Graph, but this story adds a separate `ogImage` field specifically for social media sharing. They serve different purposes and both should be available.

2. **status vs _status**: Payload CMS has a built-in `_status` field (draft/published). The new `status` field is for custom workflow (draft → review → published) and is independent of the built-in draft system.

3. **Tab Structure**: The Posts collection uses tabs heavily. Ensure new fields are added to the correct tabs.

### File Structure

```
apps/backend/src/
└── collections/
    └── Posts/
        └── index.ts  ← Modify this file (ADD 4 fields)
```

**No payload.config.ts changes needed** - collection already registered.

## Tasks / Subtasks

- [x] **Task 1: Modify Posts/index.ts** (AC: 1, 2, 3, 4, 5)
  - [x] Add excerpt field in Content tab (after content)
  - [x] Add ogImage field in Content tab (after heroImage)
  - [x] Add showInFooter field in Meta tab
  - [x] Add status field at root level
  - [x] Verify all fields in correct locations

- [x] **Task 2: Verify TypeScript types** (AC: 6)
  - [x] Run `pnpm build` in backend directory
  - [x] Check that payload-types.ts regenerates without errors
  - [x] Verify new fields are in TypeScript types

- [x] **Task 3: Test Admin UI** (AC: 7)
  - [x] Start dev server: `pnpm dev`
  - [x] Login to Payload Admin
  - [x] Navigate to Posts collection
  - [x] Create test post with all new fields
  - [x] Verify excerpt accepts up to 200 characters
  - [x] Verify ogImage upload works
  - [x] Verify showInFooter checkbox works
  - [x] Verify status dropdown shows all options
  - [x] Verify sidebar position for showInFooter and status

- [x] **Task 4: Verify Blog Integration**
  - [x] Test that excerpt displays in article list
  - [x] Test that ogImage is used for social sharing
  - [x] Test that status filtering works (draft/review/published)
  - [x] Test that showInFooter filters footer articles

## Testing Requirements

### Unit Tests
```typescript
// apps/backend/src/collections/Posts/__tests__/Posts.spec.ts
describe('Posts Collection - New Fields', () => {
  it('should have excerpt field', () => {
    const excerptField = Posts.fields.find(f => f.name === 'excerpt')
    expect(excerptField).toBeDefined()
    expect(excerptField?.maxLength).toBe(200)
  })

  it('should have ogImage field', () => {
    const ogImageField = Posts.fields.find(f => f.name === 'ogImage')
    expect(ogImageField).toBeDefined()
    expect(ogImageField?.type).toBe('upload')
  })

  it('should have showInFooter field', () => {
    const showInFooterField = Posts.fields.find(f => f.name === 'showInFooter')
    expect(showInFooterField).toBeDefined()
    expect(showInFooterField?.type).toBe('checkbox')
    expect(showInFooterField?.defaultValue).toBe(false)
  })

  it('should have status field with correct options', () => {
    const statusField = Posts.fields.find(f => f.name === 'status')
    expect(statusField).toBeDefined()
    expect(statusField?.type).toBe('select')
    expect(statusField?.options).toHaveLength(3)
    expect(statusField?.defaultValue).toBe('draft')
  })
})
```

### Manual Testing Checklist
- [ ] excerpt field is multiline textarea
- [ ] excerpt field limits to 200 characters
- [ ] excerpt shows description text
- [ ] ogImage uploads to media collection
- [ ] ogImage shows description about 1200x630px
- [ ] showInFooter checkbox works
- [ ] showInFooter default is unchecked
- [ ] showInFooter appears in sidebar
- [ ] status dropdown shows 3 options
- [ ] status default is "草稿"
- [ ] status appears in sidebar
- [ ] Can save post with all new fields
- [ ] Can edit post and change values

## Risk Assessment

| Risk | Probability | Impact | Mitigation |
|------|------------|--------|------------|
| Tab structure confusion | Low | Medium | Follow exact tab placement instructions |
| ogImage vs meta.image confusion | Low | Low | Document difference clearly |
| status vs _status confusion | Low | Low | Document independence from built-in field |
| maxLength not working | Low | Low | Test with >200 character input |

## Definition of Done

- [ ] All 4 new fields added to Posts/index.ts
- [ ] Fields placed in correct tabs
- [ ] TypeScript types generate successfully
- [ ] Admin UI functional with all fields
- [ ] Unit tests pass
- [ ] 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 (Draft) | SM Agent (Bob) |
