Skip to content

Conversation

@gocanto
Copy link
Contributor

@gocanto gocanto commented Jul 28, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced article filtering with dynamic category filters that update based on available categories from the API.
    • Added reactive search functionality in the header with input validation and debounced search.
    • Improved post fetching with support for multiple filter options (category, text, etc.).
    • Added new API methods for fetching categories and posts with filters.
  • Improvements

    • Optimized image loading and accessibility by updating image attributes (e.g., fetch priority, lazy loading, async decoding, aria-hidden).
    • Search bar now provides instant validation feedback and a clear option.
  • Chores

    • Added lodash and its type definitions as dependencies.

@coderabbitai
Copy link

coderabbitai bot commented Jul 28, 2025

Walkthrough

This update introduces dynamic category filtering and search in the articles list, enhances image loading strategies across components, and upgrades the API client and store to support POST requests with filter parameters. TypeScript interfaces for API responses and filters are added, and the package.json is updated with lodash and its type definitions.

Changes

Cohort / File(s) Change Summary
Dependency Updates
package.json
Added lodash as a runtime dependency and @types/lodash as a dev dependency.
Image Loading Enhancements
src/pages/PostPage.vue, src/partials/AvatarPartial.vue, src/partials/ArticleItemPartial.vue
Added fetchpriority, aria-hidden, loading, and decoding attributes to <img> tags to optimize loading and accessibility.
Dynamic Category Filtering & Search
src/partials/ArticlesListPartial.vue, src/partials/HeaderPartial.vue
Replaced static category filters with dynamic, API-driven, reactive filtering. Added debounced search with validation and global store integration.
API Client & Store Enhancements
src/stores/api/client.ts, src/stores/api/store.ts
Added POST method to API client. Store now supports search term state, dynamic category fetching, and posts fetching via POST with filters.
API Response & Filter Types
src/stores/api/response/categories-response.ts, src/stores/api/response/posts-response.ts
Introduced TypeScript interfaces for category collections, individual categories, and post filters.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HeaderPartial
    participant ApiStore
    participant ArticlesListPartial

    User->>HeaderPartial: Type in search box
    HeaderPartial->>HeaderPartial: Debounce input, validate
    HeaderPartial->>ApiStore: setSearchTerm(query)
    ApiStore-->>ArticlesListPartial: searchTerm updated (reactive)
    ArticlesListPartial->>ApiStore: getPosts({ category, text: searchTerm })
    ApiStore->>ApiClient: post('/posts?limit=5', filters)
    ApiClient-->>ApiStore: PostsCollectionResponse
    ApiStore-->>ArticlesListPartial: PostsCollectionResponse
    ArticlesListPartial-->>User: Render filtered posts
Loading
sequenceDiagram
    participant ArticlesListPartial
    participant ApiStore
    participant ApiClient

    ArticlesListPartial->>ApiStore: getCategories()
    ApiStore->>ApiClient: get('/categories?limit=5')
    ApiClient-->>ApiStore: CategoriesCollectionResponse
    ApiStore-->>ArticlesListPartial: CategoriesCollectionResponse
    ArticlesListPartial-->>ArticlesListPartial: Render categories as filter links
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • [feature] ApiClient + Posts Query #6: Introduces the ApiClient class with GET request capabilities and initial posts query methods, closely related to this PR's enhancements for POST requests and filtered querying in the API client and store.

Poem

Hopping through code with a bounding leap,
Dynamic filters now run deep.
Images load both fast and light,
Search debounced to work just right.
New types and posts that filter true—
A garden of features, fresh as dew!
🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hotfix/posts-theme

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @gocanto, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request transitions the article category display from static, hardcoded values to dynamic content fetched from an API. It introduces new API client methods and data models to support category retrieval, enhancing the flexibility and maintainability of the article listing page.

Highlights

  • Dynamic Category Display: The ArticlesListPartial.vue component has been updated to dynamically render article categories by iterating over a categories data property using v-for, replacing previously hardcoded list items.
  • API Integration for Categories: A new getCategories() method has been added to the apiStore to fetch category data from the backend. This method is called within the onMounted lifecycle hook of ArticlesListPartial.vue to populate the categories displayed.
  • New Data Models: New TypeScript interfaces, CategoriesCollectionResponse and CategoryResponse, have been introduced in src/stores/api/response/categories-response.ts to define the structure of the category data received from the API.
  • API Query Limits: Both the getCategories() and getPosts() API calls in src/stores/api/store.ts now include a limit=5 query parameter, restricting the number of items returned.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces functionality to fetch and display article categories from an API, replacing a previously hardcoded list. The implementation is straightforward, but I've identified a few areas for improvement regarding UI correctness, performance, and code maintainability. My review includes suggestions to fix a UI bug with category selection, optimize data fetching with parallel requests, and improve code clarity by removing magic numbers.

