--- phase: 10-core-plugins plan: 05 type: execute wave: 3 depends_on: ["10-04"] files_modified: - plugins/golem15/blog/components/Posts.scala - plugins/golem15/blog/components/Post.scala - plugins/golem15/blog/components/Categories.scala - plugins/golem15/blog/components/RelatedPosts.scala - plugins/golem15/blog/resources/views/posts/default.peb - plugins/golem15/blog/resources/views/posts/item.peb - plugins/golem15/blog/resources/views/post/default.peb - plugins/golem15/blog/resources/views/categories/default.peb - plugins/golem15/blog/resources/views/relatedposts/default.peb - plugins/golem15/blog/Plugin.scala autonomous: true must_haves: truths: - "Frontend displays post listings with pagination" - "Frontend displays individual post pages" - "Related posts shown based on shared categories/tags" - "Category tree/list component available for navigation" - "Infinite scroll works via HTMX" artifacts: - path: "plugins/golem15/blog/components/Posts.scala" provides: "Frontend post listing component" exports: ["PostsComponent", "onLoadMore"] - path: "plugins/golem15/blog/components/Post.scala" provides: "Single post display component" exports: ["PostComponent"] - path: "plugins/golem15/blog/components/Categories.scala" provides: "Category listing/tree component" exports: ["CategoriesComponent"] - path: "plugins/golem15/blog/components/RelatedPosts.scala" provides: "Related posts component" exports: ["RelatedPostsComponent"] - path: "plugins/golem15/blog/resources/views/posts/default.peb" provides: "Post listing template" contains: "hx-post" key_links: - from: "plugins/golem15/blog/components/Posts.scala" to: "BlogPostService" via: "post listing" pattern: "postService\\.listFrontend" - from: "plugins/golem15/blog/components/Post.scala" to: "BlogPostService" via: "single post lookup" pattern: "postService\\.findBySlug" - from: "plugins/golem15/blog/components/RelatedPosts.scala" to: "BlogPostService" via: "related posts query" pattern: "postService\\.getRelatedPosts" --- Create frontend components for blog display with pagination and HTMX interactions. Purpose: Enable visitors to browse and read blog content, demonstrating frontend component patterns with HTMX. Output: Complete Blog frontend with: - Posts component with pagination/infinite scroll - Post component for single post display with OpenGraph meta - Categories component for navigation - RelatedPosts component for content discovery @/home/jin/.claude/get-shit-done/workflows/execute-plan.md @/home/jin/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/10-core-plugins/10-CONTEXT.md @.planning/phases/10-core-plugins/10-RESEARCH.md # Prior plans in this phase @.planning/phases/10-core-plugins/10-03-SUMMARY.md @.planning/phases/10-core-plugins/10-04-SUMMARY.md # Reference: Phase 3 component patterns for HTMX Task 1: Posts and Post listing components plugins/golem15/blog/components/Posts.scala plugins/golem15/blog/components/Post.scala plugins/golem15/blog/resources/views/posts/default.peb plugins/golem15/blog/resources/views/posts/item.peb plugins/golem15/blog/resources/views/post/default.peb Create frontend components for blog display: **Posts.scala (research Pattern 8):** - Extend SummerComponent trait - ComponentDetails: name "Posts", description "Blog post listing" - Property schema: - pageNumber: String, URL param for pagination, default "{{ :page }}" - categoryFilter: String, filter by category slug - postsPerPage: String, default "10", validation 1-100 - noPostsMessage: String, default "No posts found." - sortOrder: Dropdown with BlogPost.allowedSortingOptions - throwNotFound: Checkbox, 404 on empty results - Page variables: posts (PaginatedResult), category (Option), noPostsMessage - onRun lifecycle: - Load category if categoryFilter set - Build PostListOptions from properties - Call postService.listFrontend or listByCategory - Handle empty results per throwNotFound - Set page context variables - onLoadMore HTMX handler: - Parse page number from form - Load next page of posts - Return items partial - Trigger "postsExhausted" when no more **Post.scala component:** - ComponentDetails: name "Post", description "Single post display" - Property schema: - slug: String, URL param for post slug, default "{{ :slug }}" - notFoundMessage: String - Page variables: post (BlogPostWithRelations) - onRun lifecycle: - Get slug from URL - Call postService.findBySlug - 404 if not found - Set page context: post, author, categories, tags, readingTime - Set OpenGraph meta tags for social sharing **Templates:** **posts/default.peb (research code example):** ```html
{% if posts.items is empty %}
{{ noPostsMessage }}
{% else %}
{% for post in posts.items %} {% include "posts/item" %} {% endfor %}
{% if posts.hasNext %}
{% endif %} {% endif %}
``` **posts/item.peb:** - Article element with post data - Featured image (lazy load) - Title as link to post page - Excerpt or auto-generated summary - Author name, published date, reading time - Category and tag links **post/default.peb:** - Full post display - OpenGraph meta tags in head - Title, featured image, author, date - Content HTML - Categories and tags - Share buttons (copy link, Twitter, Facebook) - Include RelatedPosts component placeholder
./mill summercms.compile succeeds Posts component has onLoadMore HTMX handler Post component sets OpenGraph meta tags Templates have valid Pebble syntax Posts component displays paginated listings with infinite scroll. Post component displays single post with meta tags. Templates render with HTMX interactions.
Task 2: Categories and RelatedPosts components plugins/golem15/blog/components/Categories.scala plugins/golem15/blog/components/RelatedPosts.scala plugins/golem15/blog/resources/views/categories/default.peb plugins/golem15/blog/resources/views/relatedposts/default.peb plugins/golem15/blog/Plugin.scala (modify) Create category navigation and related posts components: **Categories.scala component:** - ComponentDetails: name "Categories", description "Category listing/tree" - Property schema: - displayMode: Dropdown (tree/flat), default "tree" - showPostCount: Checkbox, default true - includeEmpty: Checkbox, show categories with no posts - Page variables: categories (tree or flat list) - onRun: Load category tree, filter empty if needed **RelatedPosts.scala component:** - ComponentDetails: name "RelatedPosts", description "Related posts by category/tag" - Property schema: - limit: String, max posts to show, default "3" - postSlug: String, current post slug (from context) - Page variables: relatedPosts (List) - onRun: Call postService.getRelatedPosts, set context **Templates:** **categories/default.peb:** - Tree or flat list based on displayMode - Category name as link - Post count if showPostCount - Recursive include for tree mode **relatedposts/default.peb:** - Grid of related post cards - Image, title, excerpt snippet - Link to full post **Register all components in Plugin.scala boot method:** - Posts component - Post component - Categories component - RelatedPosts component ./mill summercms.compile succeeds Components registered in Plugin.scala Templates have valid Pebble syntax HTMX attributes properly formatted Categories component shows category tree/list. RelatedPosts component shows related content. All components registered in Plugin.scala. Blog frontend fully functional.
After all tasks complete: 1. Blog plugin compiles: `./mill summercms.compile` 2. All four components registered in Plugin.scala 3. Posts component loads and paginates 4. Post component displays with OpenGraph meta 5. Categories component renders tree structure 6. RelatedPosts component shows related content - Frontend /blog shows post listing with load more - Frontend /blog/:slug shows full post with related posts - Category pages filter posts by category and children - Tag pages filter posts by tag - Infinite scroll works via HTMX onLoadMore handler - OpenGraph meta tags present for social sharing - Category tree/list renders correctly - Related posts show content with shared categories/tags After completion, create `.planning/phases/10-core-plugins/10-05-SUMMARY.md`