{post.title}
{post.excerpt?.slice(0, 150)}...
# Story 1-9: Blog System Implementation (Blog System Implementation) **Status:** ready-for-dev **Epic:** Epic 1 - Webflow to Payload CMS + Astro Migration **Priority:** P1 (High - Content Marketing Core) **Estimated Time:** 16 hours ## Story **作為一位** 訪客 (Visitor), **我想要** 瀏覽行銷文章和見解, **以便** 我可以從恩群數位的專業知識中學習。 ## Context Yo 各位開發者!這是我們部落格系統的實作故事!Payload CMS 的 Posts 集合已經準備好了,Categories 也設置完成了,現在是時候讓它們在前台發光發亮啦!Webflow 的 /news 頁面就是你的設計藍圖,目標是 95%+ 的視覺相似度! **Story Source:** - `docs/prd/05-epic-stories.md` - Story 1.9 - sprint-status.yaml - "1-9-blog-system" - 依賴:Story 1-2 (Collections Definition), Story 1-4 (Global Layout) **原始 HTML 參考:** - [Source: research/www.enchun.tw/news.html](../../research/www.enchun.tw/news.html) - Blog 列表頁 - [Source: research/www.enchun.tw/xing-xiao-fang-da-jing/](../../research/www.enchun.tw/xing-xiao-fang-da-jing/) - Blog 文章資料夾 - [Source: research/www.enchun.tw/wen-zhang-fen-lei](../../research/www.enchun.tw/wen-zhang-fen-lei) - Blog 分類資料夾 **Existing Infrastructure (Ready to Use!):** - Posts collection at `apps/backend/src/collections/Posts/index.ts` with fields: title, slug, heroImage, ogImage, content (richText), excerpt, categories, relatedPosts, publishedAt, authors, status - Categories collection at `apps/backend/src/collections/Categories.ts` with theming colors - Placeholder pages already exist at `news.astro` and `xing-xiao-fang-da-jing/[slug].astro` ## Acceptance Criteria ### Blog Listing Page (`/blog` or `/news`) 1. **AC1 - Display Published Posts**: Only posts with status='published' are displayed 2. **AC2 - Category Filter**: 4 category filter buttons that filter posts dynamically 3. **AC3 - Article Cards**: Each card displays: - Featured image (heroImage) - Title (linked to detail page) - Excerpt (truncated to ~150 chars) - Category badge with category color theming - Published date (formatted in Traditional Chinese) 4. **AC4 - Pagination**: Implement pagination (12 posts per page) with page navigation 5. **AC5 - Visual Fidelity**: Design matches Webflow news.html with 95%+ similarity ### Article Detail Page (`/blog/[slug]` or `/xing-xiao-fang-da-jing/[slug]`) 1. **AC6 - Full Content Display**: Rich text content rendered with proper styling 2. **AC7 - Meta Information**: Category badge and published date displayed 3. **AC8 - Related Articles**: Show 3-4 related posts from same category 4. **AC9 - Social Sharing**: OG tags configured (ogImage, ogTitle, ogDescription) 5. **AC10 - Rich Text Rendering**: Lexical editor output matches Webflow formatting ### Category Page (`/blog/category/[slug]` or `/wen-zhang-fen-lei/[slug]`) 1. **AC11 - Category Filtering**: Shows only posts belonging to selected category 2. **AC12 - Category Description**: Displays category name and description 3. **AC13 - Color Theming**: Page uses category's textColor and backgroundColor ## Dev Technical Guidance ### URL Structure Decision **Important:** Choose between these URL patterns: - Option A: `/blog` + `/blog/[slug]` + `/blog/category/[slug]` (Clean, SEO-friendly) - Option B: `/news` + `/xing-xiao-fang-da-jing/[slug]` + `/wen-zhang-fen-lei/[slug]` (Matches Webflow) **Recommendation:** Use Option A for new SEO, set up 301 redirects from Webflow URLs. ### Architecture Patterns **Data Fetching Pattern (Astro SSR):** ```typescript // apps/frontend/src/pages/blog/index.astro --- import Layout from '../../layouts/Layout.astro' import { payload } from '@payload/client' // Or use API endpoint const PAGE_SIZE = 12 const page = Astro.url.searchParams.get('page') || '1' const category = Astro.url.searchParams.get('category') // Fetch from Payload API const response = await fetch(`${import.meta.env.PAYLOAD_CMS_URL}/api/posts?where[status][equals]=published&page=${page}&limit=${PAGE_SIZE}&depth=1`) const data = await response.json() const posts = data.docs const totalPages = data.totalPages --- ``` **Rich Text Rendering Pattern:** ```typescript // Payload Lexical to HTML converter needed import { serializeLexical } from '@/utilities/serializeLexical' // In template
``` ### File Structure ``` apps/frontend/src/ ├── pages/ │ ├── blog/ │ │ ├── index.astro ← Blog listing page (CREATE) │ │ ├── [slug].astro ← Article detail page (CREATE/UPDATE) │ │ └── category/ │ │ └── [slug].astro ← Category page (CREATE/UPDATE) ├── components/ │ └── blog/ │ ├── ArticleCard.astro ← Reusable article card (CREATE) │ ├── CategoryFilter.astro ← Category filter buttons (CREATE) │ ├── RelatedPosts.astro ← Related posts section (CREATE) │ └── ShareButtons.astro ← Social sharing buttons (CREATE - optional) ├── lib/ │ └── api.ts ← API client utilities (UPDATE) └── utilities/ └── serializeLexical.ts ← Lexical to HTML converter (CREATE) ``` ### Component Specifications #### 1. ArticleCard.astro ```astro --- interface Props { post: { title: string slug: string heroImage: { url: string } | null excerpt: string categories: Array<{ title: string, slug: string, backgroundColor: string, textColor: string }> publishedAt: string } } const { post } = Astro.props const formatDate = (date: string) => { return new Date(date).toLocaleDateString('zh-TW', { year: 'numeric', month: 'long', day: 'numeric' }) } const category = post.categories?.[0] ---{post.excerpt?.slice(0, 150)}...
沒有找到文章
)} {totalPages > 1 && ( )}{category.nameEn}
)}此分類暫無文章
)}