@gocanto gocanto changed the title hook categories [feature] Filter posts Jul 28, 2025
@gocanto gocanto changed the title [feature] Filter posts [feature] Filter posts & categories Jul 28, 2025
@gocanto gocanto marked this pull request as ready for review July 29, 2025 04:29
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: 2

♻️ Duplicate comments (2)
src/stores/api/store.ts (2)

40-48: Address the magic number in getCategories.

The hardcoded limit value "5" should be extracted to a named constant as flagged in previous reviews. This same value is used in getPosts on line 50.


49-57: Good API design change but address magic number.

The change from GET to POST request for getPosts is appropriate when adding filtering capabilities. However, the hardcoded limit "5" should be extracted to a named constant as flagged in previous reviews.

🧹 Nitpick comments (2)
package.json (1)

16-16: Consider lightweight alternatives to lodash for debouncing.

While lodash is a solid choice, consider if you need the entire library just for debouncing functionality. Lightweight alternatives like lodash.debounce (individual package) or native implementations might reduce bundle size.

For debouncing only, you could use:

  • lodash.debounce package (smaller footprint)
  • A simple native implementation
  • Modern utilities like @vueuse/core which provides useDebouncedRef

Current choice is acceptable if you plan to use other lodash utilities.

src/partials/HeaderPartial.vue (1)

66-96: Consider the 5-character minimum requirement.

The search implementation is well-structured with proper debouncing and validation. However, the 5-character minimum requirement might be too restrictive for users. Consider whether a shorter minimum (e.g., 3 characters) would provide better UX while still avoiding excessive API calls.

The debounce cancellation in clearSearchAndError and proper state management are excellent practices.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c193f2 and dc1cb69.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • package.json (2 hunks)
  • src/pages/PostPage.vue (1 hunks)
  • src/partials/ArticleItemPartial.vue (1 hunks)
  • src/partials/ArticlesListPartial.vue (2 hunks)
  • src/partials/AvatarPartial.vue (1 hunks)
  • src/partials/HeaderPartial.vue (2 hunks)
  • src/stores/api/client.ts (1 hunks)
  • src/stores/api/response/categories-response.ts (1 hunks)
  • src/stores/api/response/posts-response.ts (1 hunks)
  • src/stores/api/store.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/stores/api/store.ts (4)
src/stores/api/client.ts (2)
  • ApiClient (29-134)
  • defaultCreds (5-10)
src/stores/api/response/categories-response.ts (1)
  • CategoriesCollectionResponse (1-7)
src/stores/api/http-error.ts (1)
  • parseError (17-27)
src/stores/api/response/posts-response.ts (2)
  • PostsFilters (48-54)
  • PostsCollectionResponse (1-7)
src/stores/api/client.ts (1)
src/stores/api/http-error.ts (1)
  • HttpError (1-15)
🔇 Additional comments (17)
package.json (1)

27-27: LGTM - Type definitions properly added.

The TypeScript definitions for lodash are correctly added to devDependencies with a compatible version.

src/partials/ArticleItemPartial.vue (1)

4-13: Excellent image optimization for article list items.

The image attributes are well-chosen for list item performance:

  • loading="lazy" appropriately defers loading until needed
  • decoding="async" prevents blocking the main thread
  • fetchpriority="low" correctly prioritizes these secondary images below critical content

This optimization strategy will improve initial page load performance while maintaining good UX as users scroll.

src/stores/api/client.ts (1)

83-98: Well-implemented POST method following existing patterns.

The post<T> method implementation is solid:

  • Consistent error handling with the existing get method using HttpError
  • Proper JSON serialization of request body
  • Appropriate omission of caching logic (unlike GET requests)
  • Correct use of existing createHeaders() method
  • Clean async/await pattern with proper error propagation

The method signature and implementation align well with the existing codebase patterns.

