Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdded optional frontmatter fields to blog schema; redesigned blog home and post to content-first two-column layouts; removed hero image rendering; added share buttons, newsletter block, generateStaticParams, InlineTOC active highlighting, variable Mona Sans fonts, socialIcons data and a package export; pagination and CSS tweaks. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (4)
packages/ui/package.json (1)
11-11: Prefer explicitdataexports over wildcard API expansion.Line 11 makes every future file in
src/datapublicly importable, which can create accidental semver commitments.♻️ Suggested export map adjustment
- "./data/*": "./src/data/*.ts", + "./data/footer": "./src/data/footer.ts",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ui/package.json` at line 11, The export map currently exposes "./data/*" -> "./src/data/*.ts", which expands every future file under src/data and risks accidental public API. Replace the wildcard export in package.json by either (a) listing explicit exports for each data module you intend to publish (e.g., "./data/foo": "./src/data/foo.ts", "./data/bar": "./src/data/bar.ts") or (b) create a single barrel entry (e.g., "./data": "./src/data/index.ts") that re-exports only the stable symbols, and remove the "./data/*": "./src/data/*.ts" entry to avoid implicit semver commitments.apps/blog/src/components/BlogGrid.tsx (1)
19-20:badgeis typed as a string but rendered like a boolean flag.Line 85 always renders
"Release"even whenpost.badgecontains a different value. Either renderpost.badgeor change the field type to boolean to match intent.Proposed fix (if badge text is dynamic)
{post.badge && ( - <Badge color="success" label="Release" className="w-min" /> + <Badge color="success" label={post.badge} className="w-min" /> )}Also applies to: 84-86
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/components/BlogGrid.tsx` around lines 19 - 20, The prop type for badge on the Post object (badge?: string | null) is inconsistent with how it's rendered in BlogGrid (post.badge currently causes the UI to always show the literal "Release"); either change the type to a boolean (e.g., badge?: boolean) and update rendering logic in the BlogGrid component to conditionally render the hardcoded "Release" when true, or keep badge as string and update the rendering to display post.badge value (and handle null/undefined) where the current "Release" label is output; locate uses of post.badge in BlogGrid.tsx and adjust the type and render logic to match the chosen approach.packages/eclipse/src/styles/fonts.css (1)
2-13: Addfont-display: swapto reduce FOIT on slow networks.Both
@font-faceblocks omitfont-display; addingswapimproves perceived performance and keeps content readable during font load.💡 Proposed fix
`@font-face` { font-family: "Mona Sans VF"; @@ font-style: normal; font-optical-sizing: auto; + font-display: swap; } @@ `@font-face` { font-family: "Mona Sans VF"; @@ font-style: italic; font-optical-sizing: auto; + font-display: swap; }Also applies to: 15-26
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/eclipse/src/styles/fonts.css` around lines 2 - 13, The `@font-face` declaration for font-family "Mona Sans VF" is missing a font-display setting which can cause FOIT; add font-display: swap to each `@font-face` block (both entries for "Mona Sans VF") so browsers show fallback text immediately and swap in the webfont when ready, keeping the rest of the descriptors (src, font-weight, font-stretch, font-style, font-optical-sizing) unchanged.packages/eclipse/src/styles/globals.css (1)
743-745: Preferfont-stretchover rawfont-variation-settingsfor width utility.For the width axis utility,
font-stretchis more idiomatic and less likely to override other variation settings set elsewhere.💡 Proposed fix
.stretch-display { - font-variation-settings: "wdth" 125; + font-stretch: 125%; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/eclipse/src/styles/globals.css` around lines 743 - 745, Replace the raw variation setting in the .stretch-display rule: remove font-variation-settings: "wdth" 125; and use the idiomatic property font-stretch: 125%; (you can keep a font-variation-settings fallback only if you explicitly need to target a specific variable font instance, but prefer font-stretch for the width axis). Ensure the selector .stretch-display is updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/blog/src/app/`(blog)/[slug]/page.tsx:
- Around line 142-162: Wrap the newsletter Input and Subscribe Button in a
proper <form> and wire it to a server-action submit handler so the CTA actually
sends a subscription request: create an async server action (e.g., export async
function subscribe(formData) { "use server"; /* validate email, call API/DB,
handle errors */ }) and set the form's action to that handler; ensure the Input
has a name (e.g., name="email") so formData.get("email") works, the Button
remains type="submit", and add minimal client feedback (success/error) or
redirect handling inside the server action. Use the existing Input and Button
components and update the page component to import/attach the server action to
the form.
- Around line 115-137: The "Share this article" anchors use static profile URLs
from socialIcons so they don't share the current post; update the code that
renders socialIcons (the map over socialIcons in page.tsx) to compute each
anchor href dynamically using the current post's absolute URL and title (derive
the URL from window.location.href or the page's canonical/permalink passed into
the component) and the appropriate sharing endpoint for each platform (e.g.,
Twitter, Facebook, LinkedIn), keep target="_blank" and rel="noopener
noreferrer", and ensure aria-label and the icon (fa-... referencing
socialLink.icon) reflect the post title so the CTA actually shares the current
article rather than linking to static brand pages.
- Around line 57-63: The Avatar is using a hardcoded src ("/avatar.jpg") so the
frontmatter field page.data?.authorSrc is never used; update the Avatar
component (where it renders inside the conditional for page.data?.authorSrc) to
pass page.data.authorSrc as the src prop (with an optional fallback to the
existing "/avatar.jpg" if you want a default) so the authorSrc frontmatter
controls the image.
In `@apps/blog/src/app/`(blog)/page.tsx:
- Line 141: The current call to BlogGrid uses items.slice(1, -1) which removes
both the featured post and the last post; change the slicing to only exclude the
featured item by using items.slice(1) so BlogGrid(items.slice(1), pageSize={12})
(locate the BlogGrid usage in page.tsx and update the slice from slice(1, -1) to
slice(1)).
- Line 62: The mapping sets authorSrc to null which discards the original avatar
source; locate the item mapping (the array .map callback in page.tsx where
authorSrc: null is assigned) and change it to pass through the real value (e.g.,
authorSrc: item.authorSrc or the appropriate source property) or omit the forced
null so downstream avatar logic can receive the actual URL; update the mapped
object returned by that mapping function to include the correct authorSrc symbol
instead of null.
- Around line 100-109: The featured block dereferences items[0] and always
renders an <img>, which will break on empty lists or missing image data; update
the rendering logic in the component that uses items (the anchor block with
href={items[0].url} and the <img> using items[0].imageSrc/items[0].imageAlt):
first check that items && items.length > 0 and only render the anchor/featured
layout when true (otherwise render nothing or a fallback), and within that block
guard the image by rendering the <img> only when items[0].imageSrc exists
(fallback to a placeholder element or an empty alt text when imageSrc is
missing) to avoid dereferencing undefined and broken media.
In `@apps/blog/src/components/BlogGrid.tsx`:
- Around line 79-80: The grid container's className (the div with "grid
sm:grid-cols-[1fr_384px] overflow-hidden border-b pb-4 sm:pb-6
border-stroke-neutral") is missing the "group" utility, so the child element
that uses "group-hover:…" (the image/thumbnail element around lines ~126-127)
will never receive hover state; fix by adding "group" to that parent container's
className so the child "group-hover:scale-105" (or similar) classes can trigger
on hover.
- Around line 106-113: The Avatar instance in BlogGrid is using hardcoded src
and alt values; change the Avatar props to use the post data instead: set src to
post.authorSrc (or post?.authorSrc) and set alt to the author's name (e.g.,
post.author or post?.author.name/contact field) so each card shows the correct
author image and alt text; keep other props like format/size/disabled as-is
unless intentionally different.
- Around line 61-69: formatDate is parsing date-only strings with new Date(iso)
then formatting in the user's local timezone, causing a one-day shift in
negative UTC offsets; update the formatDate function to parse the input as UTC
and format in UTC (e.g., construct the Date using Date.UTC or by appending
"T00:00:00Z" to the ISO date) and include timeZone: "UTC" in the
toLocaleDateString options so dates like "2025-05-26" always render as that UTC
date; change the implementation inside formatDate to perform these steps.
In `@packages/eclipse/src/styles/fonts.css`:
- Around line 28-29: The html selector's font-family currently uses a quoted
name and no generic fallback; update the font-family declaration in the html
rule so the font name is unquoted and add a fallback chain (e.g., Inter,
system-ui, sans-serif) to satisfy font-family-name-quotes and
font-family-no-missing-generic-family-keyword lint rules.
---
Nitpick comments:
In `@apps/blog/src/components/BlogGrid.tsx`:
- Around line 19-20: The prop type for badge on the Post object (badge?: string
| null) is inconsistent with how it's rendered in BlogGrid (post.badge currently
causes the UI to always show the literal "Release"); either change the type to a
boolean (e.g., badge?: boolean) and update rendering logic in the BlogGrid
component to conditionally render the hardcoded "Release" when true, or keep
badge as string and update the rendering to display post.badge value (and handle
null/undefined) where the current "Release" label is output; locate uses of
post.badge in BlogGrid.tsx and adjust the type and render logic to match the
chosen approach.
In `@packages/eclipse/src/styles/fonts.css`:
- Around line 2-13: The `@font-face` declaration for font-family "Mona Sans VF" is
missing a font-display setting which can cause FOIT; add font-display: swap to
each `@font-face` block (both entries for "Mona Sans VF") so browsers show
fallback text immediately and swap in the webfont when ready, keeping the rest
of the descriptors (src, font-weight, font-stretch, font-style,
font-optical-sizing) unchanged.
In `@packages/eclipse/src/styles/globals.css`:
- Around line 743-745: Replace the raw variation setting in the .stretch-display
rule: remove font-variation-settings: "wdth" 125; and use the idiomatic property
font-stretch: 125%; (you can keep a font-variation-settings fallback only if you
explicitly need to target a specific variable font instance, but prefer
font-stretch for the width axis). Ensure the selector .stretch-display is
updated accordingly.
In `@packages/ui/package.json`:
- Line 11: The export map currently exposes "./data/*" -> "./src/data/*.ts",
which expands every future file under src/data and risks accidental public API.
Replace the wildcard export in package.json by either (a) listing explicit
exports for each data module you intend to publish (e.g., "./data/foo":
"./src/data/foo.ts", "./data/bar": "./src/data/bar.ts") or (b) create a single
barrel entry (e.g., "./data": "./src/data/index.ts") that re-exports only the
stable symbols, and remove the "./data/*": "./src/data/*.ts" entry to avoid
implicit semver commitments.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a27471a1-1acd-43ff-9c4a-c47d78969711
⛔ Files ignored due to path filters (3)
apps/blog/public/fonts/MonaSansVF[wdth,wght,opsz,ital].woff2is excluded by!**/*.woff2apps/blog/public/fonts/monaspace_neon_var.woffis excluded by!**/*.woffapps/blog/public/fonts/monaspace_neon_var.woff2is excluded by!**/*.woff2
📒 Files selected for processing (11)
apps/blog/source.config.tsapps/blog/src/app/(blog)/[slug]/page.tsxapps/blog/src/app/(blog)/page.tsxapps/blog/src/app/global.cssapps/blog/src/app/layout.tsxapps/blog/src/components/BlogGrid.tsxpackages/eclipse/src/components/inline-toc.tsxpackages/eclipse/src/styles/fonts.csspackages/eclipse/src/styles/globals.csspackages/ui/package.jsonpackages/ui/src/data/footer.ts
fe8f43c to
142b21d
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (6)
apps/blog/src/app/(blog)/page.tsx (3)
140-140:⚠️ Potential issue | 🟠 Major
slice(1, -1)drops the final post unintentionally.If only the featured post should be excluded, this should be
slice(1).Suggested fix
- <BlogGrid items={items.slice(1, -1)} pageSize={12} /> + <BlogGrid items={items.slice(1)} pageSize={12} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/page.tsx at line 140, The current use of items.slice(1, -1) in the BlogGrid rendering (BlogGrid items={items.slice(1, -1)} pageSize={12}) unintentionally drops the last post; change it to items.slice(1) so only the featured first item is excluded and all remaining posts are passed to BlogGrid (update the BlogGrid prop to use items.slice(1) instead of items.slice(1, -1)).
62-62:⚠️ Potential issue | 🟠 Major
authorSrcis being dropped during mapping.Line 62 hardcodes
authorSrc: null, so avatar source never reaches the featured card/grid.Suggested fix
- authorSrc: null, + authorSrc: (data.authorSrc as string | undefined) ?? null,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/page.tsx at line 62, The mapping hardcodes authorSrc: null in apps/blog/src/app/(blog)/page.tsx so avatar URLs are never passed to the featured card/grid; change the mapping to pull the real author image field from the source object (e.g., use the post/article/item.authorSrc or post.author?.src property used elsewhere) and only fall back to null when that property is missing, and ensure the resulting value is passed into the featured card/grid prop that consumes authorSrc.
100-109:⚠️ Potential issue | 🟠 MajorGuard featured rendering for empty posts and missing featured image.
items[0]is dereferenced unconditionally; empty datasets will crash. The featured image is also rendered without checking source existence.Also applies to: 125-137
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/page.tsx around lines 100 - 109, The featured post block unconditionally dereferences items[0] and renders an <img> from items[0].imageSrc, which will crash on empty datasets or missing image; update the page component rendering logic to first guard on items && items.length > 0 before rendering the featured post markup (the block around items[0] usage) and, inside that block, only render the <img> when items[0].imageSrc is truthy (or render a fallback/placeholder) to avoid referencing undefined; apply the same guards to the duplicate featured block referenced around lines 125-137 so both occurrences (items[0] usages) are protected.apps/blog/src/app/(blog)/[slug]/page.tsx (3)
57-63:⚠️ Potential issue | 🟠 MajorUse frontmatter
authorSrcin the Avatar component.The block is conditioned on
page.data?.authorSrc, but Line 60 still hardcodes"/avatar.jpg", so the new field is ignored at runtime.Suggested fix
{page.data?.authorSrc && ( <Avatar format="image" - src="/avatar.jpg" - alt="Disabled user" + src={page.data.authorSrc} + alt={page.data.authors[0]} size="lg" - disabled /> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/[slug]/page.tsx around lines 57 - 63, The Avatar component is conditioned on page.data?.authorSrc but still uses a hardcoded src ("/avatar.jpg"); update the Avatar props in the conditional block (the Avatar element in page.tsx) to use page.data.authorSrc (or page.data?.authorSrc) as the src value, optionally keeping a fallback if desired, so the frontmatter authorSrc is actually used at runtime.
120-137:⚠️ Potential issue | 🟠 Major“Share this article” currently links to static social profiles, not share intents.
Line 122 uses
socialLink.url, so users are not sharing the current article.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/[slug]/page.tsx around lines 120 - 137, The mapped links are using socialLink.url (static profiles) instead of share intents for the current article; update the mapping in the component that renders socialIcons so href is a generated share URL (e.g., buildShareUrl(socialLink.icon, encodeURIComponent(currentUrl), encodeURIComponent(title))) instead of socialLink.url. Use the page's canonical URL (compose from site base + slug or use window.location / Next.js router/usePathname) and encodeURIComponent for title and url, switch key usage to socialLink.icon or id instead of idx if available, and replace socialLink.url with the generated shareUrl when rendering the <a> tag.
142-162:⚠️ Potential issue | 🟠 MajorNewsletter CTA is non-functional without a
<form>action/handler.The input + submit button are rendered without submit wiring, so no subscription request can be sent.
I can draft a minimal server-action form wiring (with email validation and success/error state) if you want me to open a follow-up implementation issue.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/[slug]/page.tsx around lines 142 - 162, The newsletter CTA currently renders an Input (id="newsletter") and a submit Button but has no form or submit handler; wrap the input and Button in a <form> and wire an onSubmit to a handler (e.g., subscribeNewsletter or subscribeNewsletterAction) that reads the email value, performs basic email validation, calls the subscription API/server action, and sets success/error state for UI feedback; ensure the handler prevents default (for client form) or use a Next.js server action if preferred, and update the Input/Button usage so the Button type="submit" triggers the handler and shows the result to the user.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/blog/src/app/`(blog)/[slug]/page.tsx:
- Around line 53-56: The render logic dereferences page.data.authors.length
without ensuring authors exists; update the conditional in the JSX that branches
on page.data.authors to first guard the property (e.g., use page.data.authors &&
page.data.authors.length or page.data.authors?.length consistently) before
accessing .length in the ternary, and handle the missing-authors case (render
nothing or a fallback) in the branch that currently uses
page.data.authors.length === 1 so the component (page.tsx) never throws when
authors is undefined.
In `@apps/blog/src/app/`(blog)/page.tsx:
- Line 59: The assignment that does date: data.date ? new
Date(data.date).toISOString() : "" can throw RangeError for malformed
frontmatter; update the code in page.tsx to validate the parsed date before
calling toISOString by creating a Date object from data.date (referencing
data.date and the date property assignment), then check that
!isNaN(parsedDate.getTime()) (or Date.parse(data.date) is not NaN) and only then
use parsedDate.toISOString(), otherwise set the field to an empty string (or
fallback); alternatively wrap the toISOString call in a try/catch and fallback
to "" on failure.
---
Duplicate comments:
In `@apps/blog/src/app/`(blog)/[slug]/page.tsx:
- Around line 57-63: The Avatar component is conditioned on page.data?.authorSrc
but still uses a hardcoded src ("/avatar.jpg"); update the Avatar props in the
conditional block (the Avatar element in page.tsx) to use page.data.authorSrc
(or page.data?.authorSrc) as the src value, optionally keeping a fallback if
desired, so the frontmatter authorSrc is actually used at runtime.
- Around line 120-137: The mapped links are using socialLink.url (static
profiles) instead of share intents for the current article; update the mapping
in the component that renders socialIcons so href is a generated share URL
(e.g., buildShareUrl(socialLink.icon, encodeURIComponent(currentUrl),
encodeURIComponent(title))) instead of socialLink.url. Use the page's canonical
URL (compose from site base + slug or use window.location / Next.js
router/usePathname) and encodeURIComponent for title and url, switch key usage
to socialLink.icon or id instead of idx if available, and replace socialLink.url
with the generated shareUrl when rendering the <a> tag.
- Around line 142-162: The newsletter CTA currently renders an Input
(id="newsletter") and a submit Button but has no form or submit handler; wrap
the input and Button in a <form> and wire an onSubmit to a handler (e.g.,
subscribeNewsletter or subscribeNewsletterAction) that reads the email value,
performs basic email validation, calls the subscription API/server action, and
sets success/error state for UI feedback; ensure the handler prevents default
(for client form) or use a Next.js server action if preferred, and update the
Input/Button usage so the Button type="submit" triggers the handler and shows
the result to the user.
In `@apps/blog/src/app/`(blog)/page.tsx:
- Line 140: The current use of items.slice(1, -1) in the BlogGrid rendering
(BlogGrid items={items.slice(1, -1)} pageSize={12}) unintentionally drops the
last post; change it to items.slice(1) so only the featured first item is
excluded and all remaining posts are passed to BlogGrid (update the BlogGrid
prop to use items.slice(1) instead of items.slice(1, -1)).
- Line 62: The mapping hardcodes authorSrc: null in
apps/blog/src/app/(blog)/page.tsx so avatar URLs are never passed to the
featured card/grid; change the mapping to pull the real author image field from
the source object (e.g., use the post/article/item.authorSrc or post.author?.src
property used elsewhere) and only fall back to null when that property is
missing, and ensure the resulting value is passed into the featured card/grid
prop that consumes authorSrc.
- Around line 100-109: The featured post block unconditionally dereferences
items[0] and renders an <img> from items[0].imageSrc, which will crash on empty
datasets or missing image; update the page component rendering logic to first
guard on items && items.length > 0 before rendering the featured post markup
(the block around items[0] usage) and, inside that block, only render the <img>
when items[0].imageSrc is truthy (or render a fallback/placeholder) to avoid
referencing undefined; apply the same guards to the duplicate featured block
referenced around lines 125-137 so both occurrences (items[0] usages) are
protected.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d04f7c0e-9a6b-47ca-9264-f1e5d2ece99d
📒 Files selected for processing (4)
apps/blog/src/app/(blog)/[slug]/page.tsxapps/blog/src/app/(blog)/page.tsxpackages/ui/package.jsonpackages/ui/src/data/footer.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/ui/src/data/footer.ts
- packages/ui/package.json
142b21d to
3f34703
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (8)
apps/blog/src/components/BlogGrid.tsx (2)
230-237:⚠️ Potential issue | 🟠 MajorAvatar still uses hardcoded author data.
post.authorSrcis checked, but the actualAvatarprops still ignore post-specific values.Suggested fix
{post?.authorSrc && ( <Avatar format="image" - src="/avatar.jpg" - alt="Disabled user" + src={post.authorSrc} + alt={post.author ?? "Author avatar"} size="lg" - disabled /> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/components/BlogGrid.tsx` around lines 230 - 237, The Avatar instance is using hardcoded props despite checking post?.authorSrc; update the Avatar props in BlogGrid (the Avatar JSX where post?.authorSrc is checked) to use the post data: set src={post.authorSrc} (or a fallback), set alt to the author's name (e.g., alt={post.authorName || 'Author avatar'}), and remove or conditionally set disabled/size as needed based on post metadata; ensure you keep the conditional wrapper (post?.authorSrc) so Avatar only renders when a source exists.
203-203:⚠️ Potential issue | 🟡 Minor
group-hoveranimation won’t fire without agroupparent.The image uses
group-hover:*, but the parent link class does not includegroup.Suggested fix
- className="grid sm:grid-cols-[1fr_384px] overflow-hidden border-b pb-4 sm:pb-6 border-stroke-neutral gap-8" + className="group grid sm:grid-cols-[1fr_384px] overflow-hidden border-b pb-4 sm:pb-6 border-stroke-neutral gap-8"Also applies to: 250-250
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/components/BlogGrid.tsx` at line 203, The image's group-hover utilities won't run because the parent link element is missing the "group" class; in BlogGrid (the link/anchor/NextLink component that wraps each post image) add "group" to its className so the child image's group-hover:* rules activate, and apply the same change for the second instance where an image uses group-hover (both occurrences found in BlogGrid.tsx).apps/blog/src/app/(blog)/[slug]/page.tsx (3)
57-64:⚠️ Potential issue | 🟠 MajorAvatar still ignores frontmatter
authorSrc.The condition checks
page.data?.authorSrc, but the renderedAvatarstill uses hardcoded values.Suggested fix
{page.data?.authorSrc && ( <Avatar format="image" - src="/avatar.jpg" - alt="Disabled user" + src={page.data.authorSrc} + alt={page.data.authors[0] ?? "Author avatar"} size="lg" - disabled /> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/[slug]/page.tsx around lines 57 - 64, The Avatar rendering is ignoring frontmatter because it uses hardcoded props; update the Avatar component (the JSX using Avatar) to pass the frontmatter values: set src to page.data?.authorSrc, set alt to something like page.data?.author or a sensible fallback (e.g., "Author avatar"), and keep existing props (format, size, etc.); ensure the conditional stays page.data?.authorSrc so Avatar displays the provided image and falls back correctly when absent.
115-137:⚠️ Potential issue | 🟠 Major“Share this article” currently opens brand profiles, not share intents.
The CTA copy and behavior are mismatched: these links don’t share the current post URL/title.
Suggested direction
+ {/* + Build share URLs from the current article instead of footer profile links. + Example platforms: X, LinkedIn, Facebook. + */} + {(() => { + const postUrl = encodeURIComponent(`https://www.prisma.io/blog/${params.slug}`); + const postTitle = encodeURIComponent(page.data.title); + const shareLinks = [ + { + title: "X", + icon: "x-twitter", + url: `https://twitter.com/intent/tweet?url=${postUrl}&text=${postTitle}`, + }, + { + title: "LinkedIn", + icon: "linkedin", + url: `https://www.linkedin.com/sharing/share-offsite/?url=${postUrl}`, + }, + ]; + return shareLinks.map((socialLink, idx) => ( + <a + key={idx} + href={socialLink.url} + target="_blank" + rel="noopener noreferrer" + aria-label={`Share ${page.data.title} on ${socialLink.title}`} + > + ... + </a> + )); + })()}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/[slug]/page.tsx around lines 115 - 137, The links in the socialIcons render use socialLink.url (opening brand profiles) instead of invoking a share intent; update the component so the anchor hrefs are constructed share URLs (e.g., for Twitter, Facebook, LinkedIn, WhatsApp) that include the current post URL and title via encodeURIComponent rather than using socialLink.url. Either replace each socialLink.url with a computed shareUrl when mapping (buildShareUrl(postUrl, post.title, socialLink.platform) or inline the platform-specific templates) and keep the rest of the anchor props (target, rel, aria-label, key) and icon rendering unchanged so the CTA actually shares the current article.
142-162:⚠️ Potential issue | 🟠 MajorNewsletter CTA is still non-functional.
The input/button UI is rendered, but there is no
<form>action or submit handler to process subscriptions.Suggested fix pattern
- <div className="input flex items-center gap-2 justify-center"> + <form action={subscribe} className="input flex items-center gap-2 justify-center"> <Input type="email" id="newsletter" + name="email" size="2xl" placeholder="email@example.com" className="w-62" /> <Button size="xl" variant="ppg" type="submit" className="whitespace-nowrap font-semibold" > Subscribe </Button> - </div> + </form>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/[slug]/page.tsx around lines 142 - 162, The newsletter CTA renders Input and Button but lacks a form and submit handling; wrap the Input and Button in a <form> and add a controlled state (e.g., newsletterEmail) plus an onSubmit handler (e.g., handleSubscribe or onSubmitSubscribe) that prevents default, validates the email, calls your newsletter subscribe API endpoint, and updates UI state (loading, success, error) or shows a toast; ensure the Button uses type="submit" and the Input value/onChange are wired to newsletterEmail so submissions actually send the captured email to the backend.apps/blog/src/app/(blog)/page.tsx (3)
101-107:⚠️ Potential issue | 🟠 MajorFeatured rendering should guard empty lists before
items[0]access.Directly dereferencing
items[0]will throw if the collection is empty.Suggested fix pattern
+ if (items.length === 0) { + return ( + <main className="flex-1 w-full max-w-249 mx-auto px-4 py-8"> + <h1 className="stretch-display text-4xl font-bold mb-2 landing-h1 text-center mt-9 font-display"> + Blog + </h1> + </main> + ); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/page.tsx around lines 101 - 107, The featured-item JSX dereferences items[0] without checking length, which will throw if items is empty; update the component rendering logic (the block using items[0], e.g., the <a href={items[0].url} ...> and nested <img src={items[0].imageSrc} ...>) to first guard against an empty array — either return null/placeholder when items?.length === 0 or conditionally render the featured markup only when items?.length > 0, ensuring you reference the same "items" variable used in the component.
141-141:⚠️ Potential issue | 🟠 Major
slice(1, -1)still drops the last post.If the intent is to exclude only the featured item, this should be
slice(1).Suggested fix
- <BlogGrid items={items.slice(1, -1)} pageSize={12} /> + <BlogGrid items={items.slice(1)} pageSize={12} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/page.tsx at line 141, The current use of items.slice(1, -1) in the BlogGrid rendering drops the last post unintentionally; update the call in the BlogGrid JSX (where BlogGrid is invoked with items.slice(1, -1) and pageSize={12}) to use items.slice(1) so only the first (featured) item is excluded and the last post is preserved.
63-63:⚠️ Potential issue | 🟠 Major
authorSrcis still discarded during mapping.Forcing
authorSrctonullprevents avatar rendering downstream even when frontmatter provides it.Suggested fix
- authorSrc: null, + authorSrc: (data.authorSrc as string | undefined) ?? null,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/page.tsx at line 63, The mapping unconditionally sets authorSrc to null which discards any frontmatter-provided avatar; update the mapping logic (where authorSrc is assigned) to use the source from the post frontmatter (e.g., post.frontmatter.authorSrc or frontmatter.authorSrc) and only fallback to null or undefined when that value is absent so downstream avatar rendering can receive the real URL (modify the code that currently sets authorSrc: null to assign the frontmatter value instead).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/blog/src/components/BlogGrid.tsx`:
- Around line 35-162: PaginationWithEllipsis renders and wires links for pages
outside [1, totalPages] when totalPages is small; update the component to
validate and clamp page targets and skip rendering invalid links: ensure you
early-return or render minimal controls when totalPages <= 1, only render page
"2" / "totalPages - 1" / neighboring numbers if those values are >=1 and <=
totalPages, and wrap all setCurrentPage calls with a clamp (e.g., Math.max(1,
Math.min(target, totalPages))) or conditional guards so clicks cannot set
out-of-range pages; update the handlers and conditional branches in
PaginationWithEllipsis to use these checks for PaginationLink,
PaginationPrevious, PaginationNext, and ellipsis logic.
---
Duplicate comments:
In `@apps/blog/src/app/`(blog)/[slug]/page.tsx:
- Around line 57-64: The Avatar rendering is ignoring frontmatter because it
uses hardcoded props; update the Avatar component (the JSX using Avatar) to pass
the frontmatter values: set src to page.data?.authorSrc, set alt to something
like page.data?.author or a sensible fallback (e.g., "Author avatar"), and keep
existing props (format, size, etc.); ensure the conditional stays
page.data?.authorSrc so Avatar displays the provided image and falls back
correctly when absent.
- Around line 115-137: The links in the socialIcons render use socialLink.url
(opening brand profiles) instead of invoking a share intent; update the
component so the anchor hrefs are constructed share URLs (e.g., for Twitter,
Facebook, LinkedIn, WhatsApp) that include the current post URL and title via
encodeURIComponent rather than using socialLink.url. Either replace each
socialLink.url with a computed shareUrl when mapping (buildShareUrl(postUrl,
post.title, socialLink.platform) or inline the platform-specific templates) and
keep the rest of the anchor props (target, rel, aria-label, key) and icon
rendering unchanged so the CTA actually shares the current article.
- Around line 142-162: The newsletter CTA renders Input and Button but lacks a
form and submit handling; wrap the Input and Button in a <form> and add a
controlled state (e.g., newsletterEmail) plus an onSubmit handler (e.g.,
handleSubscribe or onSubmitSubscribe) that prevents default, validates the
email, calls your newsletter subscribe API endpoint, and updates UI state
(loading, success, error) or shows a toast; ensure the Button uses type="submit"
and the Input value/onChange are wired to newsletterEmail so submissions
actually send the captured email to the backend.
In `@apps/blog/src/app/`(blog)/page.tsx:
- Around line 101-107: The featured-item JSX dereferences items[0] without
checking length, which will throw if items is empty; update the component
rendering logic (the block using items[0], e.g., the <a href={items[0].url} ...>
and nested <img src={items[0].imageSrc} ...>) to first guard against an empty
array — either return null/placeholder when items?.length === 0 or conditionally
render the featured markup only when items?.length > 0, ensuring you reference
the same "items" variable used in the component.
- Line 141: The current use of items.slice(1, -1) in the BlogGrid rendering
drops the last post unintentionally; update the call in the BlogGrid JSX (where
BlogGrid is invoked with items.slice(1, -1) and pageSize={12}) to use
items.slice(1) so only the first (featured) item is excluded and the last post
is preserved.
- Line 63: The mapping unconditionally sets authorSrc to null which discards any
frontmatter-provided avatar; update the mapping logic (where authorSrc is
assigned) to use the source from the post frontmatter (e.g.,
post.frontmatter.authorSrc or frontmatter.authorSrc) and only fallback to null
or undefined when that value is absent so downstream avatar rendering can
receive the real URL (modify the code that currently sets authorSrc: null to
assign the frontmatter value instead).
In `@apps/blog/src/components/BlogGrid.tsx`:
- Around line 230-237: The Avatar instance is using hardcoded props despite
checking post?.authorSrc; update the Avatar props in BlogGrid (the Avatar JSX
where post?.authorSrc is checked) to use the post data: set src={post.authorSrc}
(or a fallback), set alt to the author's name (e.g., alt={post.authorName ||
'Author avatar'}), and remove or conditionally set disabled/size as needed based
on post metadata; ensure you keep the conditional wrapper (post?.authorSrc) so
Avatar only renders when a source exists.
- Line 203: The image's group-hover utilities won't run because the parent link
element is missing the "group" class; in BlogGrid (the link/anchor/NextLink
component that wraps each post image) add "group" to its className so the child
image's group-hover:* rules activate, and apply the same change for the second
instance where an image uses group-hover (both occurrences found in
BlogGrid.tsx).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7f26c700-90ce-4a64-b7fa-cdd298c1a826
📒 Files selected for processing (7)
apps/blog/source.config.tsapps/blog/src/app/(blog)/[slug]/page.tsxapps/blog/src/app/(blog)/page.tsxapps/blog/src/components/BlogGrid.tsxpackages/eclipse/src/components/pagination.tsxpackages/ui/package.jsonpackages/ui/src/data/footer.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/ui/package.json
3f34703 to
a3caf28
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (11)
apps/blog/src/app/(blog)/[slug]/page.tsx (4)
57-63:⚠️ Potential issue | 🟠 MajorUse
page.data.authorSrcinAvatarinstead of a hardcoded path.Line 60 still ignores frontmatter-driven avatars, so author attribution is incorrect at runtime.
Suggested fix
<Avatar format="image" - src="/avatar.jpg" - alt="Disabled user" + src={page.data.authorSrc} + alt={page.data.authors?.[0] ?? "Author avatar"} size="lg" - disabled />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/[slug]/page.tsx around lines 57 - 63, The Avatar currently uses a hardcoded src ("/avatar.jpg") instead of the frontmatter-driven value; update the Avatar props in the component that renders page.data?.authorSrc (the Avatar JSX where page.data?.authorSrc is checked) to pass page.data.authorSrc as the src prop (e.g., src={page.data.authorSrc}) so the author avatar is sourced from the page data rather than a static path.
146-162:⚠️ Potential issue | 🟠 MajorNewsletter CTA is still non-functional without form submission wiring.
This block renders submit UI but has no
<form action>or handler, so subscriptions cannot be sent.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/[slug]/page.tsx around lines 146 - 162, The newsletter UI renders an Input and Button but lacks a form or submit handler, so subscriptions never get sent; wrap the Input and Button in a <form> and wire it to a handler (either a client-side onSubmit function in the page component or a Next.js server/action or API route) that reads the email from the Input (identify the Input by id="newsletter") and posts it to your subscription endpoint (or calls a subscribe function), handle validation/errors, and preventDefault when using client-side submission; update the page component (where Input and Button are rendered) to call that handler on submit and ensure Button stays type="submit" so the form actually triggers the handler.
53-56:⚠️ Potential issue | 🟠 MajorGuard
authorsbefore dereferencing.length.Line 55 can still throw when
authorsis missing because the branch usespage.data.authors.lengthwithout optional chaining.Suggested fix
- {page.data.authors?.length > 1 ? ( - page.data.authors.join(", ") - ) : page.data.authors.length === 1 ? ( + {page.data.authors?.length > 1 ? ( + page.data.authors.join(", ") + ) : page.data.authors?.length === 1 ? (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/[slug]/page.tsx around lines 53 - 56, The rendering ternary accesses page.data.authors.length without first ensuring authors exists; update the conditional in the JSX that renders authors (the ternary using page.data.authors) to guard against undefined by using optional chaining or a default value (e.g., treat page.data.authors ?? [] or use page.data.authors?.length) so you never dereference .length on undefined and the fallback branch still handles zero/missing authors correctly.
120-137:⚠️ Potential issue | 🟠 Major“Share this article” still links to static profile URLs.
Line 122 uses
socialLink.url, so this CTA doesn’t share the current post URL/title.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/[slug]/page.tsx around lines 120 - 137, The CTA is using socialLink.url (static profile URLs) so the "Share this article" buttons don't share the current post; update the mapping in the JSX that renders socialIcons (inside the map over socialIcons) to compute a shareHref for share-type icons by building a share URL that includes the current post's URL and title (e.g., use the page/post canonical URL or construct from the post slug and metadata.title, and encodeURIComponent) and use that shareHref as the anchor href instead of socialLink.url; keep using socialLink.url for non-share/profile icons and preserve target="_blank" and rel attributes.apps/blog/src/app/(blog)/page.tsx (3)
63-63:⚠️ Potential issue | 🟠 MajorDon’t discard
authorSrcin mapping.Line 63 hardcodes
authorSrctonull, so downstream avatar rendering never receives real data.Suggested fix
- authorSrc: null, + authorSrc: (data.authorSrc as string | undefined) ?? null,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/page.tsx at line 63, The mapping in page.tsx currently hardcodes authorSrc: null which drops real avatar data; update the object mapping that sets authorSrc (the line with "authorSrc: null") to use the source author's field (e.g., author.authorSrc or author.src depending on the source model) and/or fall back to undefined/default if missing, so downstream avatar rendering receives the actual URL; ensure you update the mapper used when building author props (the map callback where authorSrc is set) and keep any existing null/undefined-safe checks.
101-101:⚠️ Potential issue | 🟠 Major
slice(1, -1)unintentionally drops the last post.If only the featured post should be excluded from the grid, this should be
slice(1).Suggested fix
- <BlogGrid items={items.slice(1, -1)} pageSize={12} /> + <BlogGrid items={items.slice(1)} pageSize={12} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/page.tsx at line 101, The BlogGrid is currently passed items.slice(1, -1) which unintentionally omits the last post; update the call that renders the grid (BlogGrid) to use items.slice(1) so only the featured first item is excluded and the rest (including the last post) are included. Locate the BlogGrid usage in page.tsx and replace the slice bounds accordingly.
59-59:⚠️ Potential issue | 🟠 MajorValidate parsed date before calling
toISOString().Line 59 can throw on malformed frontmatter dates and break render.
Suggested fix
- date: data.date ? new Date(data.date).toISOString() : "", + date: (() => { + if (!data.date) return ""; + const parsed = new Date(data.date); + return Number.isNaN(parsed.getTime()) ? "" : parsed.toISOString(); + })(),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/app/`(blog)/page.tsx at line 59, The current assignment "date: data.date ? new Date(data.date).toISOString() : ''" can throw for malformed frontmatter; update the logic in apps/blog/src/app/(blog)/page.tsx to parse the date into a Date object first (e.g., const parsed = new Date(data.date)), validate it with isNaN(parsed.getTime()) (or parsed.toString() === 'Invalid Date'), and only call toISOString() when valid, otherwise fall back to an empty string or a safe default; locate and fix the expression that constructs the date property so it guards against invalid dates.apps/blog/src/components/BlogGrid.tsx (4)
203-210:⚠️ Potential issue | 🟡 Minor
group-hovereffects won’t trigger without agroupparent.Hover-scale classes are present on children, but parent wrappers don’t include
group, so the effect never activates.Suggested fix
- className="grid grid-cols-1 sm:grid-cols-2 rounded-square overflow-hidden border border-stroke-neutral shadow-box-low" + className="group grid grid-cols-1 sm:grid-cols-2 rounded-square overflow-hidden border border-stroke-neutral shadow-box-low" - className="grid sm:grid-cols-[1fr_384px] overflow-hidden border-b pb-4 sm:pb-6 border-stroke-neutral gap-8" + className="group grid sm:grid-cols-[1fr_384px] overflow-hidden border-b pb-4 sm:pb-6 border-stroke-neutral gap-8"Also applies to: 247-295
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/components/BlogGrid.tsx` around lines 203 - 210, The hover scale on child elements (using group-hover) won't activate because the parent wrapper for the grid in BlogGrid (the element with className="grid grid-cols-1 sm:grid-cols-2 ...") is missing the required "group" utility; add the "group" class to that parent wrapper (and to the other parent wrappers around the child elements referenced in the later block where group-hover is used, e.g., the sibling block around items[1] between lines ~247-295) so child classes like "group-hover:scale-[1.02]" work as intended.
187-195:⚠️ Potential issue | 🟡 MinorDate formatting can drift by one day for date-only strings.
new Date("YYYY-MM-DD")with localtoLocaleDateStringcan render the previous day in negative UTC offsets.Suggested fix
return date.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", + timeZone: "UTC", });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/components/BlogGrid.tsx` around lines 187 - 195, The formatDate function can produce dates off-by-one for date-only ISO strings due to local timezone parsing; update formatDate to detect "YYYY-MM-DD" inputs and parse them as UTC (e.g. construct a Date using Date.UTC or append a "T00:00:00Z" before new Date) so the produced Date represents the intended calendar day, then continue to call toLocaleDateString("en-US", ...) for formatting; reference the formatDate helper to implement the UTC-safe parse branch.
33-160:⚠️ Potential issue | 🟠 MajorClamp/guard pagination targets for small page counts.
This control can still produce invalid targets (e.g., page
0or2whentotalPagesis1) and should not render full controls when pagination is unnecessary.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/components/BlogGrid.tsx` around lines 33 - 160, PaginationWithEllipsis can generate invalid page targets (e.g., 0 or >totalPages) and renders full controls when totalPages <= 1; guard and clamp targets and hide controls when unnecessary. In the PaginationWithEllipsis component, add an early return to render nothing (or a minimal non-interactive element) when totalPages <= 1; sanitize clicks by clamping any setCurrentPage calls to Math.max(1, Math.min(target, totalPages)) and replace direct numeric targets (like 0, 2, totalPages-1) with computed safe values; update handlers inside PaginationPrevious, PaginationNext and all PaginationLink onClick callbacks to compute a safeTarget before calling setCurrentPage and use currentPage/currentPage +/-1 only after validating against totalPages.
274-281:⚠️ Potential issue | 🟠 MajorAvatar still uses hardcoded values instead of post data.
Lines 277-280 override
post.authorSrc/post.author, so cards display incorrect author visuals.Suggested fix
{post?.authorSrc && ( <Avatar format="image" - src="/avatar.jpg" - alt="Disabled user" + src={post.authorSrc} + alt={post.author ?? "Author avatar"} size="lg" - disabled /> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blog/src/components/BlogGrid.tsx` around lines 274 - 281, The Avatar component is using hardcoded props instead of the post data, causing wrong author visuals; update the Avatar usage in BlogGrid to use the post object (e.g., change src="/avatar.jpg" to src={post.authorSrc} and alt="Disabled user" to alt={post.author}) and stop overriding the post values (remove any hardcoded/disabled flags that mask real data) so Avatar renders the actual author image/name from post.authorSrc/post.author.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/blog/src/components/BlogGrid.tsx`:
- Around line 200-243: The featured block unconditionally dereferences items[0]
and duplicates that post because visibleItems still contains it; update the
BlogGrid rendering to first guard the featured section with if (items &&
items.length > 0 && currentPage === 1) before using items[0], and ensure
visibleItems does not include the featured item on page 1 by filtering it out
(compare a unique identifier like url or id) or by computing visibleItems as
items.slice(1) when currentPage === 1; adjust references to items, visibleItems,
and currentPage accordingly so the featured card is only shown when items exists
and is not duplicated in the list.
---
Duplicate comments:
In `@apps/blog/src/app/`(blog)/[slug]/page.tsx:
- Around line 57-63: The Avatar currently uses a hardcoded src ("/avatar.jpg")
instead of the frontmatter-driven value; update the Avatar props in the
component that renders page.data?.authorSrc (the Avatar JSX where
page.data?.authorSrc is checked) to pass page.data.authorSrc as the src prop
(e.g., src={page.data.authorSrc}) so the author avatar is sourced from the page
data rather than a static path.
- Around line 146-162: The newsletter UI renders an Input and Button but lacks a
form or submit handler, so subscriptions never get sent; wrap the Input and
Button in a <form> and wire it to a handler (either a client-side onSubmit
function in the page component or a Next.js server/action or API route) that
reads the email from the Input (identify the Input by id="newsletter") and posts
it to your subscription endpoint (or calls a subscribe function), handle
validation/errors, and preventDefault when using client-side submission; update
the page component (where Input and Button are rendered) to call that handler on
submit and ensure Button stays type="submit" so the form actually triggers the
handler.
- Around line 53-56: The rendering ternary accesses page.data.authors.length
without first ensuring authors exists; update the conditional in the JSX that
renders authors (the ternary using page.data.authors) to guard against undefined
by using optional chaining or a default value (e.g., treat page.data.authors ??
[] or use page.data.authors?.length) so you never dereference .length on
undefined and the fallback branch still handles zero/missing authors correctly.
- Around line 120-137: The CTA is using socialLink.url (static profile URLs) so
the "Share this article" buttons don't share the current post; update the
mapping in the JSX that renders socialIcons (inside the map over socialIcons) to
compute a shareHref for share-type icons by building a share URL that includes
the current post's URL and title (e.g., use the page/post canonical URL or
construct from the post slug and metadata.title, and encodeURIComponent) and use
that shareHref as the anchor href instead of socialLink.url; keep using
socialLink.url for non-share/profile icons and preserve target="_blank" and rel
attributes.
In `@apps/blog/src/app/`(blog)/page.tsx:
- Line 63: The mapping in page.tsx currently hardcodes authorSrc: null which
drops real avatar data; update the object mapping that sets authorSrc (the line
with "authorSrc: null") to use the source author's field (e.g., author.authorSrc
or author.src depending on the source model) and/or fall back to
undefined/default if missing, so downstream avatar rendering receives the actual
URL; ensure you update the mapper used when building author props (the map
callback where authorSrc is set) and keep any existing null/undefined-safe
checks.
- Line 101: The BlogGrid is currently passed items.slice(1, -1) which
unintentionally omits the last post; update the call that renders the grid
(BlogGrid) to use items.slice(1) so only the featured first item is excluded and
the rest (including the last post) are included. Locate the BlogGrid usage in
page.tsx and replace the slice bounds accordingly.
- Line 59: The current assignment "date: data.date ? new
Date(data.date).toISOString() : ''" can throw for malformed frontmatter; update
the logic in apps/blog/src/app/(blog)/page.tsx to parse the date into a Date
object first (e.g., const parsed = new Date(data.date)), validate it with
isNaN(parsed.getTime()) (or parsed.toString() === 'Invalid Date'), and only call
toISOString() when valid, otherwise fall back to an empty string or a safe
default; locate and fix the expression that constructs the date property so it
guards against invalid dates.
In `@apps/blog/src/components/BlogGrid.tsx`:
- Around line 203-210: The hover scale on child elements (using group-hover)
won't activate because the parent wrapper for the grid in BlogGrid (the element
with className="grid grid-cols-1 sm:grid-cols-2 ...") is missing the required
"group" utility; add the "group" class to that parent wrapper (and to the other
parent wrappers around the child elements referenced in the later block where
group-hover is used, e.g., the sibling block around items[1] between lines
~247-295) so child classes like "group-hover:scale-[1.02]" work as intended.
- Around line 187-195: The formatDate function can produce dates off-by-one for
date-only ISO strings due to local timezone parsing; update formatDate to detect
"YYYY-MM-DD" inputs and parse them as UTC (e.g. construct a Date using Date.UTC
or append a "T00:00:00Z" before new Date) so the produced Date represents the
intended calendar day, then continue to call toLocaleDateString("en-US", ...)
for formatting; reference the formatDate helper to implement the UTC-safe parse
branch.
- Around line 33-160: PaginationWithEllipsis can generate invalid page targets
(e.g., 0 or >totalPages) and renders full controls when totalPages <= 1; guard
and clamp targets and hide controls when unnecessary. In the
PaginationWithEllipsis component, add an early return to render nothing (or a
minimal non-interactive element) when totalPages <= 1; sanitize clicks by
clamping any setCurrentPage calls to Math.max(1, Math.min(target, totalPages))
and replace direct numeric targets (like 0, 2, totalPages-1) with computed safe
values; update handlers inside PaginationPrevious, PaginationNext and all
PaginationLink onClick callbacks to compute a safeTarget before calling
setCurrentPage and use currentPage/currentPage +/-1 only after validating
against totalPages.
- Around line 274-281: The Avatar component is using hardcoded props instead of
the post data, causing wrong author visuals; update the Avatar usage in BlogGrid
to use the post object (e.g., change src="/avatar.jpg" to src={post.authorSrc}
and alt="Disabled user" to alt={post.author}) and stop overriding the post
values (remove any hardcoded/disabled flags that mask real data) so Avatar
renders the actual author image/name from post.authorSrc/post.author.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f6d21350-ffb9-4984-836c-66afed83bf1c
📒 Files selected for processing (5)
apps/blog/src/app/(blog)/[slug]/page.tsxapps/blog/src/app/(blog)/page.tsxapps/blog/src/components/BlogGrid.tsxpackages/ui/package.jsonpackages/ui/src/data/footer.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/ui/src/data/footer.ts
- packages/ui/package.json
a3caf28 to
e428ad8
Compare
Add missing descriptions
Update img to Image
e428ad8 to
692c0ff
Compare
790a1fa to
abc884d
Compare
Summary by CodeRabbit
New Features
Improvements
Data