Files
Jakub Zych b05e85cd55 fix(10): revise plans based on checker feedback
- Plan 10-02: Added explicit UserMailService wiring in Account.scala
  onRegister handler for activation emails when activateMode == User
- Plan 10-04: Split into backend-only (models, services, admin controller)
  reducing from 16 to 15 files, estimated context ~50%
- Plan 10-05: New plan for frontend components (Posts, Post, Categories,
  RelatedPosts), Wave 3 depends on 10-04
- Updated ROADMAP.md to reflect 5 plans for Phase 10
2026-02-05 16:13:31 +01:00

9.8 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
phase plan type wave depends_on files_modified autonomous must_haves
10-core-plugins 05 execute 3
10-04
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
true
truths artifacts key_links
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
path provides exports
plugins/golem15/blog/components/Posts.scala Frontend post listing component
PostsComponent
onLoadMore
path provides exports
plugins/golem15/blog/components/Post.scala Single post display component
PostComponent
path provides exports
plugins/golem15/blog/components/Categories.scala Category listing/tree component
CategoriesComponent
path provides exports
plugins/golem15/blog/components/RelatedPosts.scala Related posts component
RelatedPostsComponent
path provides contains
plugins/golem15/blog/resources/views/posts/default.peb Post listing template hx-post
from to via pattern
plugins/golem15/blog/components/Posts.scala BlogPostService post listing postService.listFrontend
from to via pattern
plugins/golem15/blog/components/Post.scala BlogPostService single post lookup postService.findBySlug
from to via pattern
plugins/golem15/blog/components/RelatedPosts.scala BlogPostService related posts query 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

<execution_context> @/home/jin/.claude/get-shit-done/workflows/execute-plan.md @/home/jin/.claude/get-shit-done/templates/summary.md </execution_context>

@.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
<div class="blog-posts" id="posts-{{ __SELF__ }}">
  {% if posts.items is empty %}
    <div class="no-posts">{{ noPostsMessage }}</div>
  {% else %}
    <div class="posts-list">
      {% for post in posts.items %}
        {% include "posts/item" %}
      {% endfor %}
    </div>

    {% if posts.hasNext %}
      <div class="load-more">
        <button
          hx-post="{{ componentHandler('onLoadMore') }}"
          hx-vals='{"page": {{ posts.currentPage + 1 }}}'
          hx-target="#posts-{{ __SELF__ }} .posts-list"
          hx-swap="beforeend"
          hx-indicator=".htmx-indicator">
          Load More
          <span class="htmx-indicator">Loading...</span>
        </button>
      </div>
    {% endif %}
  {% endif %}
</div>
```

**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

<success_criteria>

  • 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 </success_criteria>
After completion, create `.planning/phases/10-core-plugins/10-05-SUMMARY.md`