src/stores/api/response/posts-response.ts (1)

48-54: Well-designed filtering interface.

The PostsFilters interface is well-structured with optional string properties that provide flexible filtering capabilities. The interface follows TypeScript best practices and integrates cleanly with the rest of the codebase.

src/stores/api/response/categories-response.ts (1)

1-14: Consistent and well-structured category interfaces.

Both interfaces follow established patterns in the codebase. CategoriesCollectionResponse maintains consistency with the existing pagination structure used in PostsCollectionResponse, and CategoryResponse includes all necessary fields for category data management.

src/stores/api/store.ts (2)

12-12: Well-implemented search term management.

The addition of searchTerm to the store state and the setSearchTerm action provides a clean way to manage global search state across components.

Also applies to: 20-20


23-25: Clean implementation of search term setter.

The setSearchTerm action is straightforward and properly updates the store state.

src/partials/HeaderPartial.vue (4)

6-6: Good form handling with prevent default.

Proper use of @submit.prevent to handle form submission without page reload.


11-11: Effective validation feedback implementation.

The dynamic class binding provides clear visual feedback for validation errors, and the v-model binding is properly implemented.


13-22: Clean conditional icon rendering.

The conditional rendering between search and clear icons based on validation state provides good UX, and the clear functionality is properly implemented.


58-61: Proper imports and store integration.

Clean imports including lodash debounce and proper integration with the API store.

src/partials/ArticlesListPartial.vue (6)

7-19: Excellent dynamic category filtering implementation.

The dynamic category rendering with conditional styling based on filters.category properly addresses the previous review comment about hardcoded active states. The implementation correctly applies active/inactive styles based on the selected category.


30-36: Clean imports and proper typing.

Good use of lodash debounce and proper TypeScript interface imports for type safety.


41-51: Well-structured reactive state management.

The reactive filters object and category state variables are properly typed and organized. The selectCategory function provides a clean interface for category selection.


53-61: Clean posts fetching implementation.

The fetchPosts function is well-structured with proper error handling and type casting.


64-78: Good reactive filtering with debouncing.

The watchers for category and search term changes are well-implemented. The debounced category filter prevents excessive API calls, while the immediate search term filter provides quick feedback for user searches.


81-94: Consider parallel API calls for better performance.

The sequential API calls for categories and posts could be optimized by running them in parallel, as suggested in previous reviews. However, since posts depend on having a category selected first, the current sequential approach is actually correct for the business logic.

The automatic selection of the first category and subsequent post fetching is a good UX decision.

@gocanto
Copy link
Contributor Author

gocanto commented Jul 29, 2025

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jul 29, 2025

✅ Actions performed

Full review triggered.

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

♻️ Duplicate comments (5)
src/partials/AvatarPartial.vue (1)

2-2: Same accessibility issue as previously identified.

The aria-hidden="true" attribute is still problematic on meaningful avatar images that have descriptive alt text.

src/pages/PostPage.vue (1)

89-89: Same accessibility issue as previously identified.

The aria-hidden="true" attribute remains problematic on the meaningful post cover image that has semantic alt text.

src/stores/api/store.ts (2)

40-48: LGTM! Consistent API method pattern.

The getCategories method follows the same error handling pattern as other API methods in the store.


49-57: Verify the design decision to use POST for fetching posts.

Changing from GET to POST for fetching posts is an unusual architectural decision, as GET requests are typically used for data retrieval and POST for mutations. This could impact caching, SEO, and API conventions.

#!/bin/bash
# Check if there are any comments or documentation explaining why POST is used for getPosts
rg -A 5 -B 5 "getPosts.*POST|POST.*getPosts" --type md --type txt
rg -A 10 -B 5 "why.*POST|POST.*why" --type md --type txt --type js --type ts
src/partials/ArticlesListPartial.vue (1)

81-94: Consider parallel API calls for better performance.

The current sequential API calls could be optimized by running them in parallel since they don't depend on each other.

 onMounted(async () => {
 	try {
-		categoriesCollection.value = await apiStore.getCategories();
-		categories.value = categoriesCollection.value.data as CategoryResponse[];
+		const [categoriesResult] = await Promise.all([
+			apiStore.getCategories()
+		]);
+		
+		categoriesCollection.value = categoriesResult;
+		categories.value = categoriesResult.data as CategoryResponse[];
 
 		if (categories.value.length > 0) {
 			filters.category = categories.value[0].slug;
 
 			await fetchPosts();
 		}
 	} catch (error) {
 		debugError(error);
 	}
 });
