Skip to content

Conversation

@pharmacist-sabot
Copy link
Owner

@pharmacist-sabot pharmacist-sabot commented Dec 31, 2025

Summary by Sourcery

Add related blog post recommendations to the blog post layout using tag and category similarity.

New Features:

  • Display a "You might also like" related posts section on blog post pages.
  • Introduce a utility to compute related blog posts based on shared tags, category, and recency.

Summary by CodeRabbit

  • New Features

    • Blog posts now include a "You might also like" Related Posts section beneath content.
    • Shows up to 3 recommended articles determined by shared tags/category and recency.
    • Displays recommendations in a responsive grid (1 column on small, 2 on medium, 3 on large) with card-style presentation.
  • Chores / Performance

    • Reduces repeated data fetching during builds to improve rendering efficiency.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 31, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
rxdevnotes Ready Ready Preview, Comment Dec 31, 2025 11:29pm

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 31, 2025

Reviewer's Guide

Implements a related posts feature for blog articles by computing related posts based on tags and category, exposing the logic via a new utility, and rendering them in a new RelatedPosts component within the existing BlogPostLayout.

Sequence diagram for computing and rendering related blog posts

sequenceDiagram
  actor User
  participant BlogPostPage as BlogPostLayout
  participant ContentAPI as AstroContent
  participant Utils as post_utils
  participant Related as RelatedPosts
  participant Card as BlogPostCard

  User->>BlogPostPage: Request blog post
  BlogPostPage->>ContentAPI: getCollection(blog)
  ContentAPI-->>BlogPostPage: allPosts
  BlogPostPage->>Utils: getRelatedPosts(allPosts, postSlug, tags, category, 3)
  Utils-->>BlogPostPage: relatedPosts
  BlogPostPage->>Related: Render RelatedPosts with relatedPosts
  alt has relatedPosts
    loop for each relatedPost
      Related->>Card: Render BlogPostCard(post)
      Card-->>Related: Blog post card HTML
    end
  else no relatedPosts
    Related-->>BlogPostPage: No related posts section rendered
  end
  BlogPostPage-->>User: Full blog post page HTML including related posts
Loading

Class diagram for related posts utility and components

classDiagram
  class BlogPostLayout {
    +string title
    +string description
    +Date pubDate
    +string slug
    +string[] tags
    +string category
    +string postSlug
    +CollectionEntry_blog_[] allPosts
    +CollectionEntry_blog_[] relatedPosts
    +render()
  }

  class RelatedPosts {
    +CollectionEntry_blog_[] relatedPosts
    +render()
  }

  class BlogPostCard {
    +CollectionEntry_blog_ post
    +render()
  }

  class post_utils {
    +getRelatedPosts(allPosts CollectionEntry_blog_[], currentSlug string, currentTags string[], currentCategory string, limit number) CollectionEntry_blog_[]
  }

  class CollectionEntry_blog_ {
    +string slug
    +data Data_blog_
  }

  class Data_blog_ {
    +string[] tags
    +string category
    +Date pubDate
  }

  BlogPostLayout --> post_utils : uses
  BlogPostLayout --> RelatedPosts : renders
  RelatedPosts --> BlogPostCard : renders
  BlogPostLayout --> CollectionEntry_blog_ : gets_from_getCollection
  CollectionEntry_blog_ --> Data_blog_ : has_data
  post_utils --> CollectionEntry_blog_ : processes
Loading

File-Level Changes

Change Details Files
Compute related posts for each blog post and render them in the blog post layout.
  • Import getCollection and getRelatedPosts into the blog post layout.
  • Derive postSlug from slug or title and use it to exclude the current post when computing related posts.
  • Fetch all blog posts at layout level and compute a limited list of related posts using tags and category.
  • Render a RelatedPosts section below the blog post footer, passing the computed related posts.
src/layouts/BlogPostLayout.astro
Introduce a reusable RelatedPosts component to display a grid of related blog posts.
  • Define a RelatedPosts Astro component that accepts a list of blog CollectionEntry items as props.
  • Render a heading and a responsive grid of BlogPostCard components when related posts are present.
  • Add responsive styling for desktop, tablet, and mobile layouts including spacing, borders, and typography.
