Skip to content

Sanity#79

Merged
ViktorSvertoka merged 4 commits into
developfrom
sanity
Dec 23, 2025
Merged

Sanity#79
ViktorSvertoka merged 4 commits into
developfrom
sanity

Conversation

@ViktorSvertoka
Copy link
Copy Markdown
Member

@ViktorSvertoka ViktorSvertoka commented Dec 23, 2025

Summary by CodeRabbit

Release Notes

New Features

  • Added live search suggestions with Tab auto-complete for tag filtering

Style

  • Redesigned blog card layout with responsive image sizing and enhanced visual effects
  • Refined tag and category styling with consistent neutral color palette
  • Improved filter controls and button design for better user interaction

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

@netlify
Copy link
Copy Markdown

netlify Bot commented Dec 23, 2025

Deploy Preview for develop-devlovers ready!

Name Link
🔨 Latest commit c4445fc
🔍 Latest deploy log https://app.netlify.com/projects/develop-devlovers/deploys/694a868233df240008a07ec5
😎 Deploy Preview https://deploy-preview-79--develop-devlovers.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 23, 2025

Walkthrough

The changes refactor blog components by introducing portable text types for rich-text content, replacing absolute with relative imports, moving inline helper logic into components, enhancing the search UI with autocomplete suggestions and keyboard interactions, and applying refined styling across multiple elements.

Changes

Cohort / File(s) Summary
Type System & Imports
frontend/components/blog/BlogFilters.tsx
Introduced PortableText type system with PortableTextSpan, PortableTextBlock, PortableTextImage, and PortableText types; updated Author.bio and Post.body to use PortableText instead of generic any/any[].
BlogCard Refactoring
frontend/components/blog/BlogCard.tsx
Converted absolute imports to relative paths; removed extractExcerpt helper and compute excerpt inline by filtering post.body blocks; updated root element, image container, title, category badge, tag, excerpt, and resource link styling with refined Tailwind classes; replaced tag normalization approach.
BlogFilters Search & UI Enhancement
frontend/components/blog/BlogFilters.tsx
Added SearchIcon component; enhanced search input with styled container and live autocomplete suggestions overlay; implemented tag aggregation (allTags) from posts with normalization and sorting; introduced input normalization and suggestion mechanism with Tab auto-complete support; replaced useTranslations with inline literals; updated tag UI styling and keyboard interactions (Enter to add, Tab to complete).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Sanity' is vague and does not clearly describe the main changes in the pull request, which involve refactoring blog components with new type definitions and enhanced filtering logic. Replace with a descriptive title that summarizes the primary changes, such as 'Add PortableText type definitions and refactor blog components' or 'Implement Sanity-based rich text types for blog content'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sanity

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

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (2)
frontend/components/blog/BlogCard.tsx (2)

17-22: Consider extracting excerpt generation to a reusable helper.

The inline excerpt extraction logic is correct but spans six lines with nested operations. For better maintainability and testability, consider extracting this to a separate function (e.g., generateExcerpt(body: PortableText | undefined): string).

🔎 Proposed refactor

Create a helper function in a shared utilities file or at the top of this file:

+function generateExcerpt(body?: PortableText, maxLength = 160): string {
+  return (
+    body
+      ?.filter((b): b is PortableTextBlock => b?._type === 'block')
+      .map(b => (b.children || []).map(c => c.text || '').join(' '))
+      .join(' ')
+      .slice(0, maxLength) || ''
+  );
+}
+
 export default function BlogCard({
   post,
   selectedTags,
   onTagToggle,
 }: {
   post: Post;
   selectedTags: string[];
   onTagToggle: (tag: string) => void;
 }) {
-  const excerpt =
-    post.body
-      ?.filter((b): b is PortableTextBlock => b?._type === 'block')
-      .map(b => (b.children || []).map(c => c.text || '').join(' '))
-      .join(' ')
-      .slice(0, 160) || '';
+  const excerpt = generateExcerpt(post.body);

59-59: Move font-family to Tailwind configuration.

The inline style prop breaks the styling convention established by Tailwind classes elsewhere. Define this font stack in tailwind.config.js under theme.extend.fontFamily and reference it via a class (e.g., font-rounded).

🔎 Proposed refactor

In tailwind.config.js:

module.exports = {
  theme: {
    extend: {
      fontFamily: {
        rounded: ['ui-rounded', 'system-ui', '-apple-system', 'sans-serif'],
      },
    },
  },
};

Then update the component:

         className="
           text-[22px]
           font-semibold
+          font-rounded
           text-gray-900
           leading-snug
           hover:text-gray-700
           transition
         "
-        style={{ fontFamily: 'ui-rounded, system-ui, -apple-system' }}
       >
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dfaa3bc and c4445fc.

⛔ Files ignored due to path filters (2)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • studio/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • frontend/components/blog/BlogCard.tsx
  • frontend/components/blog/BlogFilters.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/components/blog/BlogCard.tsx (1)
frontend/components/blog/BlogFilters.tsx (1)
  • PortableTextBlock (17-21)
🔇 Additional comments (13)
frontend/components/blog/BlogCard.tsx (5)

5-6: LGTM: Relative imports improve module cohesion.

The shift to relative imports for co-located components is appropriate and PortableTextBlock is correctly exported from BlogFilters.


25-36: LGTM: Enhanced card styling with smooth transitions.

The custom shadow utilities provide refined visual depth, and the transition on hover improves the user experience.


38-45: LGTM: Responsive aspect ratio improves layout consistency.

The change from fixed height (h-56) to aspect-[16/9] ensures consistent image proportions across different viewport sizes, and the gray background provides a better loading experience.


113-117: LGTM: Conditional rendering prevents empty paragraph elements.

Checking for excerpt existence before rendering improves the DOM structure and avoids unnecessary layout shifts.


124-136: LGTM: Accessible full-width button with clear visual feedback.

The button styling provides appropriate visual hierarchy and hover states, improving the overall user experience.

frontend/components/blog/BlogFilters.tsx (8)

37-37: LGTM: Type safety improved by replacing any with PortableText.

Replacing any and any[] with the structured PortableText type provides better compile-time safety and IDE support.

Also applies to: 50-50


62-76: LGTM: Properly implemented decorative icon component.

The SearchIcon correctly uses aria-hidden="true" to hide the decorative SVG from assistive technologies, and the flexible className prop allows for styling customization.


107-113: LGTM: Efficient tag aggregation with proper memoization.

Using useMemo with a Set for deduplication and normalizeTag for consistency is the right approach. The alphabetical sort ensures predictable autocomplete suggestions.


115-127: LGTM: Autocomplete logic correctly implements prefix matching.

The suggestion algorithm efficiently finds the first alphabetically-matching tag and correctly computes the remainder for inline display. The memoization prevents unnecessary recalculations on each render.


161-176: LGTM: Keyboard interactions enhance autocomplete UX.

The Tab-to-complete and Enter-to-add interactions are correctly implemented with proper guards and preventDefault calls. This provides a smooth typing experience.


193-242: LGTM: Tag controls provide clear, accessible interactions.

The conditional rendering logic correctly uses inputTrim instead of raw input, and the title attribute on the remove button (line 207) aids discoverability. The consistent gray styling aligns well with the overall design.


253-257: LGTM: Clear empty state messaging.

The no-results message provides helpful feedback when filters return no matches, improving the user experience.


12-29: Consider making PortableText types more comprehensive to match Sanity schema.

The type definitions are incomplete compared to your Sanity blockContent schema. Sanity blocks include style, marks, and level fields, and images include additional properties, but your types only define the minimal subset currently used (_type, text, children, url). While this works for current code that only extracts plain text, it creates maintenance risk. If code expands to access schema fields like style or marks, those accesses won't be type-safe. Additionally, PostDetails.tsx and AuthorModal.tsx type bio and body as any instead of using the exported types from BlogFilters.tsx. Consider aligning your type definitions with Sanity's actual structure or documenting why the minimal subset is intentional.

Comment on lines 90 to 92
{post.tags.map((tag, i) => {
const norm = normalizeTag(tag);
const norm = tag.toLowerCase();
const active = selectedTags.includes(norm);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Inconsistent tag normalization—use the shared normalizeTag helper.

Line 91 uses tag.toLowerCase(), but BlogFilters.tsx exports a more comprehensive normalizeTag function that strips #, trims whitespace, lowercases, and normalizes internal spaces. This inconsistency can cause tag-matching bugs if tags have leading/trailing whitespace or other variations.

🔎 Proposed fix

Import and use the existing helper:

 import AuthorModal from './AuthorModal';
-import type { Post, PortableTextBlock } from './BlogFilters';
+import type { Post, PortableTextBlock } from './BlogFilters';
+import { normalizeTag } from './BlogFilters';

 export default function BlogCard({
   post,
   selectedTags,
   onTagToggle,
 }: {
   post: Post;
   selectedTags: string[];
   onTagToggle: (tag: string) => void;
 }) {
   const excerpt = // ...

   return (
     <article ...>
       ...
       {post.tags?.length ? (
         <div className="mt-3 flex flex-wrap gap-2">
           {post.tags.map((tag, i) => {
-            const norm = tag.toLowerCase();
+            const norm = normalizeTag(tag);
             const active = selectedTags.includes(norm);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{post.tags.map((tag, i) => {
const norm = normalizeTag(tag);
const norm = tag.toLowerCase();
const active = selectedTags.includes(norm);
import AuthorModal from './AuthorModal';
import type { Post, PortableTextBlock } from './BlogFilters';
import { normalizeTag } from './BlogFilters';
export default function BlogCard({
post,
selectedTags,
onTagToggle,
}: {
post: Post;
selectedTags: string[];
onTagToggle: (tag: string) => void;
}) {
// ... other code ...
{post.tags.map((tag, i) => {
const norm = normalizeTag(tag);
const active = selectedTags.includes(norm);
🤖 Prompt for AI Agents
In frontend/components/blog/BlogCard.tsx around lines 90 to 92, the code
normalizes tags using tag.toLowerCase(), which is inconsistent with the shared
normalizeTag helper in BlogFilters.tsx; import normalizeTag from its module and
replace tag.toLowerCase() with normalizeTag(tag) so tags are stripped of '#',
trimmed, lowercased and have internal spaces normalized, and ensure selectedTags
uses the same normalization for matching.

addTag(input);
}
}}
placeholder="Пошук..."
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Localization inconsistency: placeholder in Ukrainian but UI in English.

The search input placeholder is "Пошук..." (Ukrainian), but all other UI text in this file is hardcoded in English: "Add" (line 224), "Clear" (line 238), "Remove tag" (line 207), and "No posts found for selected tags." (line 255). This creates an inconsistent user experience.

Consider either:

  1. Using English throughout: placeholder="Search..."
  2. Implementing proper i18n for all strings using next-intl (currently imported but unused)
🔎 Quick fix for consistency
-            placeholder="Пошук..."
+            placeholder="Search..."
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
placeholder="Пошук..."
placeholder="Search..."
🤖 Prompt for AI Agents
In frontend/components/blog/BlogFilters.tsx around line 177 (and nearby lines
207, 224, 238, 255), the placeholder is in Ukrainian while other UI strings are
in English, causing localization inconsistency; fix by making strings
consistent: either replace placeholder="Пошук..." with placeholder="Search..."
to keep English everywhere, or preferably use the existing next-intl import to
internationalize all hardcoded strings (replace literal strings "Add", "Clear",
"Remove tag", "No posts found for selected tags.", and the placeholder with
calls to intl.formatMessage or <FormattedMessage> using appropriate message
keys) and ensure next-intl is actually used and message keys are added to the
locale files.

@ViktorSvertoka ViktorSvertoka merged commit 8c756de into develop Dec 23, 2025
6 checks passed
@ViktorSvertoka ViktorSvertoka deleted the sanity branch December 23, 2025 12:31
@coderabbitai coderabbitai Bot mentioned this pull request Dec 23, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Dec 31, 2025
liudmylasovetovs pushed a commit that referenced this pull request Jan 9, 2026
This was referenced Jan 9, 2026
This was referenced Jan 25, 2026
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