🧹 Nitpick comments (1)
src/partials/HeaderPartial.vue (1)

69-96: LGTM! Well-implemented search functionality with proper debouncing.

The search implementation includes:

  • Proper debouncing to prevent excessive API calls
  • Reasonable validation (5-character minimum)
  • Clean error state management
  • Proper cleanup when clearing search

Consider whether the 5-character minimum might be too restrictive for some use cases, but the current implementation provides good balance between UX and API efficiency.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c193f2 and dc1cb69.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • package.json (2 hunks)
  • src/pages/PostPage.vue (1 hunks)
  • src/partials/ArticleItemPartial.vue (1 hunks)
  • src/partials/ArticlesListPartial.vue (2 hunks)
  • src/partials/AvatarPartial.vue (1 hunks)
  • src/partials/HeaderPartial.vue (2 hunks)
  • src/stores/api/client.ts (1 hunks)
  • src/stores/api/response/categories-response.ts (1 hunks)
  • src/stores/api/response/posts-response.ts (1 hunks)
  • src/stores/api/store.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/stores/api/client.ts (1)
src/stores/api/http-error.ts (1)
  • HttpError (1-15)
src/stores/api/store.ts (4)
src/stores/api/client.ts (2)
  • ApiClient (29-134)
  • defaultCreds (5-10)
src/stores/api/response/categories-response.ts (1)
  • CategoriesCollectionResponse (1-7)
src/stores/api/http-error.ts (1)
  • parseError (17-27)
src/stores/api/response/posts-response.ts (2)
  • PostsFilters (48-54)
  • PostsCollectionResponse (1-7)
🔇 Additional comments (11)
src/partials/ArticleItemPartial.vue (1)

4-13: Excellent image loading optimization.

The combination of loading="lazy", decoding="async", and fetchpriority="low" is well-suited for article list thumbnails. This improves performance by deferring non-critical image loading while maintaining accessibility with proper alt text.

src/stores/api/response/posts-response.ts (1)

48-54: Well-designed filtering interface.

The PostsFilters interface provides a clean, flexible contract for post filtering with optional properties that allow various filtering combinations.

src/stores/api/client.ts (1)

83-98: Well-implemented POST method following established patterns.

The new post<T> method maintains consistency with the existing get<T> method structure, uses the same error handling approach with HttpError, and appropriately excludes caching logic for POST requests.

src/stores/api/response/categories-response.ts (1)

1-14: LGTM! Well-structured TypeScript interfaces.

The interfaces follow a consistent pattern with the existing API response structure and provide proper typing for category data. The pagination metadata and category properties are appropriately defined.

src/stores/api/store.ts (1)

12-12: LGTM! Clean search state management.

The addition of searchTerm state and setSearchTerm action provides a clean way to manage global search state across components.

Also applies to: 20-20, 23-25

src/partials/HeaderPartial.vue (2)

6-6: LGTM! Proper form submission handling.

The form correctly prevents default submission and delegates to the performSearch method.


11-22: LGTM! Excellent UX with conditional validation feedback.

The search input provides clear visual feedback with conditional border styling and icon changes based on validation state. The toggle between search and clear icons enhances usability.

src/partials/ArticlesListPartial.vue (4)

7-19: LGTM! Excellent improvement from hardcoded to dynamic categories.

The dynamic category rendering with proper conditional class binding resolves the previous issue with hardcoded active styles. Each category now correctly shows active/inactive state based on the selected filter.


44-51: LGTM! Clean reactive filter management.

The selectCategory function and reactive filters object provide a clean way to manage filtering state.


53-61: LGTM! Well-structured posts fetching with proper error handling.

The extracted fetchPosts function provides a clean, reusable way to fetch filtered posts with appropriate error handling.


64-78: LGTM! Appropriate debouncing strategy for different filter types.

The implementation correctly uses debouncing for category changes (which are user-initiated clicks) but immediate execution for search term changes (which come from a pre-debounced source). This provides optimal UX.

@gocanto gocanto merged commit 83bbced into main Jul 29, 2025
1 check passed
@gocanto gocanto deleted the hotfix/posts-theme branch July 29, 2025 08:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants