# 03. Blog Route With DB Integration, Featured Post, And Chronological Feed meta: id: landing-pages-and-admin-03 feature: landing-pages-and-admin priority: P1 depends_on: [landing-pages-and-admin-02] tags: [implementation, routes, blog, database, tRPC] objective: - Refactor the `/blog` route to fetch posts from the database via tRPC instead of hardcoded data, support a featured post display at the top, and show remaining posts in chronological order with tag filtering and pagination. deliverables: - Rewritten `/web/src/routes/blog.tsx` with database-backed data - tRPC query `blog.list` for fetching posts with filtering, pagination, and featured flag - Featured post hero section at top of blog listing - Chronological feed with tag filtering and pagination - Updated `/web/src/routes/blog/[slug].tsx` to fetch from database - tRPC query `blog.bySlug` for fetching individual post - tRPC mutation `blog.incrementViews` for tracking view counts steps: - Create tRPC router procedure `blog.list`: - Accept optional `tag`, `limit`, `offset` parameters - Query `blogPosts` table for published posts only - Order by `publishedAt` descending (chronological) - Return posts with all fields including `featured` flag - Create tRPC router procedure `blog.bySlug`: - Accept `slug` parameter - Query `blogPosts` table for published post by slug - Increment `viewCount` on each view - Return post with related posts (same tags, excluding current) - Create tRPC router procedure `blog.tags`: - Return all unique tags from published posts with counts - Refactor `/blog.tsx`: - Replace hardcoded `blogPosts` array with tRPC query - Add featured post section at top (large card with full excerpt, shown only if a post is marked featured) - Show remaining posts in chronological grid below featured post - Preserve tag filtering UI (fetch tags from database) - Preserve pagination ("Load More Posts" button) - Add loading states and error handling - Refactor `/blog/[slug].tsx`: - Replace hardcoded data with tRPC query by slug - Preserve markdown rendering, author sidebar, related posts, social sharing - Add 404 handling for non-existent slugs - Track view count on page load - Add database migration for `featured` column on `blogPosts` table - Ensure `publishedAt` is properly set on all existing posts tests: - Unit: tRPC queries return correct data shapes - Integration: Blog list page loads posts from database - Integration: Featured post displays at top when one is marked featured - Integration: Tag filtering correctly filters posts - Integration: Pagination loads next batch of posts - Integration: Individual post page loads by slug and increments view count - Integration: 404 shown for non-existent slug acceptance_criteria: - `/blog` fetches posts from `blogPosts` database table (no hardcoded data) - One post can be marked as `featured` and displays prominently at the top of the blog listing - Remaining posts display in chronological order (newest first) - Tag filtering works with tags sourced from the database - Pagination ("Load More") loads additional posts in batches - `/blog/[slug]` fetches individual post from database - View count increments each time a post is viewed - Related posts section shows posts with matching tags - 404 page shown for non-existent blog slugs - Loading states displayed while data is being fetched validation: - `cd /Users/mike/Code/Kordant/web && pnpm dev` then navigate to `/blog` - Verify posts load from database (not hardcoded) - Mark a post as featured via admin and verify it appears at top - Test tag filtering by clicking different tag buttons - Click "Load More" and verify additional posts load - Navigate to individual post and verify content renders correctly - Refresh post page and verify view count increments - Navigate to non-existent slug and verify 404 notes: - Current blog schema in `marketing.ts` has: id, slug, title, excerpt, content, authorName, coverImageUrl, tags (JSON), published, publishedAt, viewCount - Need to add `featured` boolean column (default: false) - `publishedAt` should be used for chronological ordering - `tags` is stored as JSON array in database - Existing markdown parser in `[slug].tsx` should be preserved - Related posts logic: find other published posts sharing at least one tag - Use `createQuery` from `@trpc/client` (SolidJS adapter) for data fetching - Consider server-side rendering for initial blog list (SolidStart supports this)