src/components/blog/RelatedPosts.astro
Add a post utility to score and select related posts with a fallback to recent posts.
  • Implement getRelatedPosts utility that filters out the current post based on slug.
  • Score candidate posts using matching tags (+2 each) and matching category (+1).
  • Sort posts by score and publication date (newest first) and return the top N posts.
  • Provide a fallback that returns the most recent posts when no posts achieve a positive score.
src/utils/post-utils.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link

coderabbitai bot commented Dec 31, 2025

📝 Walkthrough

Walkthrough

Adds a cached blog posts accessor, a scoring-based related-posts utility, a new RelatedPosts Astro component, and integrates related-post rendering into BlogPostLayout to display up to three related posts beneath a post.

Changes

Cohort / File(s) Summary
Related posts UI & integration
src/components/blog/RelatedPosts.astro, src/layouts/BlogPostLayout.astro
New RelatedPosts component renders a responsive "You might also like" grid of BlogPostCard items. BlogPostLayout now imports RelatedPosts, fetches posts, computes relatedPosts, and passes them to the component.
Post matching logic
src/utils/post-utils.ts
Added getRelatedPosts(allPosts, currentSlug, currentTags, currentCategory, limit) that filters out the current post and posts without valid pubDate, scores posts (+2 per shared tag, +1 per shared category), sorts by score then pubDate, and falls back to most recent posts when no positive scores.
Cached blog accessor
src/lib/blog.ts
New getAllBlogPosts(): Promise<CollectionEntry<'blog'>[]> which caches getCollection('blog') at module scope to avoid repeated fetches during the build.

Sequence Diagram

sequenceDiagram
    participant Layout as BlogPostLayout
    participant Cache as getAllBlogPosts
    participant Util as getRelatedPosts
    participant Component as RelatedPosts
    participant UI as Browser

    Layout->>Cache: fetch allPosts[]
    Cache-->>Layout: return cached allPosts[]
    Note over Layout: determine currentSlug, tags, category
    Layout->>Util: getRelatedPosts(allPosts, currentSlug, tags, category, 3)
    Util->>Util: filter & score posts\n(sort by score, then pubDate)
    Util-->>Layout: relatedPosts[]
    Layout->>Component: render with relatedPosts
    Component->>Component: build responsive grid\n(render BlogPostCard per item)
    Component-->>UI: display "You might also like" section
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped through tags and dates today,

Found friends of posts along the way.
Three cozy hops, a card or two,
A nibble of content, fresh and new.
Hop in — related reads await!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feat: add feature related post' is vague and contains redundant terminology. It says 'add feature related post' which is unclear about what the actual feature is—it should specify that it adds a related posts recommendation section or similar. Consider revising the title to be more specific, such as 'feat: add related posts recommendation section' or 'feat: display related blog posts by tag and category similarity'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The getRelatedPosts helper assumes currentTags and currentCategory are always defined and that all posts have data.pubDate; consider adding null/undefined guards or default values to avoid runtime errors when frontmatter is missing or incomplete.
  • Fetching allPosts via getCollection('blog') inside BlogPostLayout means this runs on every post render; consider moving this to a higher level or caching the collection so related posts are computed without repeatedly querying all content.
  • In RelatedPosts.astro, the relatedPosts.map loop renders BlogPostCard without a stable key; consider passing a unique key (e.g., post.id or post.slug) to help Astro/underlying framework optimize DOM updates.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `getRelatedPosts` helper assumes `currentTags` and `currentCategory` are always defined and that all posts have `data.pubDate`; consider adding null/undefined guards or default values to avoid runtime errors when frontmatter is missing or incomplete.
- Fetching `allPosts` via `getCollection('blog')` inside `BlogPostLayout` means this runs on every post render; consider moving this to a higher level or caching the collection so related posts are computed without repeatedly querying all content.
- In `RelatedPosts.astro`, the `relatedPosts.map` loop renders `BlogPostCard` without a stable key; consider passing a unique key (e.g., `post.id` or `post.slug`) to help Astro/underlying framework optimize DOM updates.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/utils/post-utils.ts (1)

50-64: Consider edge case: handling when allPosts contains only the current post.

The logic correctly returns related posts or falls back to recent posts. However, there's a subtle edge case: if allPosts contains only the current post, both branches will return an empty array (which is correct), but it might be worth noting this behavior in a comment for future maintainers.

📝 Optional: Add clarifying comment
  // Check if we have any posts with score > 0
  const postsWithScore = scoredPosts.filter(item => item.score > 0);

  if (postsWithScore.length > 0) {
    // Return top 'limit' related posts
    return postsWithScore.slice(0, limit).map(item => item.post);
  }

-  // Fallback: return most recent posts if no related posts found
+  // Fallback: return most recent posts if no related posts found
+  // Returns empty array if only the current post exists
  const recentPosts = [...otherPosts].sort(
    (a, b) =>
      new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime(),
  );

  return recentPosts.slice(0, limit);
src/components/blog/RelatedPosts.astro (1)

13-24: Consider adding a unique key for better performance.

The conditional rendering and mapping logic is correct. However, Astro (like React) benefits from explicit keys in mapped lists to optimize re-rendering, especially for dynamic content.

🔎 Add key attribute to mapped items
      <div class="related-posts-grid">
-        {relatedPosts.map(post => (
-          <BlogPostCard post={post} />
+        {relatedPosts.map(post => (
+          <BlogPostCard key={post.slug} post={post} />
        ))}
      </div>
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bac15c5 and 61c21cc.

📒 Files selected for processing (3)
  • src/components/blog/RelatedPosts.astro
  • src/layouts/BlogPostLayout.astro
  • src/utils/post-utils.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (9)
src/utils/post-utils.ts (4)

8-14: LGTM! Well-documented function signature.

The function signature is clear with appropriate TypeScript types and a sensible default for the limit parameter. The JSDoc comment effectively explains the scoring system and fallback behavior.


15-16: LGTM! Correct filtering of the current post.

Properly excludes the current post from the results to avoid self-referencing.


38-48: LGTM! Solid sorting logic with proper tie-breaking.

The sort implementation correctly prioritizes score first, then uses publication date as a tie-breaker, ensuring newer posts are preferred when scores are equal.


18-36: The scoring logic is correctly implemented. However, the case-sensitivity concern is not supported by the actual data in this repository—all tags and categories are consistently title-cased (e.g., 'JavaScript', 'DevOps', 'Git'). No case variations like "javascript" or "devops" exist in the blog posts, making the current direct string comparison appropriate. Making comparisons case-insensitive would add unnecessary overhead without addressing a real issue.

Likely an incorrect or invalid review comment.

src/components/blog/RelatedPosts.astro (2)

1-11: LGTM! Properly typed Astro component.

The component is correctly set up with TypeScript types using Astro's CollectionEntry type, and the props destructuring follows Astro v5 conventions.


26-68: LGTM! Well-structured responsive design.

The CSS provides a clean, responsive layout with appropriate breakpoints and uses CSS custom properties for theming. The grid gracefully adapts from 3 columns → 2 columns → 1 column as viewport narrows.

src/layouts/BlogPostLayout.astro (3)

5-10: LGTM! Appropriate imports for the related posts feature.

The imports are correctly structured and use Astro v5's stable content collections API (not the deprecated legacy system).


84-84: LGTM! Clean integration of the RelatedPosts component.

The component is placed logically after the post footer within the article, providing a natural flow for readers to discover related content.


30-35: No changes needed. This is a static site (confirmed via astro.config.mjs with Vercel adapter and default static output mode). For static generation, getCollection('blog') executing at build time is standard Astro behavior and incurs no runtime penalty. The review comment's concerns about SSR and server-side performance do not apply to this codebase.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/lib/blog.ts (1)

1-22: Consider the implications of module-level caching in development mode.

The module-level cache works well for production builds where Astro pre-renders pages once. However, during development with Hot Module Replacement (HMR), the cache persists across file changes and won't reflect newly added or modified blog posts until the dev server is restarted.

Recommended approaches

Option 1: Add cache invalidation in development

+import.meta.env?.DEV && (cachedPosts = null);
+
 export async function getAllBlogPosts(): Promise<CollectionEntry<'blog'>[]> {
-  if (cachedPosts === null) {
+  if (cachedPosts === null || import.meta.env?.DEV) {
     cachedPosts = await getCollection('blog');
   }
   return cachedPosts;
 }

Option 2: Document the limitation

 /**
  * Get all blog posts with caching.
  * Results are cached at the module level to avoid repeated collection fetches.
+ * Note: In development mode, restart the dev server to refresh cached posts.
  */
src/layouts/BlogPostLayout.astro (2)

29-30: Clarify the "database key" comment.

The term "database key" may be misleading since the slug is used for the ViewCounter component and related posts computation, not necessarily direct database access. Consider updating the comment to better reflect its actual usage.

Suggested wording
-// Use the provided slug or generate from title as a database key
+// Use the provided slug or generate from title for view tracking and related posts
 const postSlug = slug || slugify(title);

32-34: Consider adding error handling for blog posts fetching.

If getAllBlogPosts() throws an error, the entire page build will fail. Consider adding error handling to gracefully degrade the related posts feature.

Proposed error handling
-// Fetch all blog posts (cached) and compute related posts
-const allPosts = await getAllBlogPosts();
-const relatedPosts = getRelatedPosts(allPosts, postSlug, tags, category, 3);
+// Fetch all blog posts (cached) and compute related posts
+let relatedPosts: CollectionEntry<'blog'>[] = [];
+try {
+  const allPosts = await getAllBlogPosts();
+  relatedPosts = getRelatedPosts(allPosts, postSlug, tags, category, 3);
+} catch (error) {
+  console.error('Failed to fetch related posts:', error);
+}
src/components/blog/RelatedPosts.astro (1)

43-47: Consider grid behavior with fewer than 3 related posts.

The 3-column grid layout works well when you have exactly 3 related posts, but with only 1 or 2 posts, the items may look isolated or asymmetric on large screens. Consider whether this is the desired visual effect.

Optional: Center items when fewer than 3 posts
 .related-posts-grid {
   display: grid;
   grid-template-columns: repeat(3, 1fr);
   gap: 1.5rem;
+  justify-items: center;
 }
 
+.related-posts-grid:has(.grid-item:last-child:nth-child(-n+2)) {
+  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+  max-width: 600px;
+  margin: 0 auto;
+}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61c21cc and 28611b5.

📒 Files selected for processing (4)
  • src/components/blog/RelatedPosts.astro
  • src/layouts/BlogPostLayout.astro
  • src/lib/blog.ts
  • src/utils/post-utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/post-utils.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (3)
src/layouts/BlogPostLayout.astro (1)

5-9: Related posts integration looks good.

The imports are clean and the integration of the RelatedPosts component is straightforward and well-placed after the post footer.

Also applies to: 83-83

src/components/blog/RelatedPosts.astro (2)

1-11: Well-structured component with clean conditional rendering.

The component structure is solid with proper typing, conditional rendering to avoid empty sections, and responsive CSS. The use of BlogPostCard ensures consistency with the rest of the blog UI.

Also applies to: 13-26, 28-74


19-19: No changes required. The code correctly uses post.slug. In Astro v5's astro:content API, slug is a built-in property derived from the file structure (not defined in the schema), and it is the correct property to use for CollectionEntry<'blog'>. The id property does not exist on this type. The codebase consistently uses post.slug throughout (in routing, links, and utilities), confirming this is the correct approach.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/utils/post-utils.ts (2)

34-40: Consider using a Set for tag matching performance.

The current implementation uses includes() inside a nested loop, resulting in O(n × m × k) complexity. Converting currentTags to a Set would improve tag lookups to O(1), reducing complexity to O(n × m).

🔎 Proposed optimization
  // Calculate scores for each post
+ const currentTagsSet = new Set(currentTags);
  const scoredPosts = otherPosts.map((post) => {
    let score = 0;

    // +2 points for each matching tag
    const postTags = post.data.tags ?? [];
    for (const tag of postTags) {
-     if (currentTags.includes(tag)) {
+     if (currentTagsSet.has(tag)) {
        score += 2;
      }
    }

68-68: Optional: Remove redundant array spread.

The spread operator [...otherPosts] creates an unnecessary copy since otherPosts is already a new array from the filter() operation and isn't reused afterward.

🔎 Proposed simplification
  // Fallback: return most recent posts if no related posts found
- const recentPosts = [...otherPosts].sort(
+ const recentPosts = otherPosts.sort(
    (a, b) => safeGetTime(b.data.pubDate) - safeGetTime(a.data.pubDate),
  );
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28611b5 and 2b8add6.

📒 Files selected for processing (1)
  • src/utils/post-utils.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (2)
src/utils/post-utils.ts (2)

6-11: LGTM! Safe date parsing implementation.

The safeGetTime helper correctly handles undefined/null dates and invalid date parsing, returning 0 as a safe fallback for sorting operations.


18-28: LGTM! Clean function signature and filtering logic.

The default parameters are sensible, and the filtering correctly excludes the current post and posts without valid publication dates.

Comment on lines +26 to +28
const otherPosts = allPosts.filter(
post => post.slug !== currentSlug && post.data.pubDate,
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for getAllBlogPosts implementation
echo "=== Searching for getAllBlogPosts ==="
rg -n --type=ts "getAllBlogPosts" --max-count=10

echo ""
echo "=== Checking post-utils.ts context ==="
# Read the file to see the actual implementation context
cat -n src/utils/post-utils.ts | head -50

echo ""
echo "=== Searching for draft filtering logic ==="
rg -n --type=ts -C3 "draft|published" src/

Repository: pharmacist-sabot/rxdevnotes

Length of output: 2075


🏁 Script executed:

#!/bin/bash
# Read the getAllBlogPosts implementation
echo "=== getAllBlogPosts implementation ==="
cat -n src/lib/blog.ts

echo ""
echo "=== Search for content schema definition ==="
fd -e ts -e tsx "schema\|config" src/ | head -10

echo ""
echo "=== Look for blog collection definition ==="
rg -n "type.*blog\|interface.*blog" --type=ts -i src/

Repository: pharmacist-sabot/rxdevnotes

Length of output: 1069


🏁 Script executed:

#!/bin/bash
# Find Astro content config file
echo "=== Looking for Astro content configuration ==="
fd -name "config.ts" src/content/

echo ""
echo "=== Check directory structure of src/content ==="
find src/content -type f | head -20

echo ""
echo "=== Search for any draft-related filtering in getCollection calls ==="
rg -n "getCollection" --type=ts -A5 -B5 src/

Repository: pharmacist-sabot/rxdevnotes

Length of output: 2219


🏁 Script executed:

#!/bin/bash
# Read the Astro content config
echo "=== Astro content config ==="
cat -n src/content/config.ts

echo ""
echo "=== Check for draft field in blog frontmatter ==="
# Sample one blog post to see the frontmatter
head -50 src/content/blog/automate-release-with-semantic-release/index.mdx

Repository: pharmacist-sabot/rxdevnotes

Length of output: 3191


Consider adding draft post support if needed. The codebase currently lacks a draft field in the blog collection schema and has no draft filtering logic. If draft posts are needed, add a draft: z.boolean().optional() field to the schema in src/content/config.ts and implement filtering in getAllBlogPosts() to exclude them (e.g., await getCollection('blog').then(posts => posts.filter(p => !p.data.draft))). Currently, the getRelatedPosts() function only filters by pubDate and slug, which is insufficient for draft post handling.

🤖 Prompt for AI Agents
In src/utils/post-utils.ts around lines 26 to 28, getRelatedPosts currently
filters only by slug and pubDate and will include draft posts if drafts exist;
add draft handling by adding a draft: z.boolean().optional() to the blog
collection schema in src/content/config.ts and update the blog post retrieval to
exclude drafts (e.g., when calling getCollection('blog') filter out posts with
data.draft === true) so that otherPosts excludes drafts as well as unpublished
posts.

@pharmacist-sabot pharmacist-sabot merged commit ab0c07c into main Dec 31, 2025
10 checks passed
@pharmacist-sabot pharmacist-sabot deleted the feature/related-post branch December 31, 2025 23:43
github-actions bot pushed a commit that referenced this pull request Dec 31, 2025
## [1.3.0](v1.2.2...v1.3.0) (2025-12-31)

### 🚀 New Features

* add feature related post ([#7](#7)) ([ab0c07c](ab0c07c))
@github-actions
Copy link

🎉 This PR is included in version 1.3.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant