feat: complete frontend build#2
Conversation
- Root layout, fonts, and global styles - UI primitives: Button, Badge, Input, Textarea, Card - Navbar and Footer with brand assets - Home page: Hero, Products, Latest Releases, Hackathon strip - About page - Products list and individual product pages - Blog/Updates list and individual post pages - Team page - Careers page with application form - Contact page with contact form - 404 page - Admin dashboard: all CRUD pages, TipTap editor, image upload
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (24)
📝 WalkthroughWalkthroughThis PR implements a complete website and admin panel for CodedDevs, a technology company. It introduces public-facing pages for home, products, blog, team, careers, and contact alongside an admin dashboard for managing content and applications. The implementation includes documentation updates defining design direction, database schema, API routes, and component specifications. ChangesWebsite & Admin Platform Implementation
Sequence Diagram(s)sequenceDiagram
participant User as User<br/>(Browser)
participant PublicPage as Public Page<br/>(Next.js)
participant DB as Database
participant AdminPage as Admin Page<br/>(Next.js)
rect rgba(0, 150, 200, 0.5)
Note over User,DB: Public Content Flow
User->>PublicPage: Request /products
PublicPage->>DB: SELECT * FROM products<br/>ORDER BY order_index
DB-->>PublicPage: Product rows
PublicPage-->>User: Rendered products grid<br/>with cards & links
User->>PublicPage: Click product link
PublicPage->>DB: SELECT * FROM products<br/>WHERE slug = ?
DB-->>PublicPage: Product details
PublicPage-->>User: Rendered product page<br/>with images & description
end
rect rgba(150, 0, 200, 0.5)
Note over User,DB: Admin Management Flow
User->>AdminPage: Request /admin/products
AdminPage->>DB: SELECT * FROM products<br/>ORDER BY order_index
DB-->>AdminPage: Product rows
AdminPage-->>User: Rendered products table<br/>with edit/delete actions
User->>AdminPage: Click edit product
AdminPage->>DB: SELECT * FROM products<br/>WHERE id = ?
DB-->>AdminPage: Product record
AdminPage-->>User: Rendered product form<br/>with current values
User->>AdminPage: Submit form changes
AdminPage->>DB: UPDATE products<br/>SET name = ?, ...
DB-->>AdminPage: Success/Error response
AdminPage-->>User: Refresh page & show<br/>success/error message
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 14
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (7)
src/app/admin/careers/page.tsx-45-50 (1)
45-50:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winKeep the actions cell as a table cell.
Putting
flexdirectly on the<td>changes its display behavior and can break table sizing/alignment. Wrap the buttons in an inner flex container instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/admin/careers/page.tsx` around lines 45 - 50, The actions cell currently applies layout classes directly to the <td>, which alters table rendering; remove layout classes like "flex gap-2" from the <td> and keep only table-related classes (e.g., "px-4 py-3"), then wrap the Button, Link and AdminDeleteButton elements in an inner container (e.g., a <div> with "flex gap-2") to handle spacing and alignment; update the element containing Button, Link and AdminDeleteButton so the <td> remains a table cell and the inner div provides the flex layout.src/app/admin/blog/page.tsx-57-62 (1)
57-62:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winKeep the actions cell as a table cell.
Putting
flexdirectly on the<td>changes its display behavior and can break table sizing/alignment. Wrap the buttons in an inner flex container instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/admin/blog/page.tsx` around lines 57 - 62, The actions cell currently applies "flex" to the <td> which alters table layout; remove the flex from the <td> (keep padding classes like "px-4 py-3") and wrap the Button and AdminDeleteButton in an inner element with the flex layout (e.g., add a <div className="flex gap-2"> around <Button asChild>... and <AdminDeleteButton endpoint=...>), keeping existing components and Link usage intact (refer to the <td>, <Button asChild>, <Link href={`/admin/blog/${post.id}`}>Edit</Link>, and <AdminDeleteButton endpoint={`/api/admin/blog/${post.id}`} />).src/app/admin/products/page.tsx-45-50 (1)
45-50:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winKeep the actions cell as a table cell.
Putting
flexdirectly on the<td>changes its display behavior and can break table sizing/alignment. Wrap the buttons in an inner flex container instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/admin/products/page.tsx` around lines 45 - 50, The actions cell currently applies "flex" directly to the <td>, which alters table layout; remove the flex/gap classes from the <td> and instead add an inner wrapper (e.g., a <div> with className="flex gap-2 px-0 py-0" or similar) that contains the <Button asChild> (Link to `/admin/products/${product.id}`) and <AdminDeleteButton endpoint={`/api/admin/products/${product.id}`} /> so the <td> remains a proper table cell while the buttons retain their horizontal spacing and styles.src/components/ui/Input.tsx-16-45 (1)
16-45:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winGenerate a stable id for labeled/error inputs.
Relying on
props.nameleaveshtmlFor,aria-describedby, and the error<p>id broken when callers pass neitheridnorname. This is a small but real accessibility regression on a shared form primitive.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/Input.tsx` around lines 16 - 45, The component currently sets inputId = id ?? props.name which breaks labeling/aria when both are absent; generate a stable fallback id inside the Input component (e.g., const generatedId = useId() or useRef(generateUnique()) on first render) and then set inputId = id ?? props.name ?? generatedId; update all uses (label htmlFor, input id, aria-describedby, and error <p> id) to rely on this inputId so labeled/error inputs remain accessible even if callers omit id and name.src/components/ui/Textarea.tsx-16-33 (1)
16-33:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winEnsure a stable fallback ID for label/error associations.
If neither
idnornameis provided, the label and error text lose semantic linkage. Add a generated fallback id (e.g.,useId) sohtmlFor/aria-describedbyalways point to a real element.Suggested patch
-import type { TextareaHTMLAttributes } from "react"; +import { useId, type TextareaHTMLAttributes } from "react"; @@ export default function Textarea({ @@ }: TextareaProps) { - const textareaId = id ?? props.name; + const generatedId = useId(); + const textareaId = id ?? props.name ?? generatedId;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/Textarea.tsx` around lines 16 - 33, The Textarea component can lose label/error associations when both id and props.name are missing; generate a stable fallback id by importing and calling React's useId and setting textareaId = id ?? props.name ?? useId(), then use that textareaId for htmlFor, id, and aria-describedby (and the error element id like `${textareaId}-error`) so label and aria attributes always point to a real element; update imports to include useId and ensure textareaId is computed once at the top of the component.src/app/(public)/products/[slug]/page.tsx-155-157 (1)
155-157:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winParagraph key can collide when description repeats text.
Using the paragraph string as
keycan duplicate keys and cause unstable rendering in edge cases.Suggested patch
- {product.description.split(/\n{2,}/).map((paragraph) => ( - <p key={paragraph}>{paragraph}</p> + {product.description.split(/\n{2,}/).map((paragraph, index) => ( + <p key={`${index}-${paragraph.slice(0, 24)}`}>{paragraph}</p> ))}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(public)/products/[slug]/page.tsx around lines 155 - 157, The map over product.description uses the paragraph string as the React key which can collide for repeated paragraphs; change the key to a stable unique identifier by using the map index or a derived index-based key (e.g., use map((paragraph, idx) => ...) and set key={`paragraph-${idx}`}) so each <p> rendered in the product.description.split(...) mapping has a unique, stable key instead of the raw paragraph text.src/components/contact/ContactForm.tsx-107-133 (1)
107-133:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winGuard
response.json()so non-JSON responses don't throw.If
/api/contactreturns an empty body or HTML error page, the current parse step bypasses the!response.okhandling and drops into the generic catch-all path.🛠 Suggested fix
- const result: unknown = await response.json(); + let result: unknown = null; + try { + result = await response.json(); + } catch { + result = null; + } if (!response.ok) { setSubmitError(getErrorMessage(result)); setSubmitState("idle"); return; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/contact/ContactForm.tsx` around lines 107 - 133, The call to response.json() can throw on empty or non-JSON responses; update the fetch handling in ContactForm so you first check Content-Type (response.headers.get("content-type") includes "application/json") or wrap response.json() in a try/catch/conditional to safely parse only when JSON is present — if not JSON, fall back to using response.statusText or await response.text() as the error payload; then use that payload with getErrorMessage(result) and setSubmitError/setSubmitState as before and ensure parsing errors don't bypass the !response.ok branch.
🧹 Nitpick comments (4)
src/components/layout/AdminSidebar.tsx (1)
78-78: ⚡ Quick winUse “Updates” as the visible label for blog navigation.
The route can remain
/admin/blog, but the displayed label should follow the project-wide naming rule.♻️ Suggested change
- { href: "/admin/blog", label: "Blog", icon: <DocumentIcon /> }, + { href: "/admin/blog", label: "Updates", icon: <DocumentIcon /> },As per coding guidelines "
{src/app/(public)/blog/**/*.tsx,src/components/**/*.tsx}: Blog routes use /blog URL internally but display as 'Updates' to users".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/layout/AdminSidebar.tsx` at line 78, In the AdminSidebar component update the navigation entry that currently reads { href: "/admin/blog", label: "Blog", icon: <DocumentIcon /> } so the visible label is "Updates" while keeping the href as "/admin/blog"; locate the nav items array (inside AdminSidebar) and change the label value for the object with href "/admin/blog" to "Updates" to conform to the project-wide naming rule.src/components/layout/Navbar.tsx (1)
40-40: ⚡ Quick winAlign spacing utilities with the allowed spacing scale.
gap-7andgap-1.5are outside the approved spacing list for component files.♻️ Suggested change
- <nav className="hidden items-center gap-7 md:flex" aria-label="Primary"> + <nav className="hidden items-center gap-6 md:flex" aria-label="Primary"> ... - <span className="flex flex-col gap-1.5" aria-hidden="true"> + <span className="flex flex-col gap-2" aria-hidden="true">As per coding guidelines "
src/components/**/*.tsx: Use Tailwind spacing units (4, 8, 12, 16, 20, 24, 32, 48, 64, 96) for all padding and margin".Also applies to: 71-71
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/layout/Navbar.tsx` at line 40, The nav spacing utilities (gap-7 and gap-1.5) in the Navbar component violate the approved Tailwind spacing scale; update the className(s) in Navbar.tsx (the <nav ...> and the other occurrence around line 71) to use nearest allowed spacing tokens (replace gap-7 with gap-6 or gap-8 and replace gap-1.5 with gap-2 or gap-4) so all padding/margin/gap utilities conform to the approved list for src/components/**/*.tsx.src/components/layout/Footer.tsx (1)
97-117: ⚡ Quick winUse the heading font for the footer section titles.
These
h2elements are styled withfont-sans, which conflicts with the typography rule for headings. As per coding guidelines, "Use JetBrains Mono for headings (H1-H4) and IBM Plex Sans for body text per typography table."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/layout/Footer.tsx` around lines 97 - 117, The two footer section headings in Footer (the <h2> elements for "Company" and "Products") are using the body font class "font-sans"; replace that class with the project's heading-font utility for H1–H4 (the JetBrains Mono heading font used across the app) so headings follow the typography rule — update the className on both <h2> elements (currently containing "font-sans text-sm font-medium text-[`#121F38`]") to use the heading font utility instead while keeping the rest of the classes intact.src/components/blog/UpdatesList.tsx (1)
75-75: ⚡ Quick winUse an allowed spacing token for vertical rhythm consistency.
space-y-10is outside the approved spacing set. Switch to an allowed token (for examplespace-y-8orspace-y-12) to stay consistent with the design system.🎯 Minimal diff
- <div className="space-y-10"> + <div className="space-y-8">As per coding guidelines "
src/components/**/*.tsx: Use Tailwind spacing units (4, 8, 12, 16, 20, 24, 32, 48, 64, 96) for all padding and margin."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/blog/UpdatesList.tsx` at line 75, The div in the UpdatesList component uses a non-approved spacing token "space-y-10"; replace that token with an approved Tailwind spacing token such as "space-y-8" or "space-y-12" in the className on the element in UpdatesList.tsx (look for the div with className="space-y-10") to conform to the design-system spacing set (4,8,12,16,20,24,32,48,64,96).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`(public)/products/page.tsx:
- Around line 77-82: The primary product CTA currently uses an internal Link to
`/products/${product.slug}`; change it to open the product's external URL in a
new tab (use an anchor or Link configured with href={product.externalUrl} plus
target="_blank" and rel="noopener noreferrer") and, if product.externalUrl is
missing/falsey, hide the CTA entirely; update the component(s) referenced (the
Link usage in page.tsx and the ProductsSection component in
src/components/sections/ProductsSection.tsx) to check product.externalUrl before
rendering and to apply target="_blank" rel="noopener noreferrer" when rendering
the CTA.
In `@src/app/admin/applications/page.tsx`:
- Around line 7-23: AdminApplicationsPage is querying sensitive
careerApplications without an auth guard; add a NextAuth session check at the
start of the AdminApplicationsPage function (before the db.select call) by
calling the appropriate session helper (e.g., getServerSession / getSession with
your auth options) and if no valid session exists return a 401 JSON response.
Ensure the session check runs in the same server-side context as
AdminApplicationsPage and short-circuits the handler with a 401 JSON payload
when unauthorized.
In `@src/app/admin/blog/page.tsx`:
- Around line 19-23: AdminBlogPage currently queries the database
unconditionally; add an authentication gate at the start of AdminBlogPage to
validate the current session and immediately return a 401 response when
unauthenticated before calling db.select().from(blogPosts). Use your app’s
existing auth helper (e.g., getSession/getServerSession or your auth middleware)
inside AdminBlogPage to check the session, and only proceed to the
db.select().from(blogPosts).orderBy(desc(blogPosts.created_at)) call when the
session is valid; return a Response/NextResponse with status 401 (or throw an
appropriate unauthorized error) otherwise.
In `@src/app/admin/careers/page.tsx`:
- Around line 7-11: AdminCareersPage currently queries the database before
verifying the request is authenticated; add an auth gate at the start of the
AdminCareersPage function that checks the current session and immediately
returns a 401 response (or throws an unauthorized response) when no valid
session exists, and only proceed to run the
db.select().from(careers).orderBy(...) when the session is present; locate the
authentication helper you use across admin routes (e.g.,
getServerSession/getSession/requireAuth) and call it at the top of
AdminCareersPage before the careers query to enforce the admin-route auth
requirement.
In `@src/app/admin/dashboard/page.tsx`:
- Around line 81-83: Add an explicit server-side session validation at the start
of AdminDashboardPage before calling getDashboardData(): call your session
verification helper (e.g., getSession / verifyAdminSession) and if it returns no
valid session, immediately return the proper unauthorized handling (throw a 401
Response, redirect to login, or render an unauthorized UI) instead of loading
stats; ensure this gate is applied in AdminDashboardPage and mirrors the pattern
required by your admin routes so getDashboardData() only runs for authenticated
admin sessions.
In `@src/app/admin/layout.tsx`:
- Around line 9-18: Add a server-side session guard at the top of the admin
layout: import getServerSession (from next-auth) and your authOptions, and
import redirect from next/navigation; inside the default exported layout
function (the component that renders AdminSidebar and children) call await
getServerSession(req?/context?) or the app-router-compatible
getServerSession(authOptions) to verify a valid session, and if it returns null
call redirect('/admin/login') (or throw a redirect) before returning the JSX;
ensure you reference getServerSession, authOptions, redirect and the layout
component (the one that renders AdminSidebar and children) so admin pages are
protected server-side.
In `@src/app/admin/messages/page.tsx`:
- Around line 7-12: Add a server-side session guard at the top of
AdminMessagesPage before any DB access: call your NextAuth session helper (e.g.,
getServerSession or your custom requireAdminSession) inside AdminMessagesPage
and verify the session/role, and if not present/authorized perform a
redirect/throw (using next/navigation redirect or a 401) so the db.select()
against contactSubmissions only runs for authenticated admin users; update
AdminMessagesPage to check the session first (and reference
getServerSession/authOptions or your requireAdminSession helper) and bail out
before calling db or contactSubmissions.
In `@src/app/admin/products/page.tsx`:
- Around line 7-11: AdminProductsPage currently queries the DB unconditionally
which exposes products to unauthenticated users; add an auth gate at the start
of AdminProductsPage that validates the session (e.g., call your app's
session/auth helper) and if no valid session return a 401 response before
executing the db.select(). Ensure the check occurs before the call to
db.select().from(products) so unauthorized requests never reach the database
query.
In `@src/components/admin/ApplicationsManager.tsx`:
- Around line 119-125: The expansion toggle is currently bound to a
non-focusable <tr onClick> (using setOpenId and openId with application.id),
making it inaccessible to keyboard users; move the click handler into a
focusable control by adding a <button> (placed inside a <td> in the row) that
calls setOpenId(openId === application.id ? null : application.id), add
aria-expanded={openId === application.id} and an accessible label (e.g., "Show
details" / "Hide details"), and remove the onClick from the <tr>; apply the same
change for the other expandable block referenced around lines 131-155 so all
expansion toggles are keyboard- and screen-reader-accessible.
- Around line 3-5: In the ApplicationsManager component, replace inline ternary
Tailwind class strings on the filter buttons with the project's cn() helper: add
an import for cn from the utils module and update the className props on the
filter button elements (the buttons that toggle the filter state / use
setFilter) to use cn(condition, "base-classes", { "active-classes": condition })
instead of string interpolation; also update the other occurrence around the
filter buttons (the block noted at lines 96-100) so all conditional Tailwind
classes use cn().
- Around line 66-81: The updateStatus function currently calls fetch without
guarding against network exceptions—wrap the fetch + response handling inside a
try/catch in updateStatus (the async function updateStatus(id, status)) so any
thrown fetch errors are caught; in the catch block call window.alert (or set the
same failure UI path) and return, and keep the existing response.ok check after
the awaited fetch to handle HTTP error responses.
In `@src/components/admin/MessagesManager.tsx`:
- Around line 81-85: The table rows currently toggle expansion by clicking the
<tr> with onClick using setOpenId/openId and message.id, which is inaccessible
to keyboard users; replace the clickable <tr> with a focusable control (e.g., a
button placed inside the row) or add keyboard handlers and proper ARIA on an
interactive element: move the onClick logic to a button element (or add
onKeyDown that handles Enter/Space) and include aria-expanded={openId ===
message.id} and aria-controls pointing to the expandable content ID to ensure
keyboard focus and screen-reader visibility; apply the same change for the other
rows referenced around the instances using setOpenId/openId/message.id (lines
noted in the comment).
- Around line 32-49: The markAsRead function currently relies only on
response.ok and can throw uncaught fetch errors; wrap the fetch call and
response handling for markAsRead (and the similar handler around lines 51-66) in
a try/catch, await the fetch inside the try, check response.ok and show
window.alert on non-ok, and in the catch log the error (console.error or process
logger) and show a user-friendly alert so no rejections bubble up; only update
state via setItems after a successful response.
In `@src/components/sections/ProductsSection.tsx`:
- Around line 51-53: The product links in the ProductsSection component use the
Link element for product navigation but are missing the required new-tab
attributes; update both Link usages that render
href={`/products/${product.slug}`} (and the other product Link) to include
target="_blank" and rel="noopener noreferrer" so product links open in a new tab
safely; locate the Link elements in ProductsSection (the ones referencing
product.slug) and add those attributes to each Link element.
---
Minor comments:
In `@src/app/`(public)/products/[slug]/page.tsx:
- Around line 155-157: The map over product.description uses the paragraph
string as the React key which can collide for repeated paragraphs; change the
key to a stable unique identifier by using the map index or a derived
index-based key (e.g., use map((paragraph, idx) => ...) and set
key={`paragraph-${idx}`}) so each <p> rendered in the
product.description.split(...) mapping has a unique, stable key instead of the
raw paragraph text.
In `@src/app/admin/blog/page.tsx`:
- Around line 57-62: The actions cell currently applies "flex" to the <td> which
alters table layout; remove the flex from the <td> (keep padding classes like
"px-4 py-3") and wrap the Button and AdminDeleteButton in an inner element with
the flex layout (e.g., add a <div className="flex gap-2"> around <Button
asChild>... and <AdminDeleteButton endpoint=...>), keeping existing components
and Link usage intact (refer to the <td>, <Button asChild>, <Link
href={`/admin/blog/${post.id}`}>Edit</Link>, and <AdminDeleteButton
endpoint={`/api/admin/blog/${post.id}`} />).
In `@src/app/admin/careers/page.tsx`:
- Around line 45-50: The actions cell currently applies layout classes directly
to the <td>, which alters table rendering; remove layout classes like "flex
gap-2" from the <td> and keep only table-related classes (e.g., "px-4 py-3"),
then wrap the Button, Link and AdminDeleteButton elements in an inner container
(e.g., a <div> with "flex gap-2") to handle spacing and alignment; update the
element containing Button, Link and AdminDeleteButton so the <td> remains a
table cell and the inner div provides the flex layout.
In `@src/app/admin/products/page.tsx`:
- Around line 45-50: The actions cell currently applies "flex" directly to the
<td>, which alters table layout; remove the flex/gap classes from the <td> and
instead add an inner wrapper (e.g., a <div> with className="flex gap-2 px-0
py-0" or similar) that contains the <Button asChild> (Link to
`/admin/products/${product.id}`) and <AdminDeleteButton
endpoint={`/api/admin/products/${product.id}`} /> so the <td> remains a proper
table cell while the buttons retain their horizontal spacing and styles.
In `@src/components/contact/ContactForm.tsx`:
- Around line 107-133: The call to response.json() can throw on empty or
non-JSON responses; update the fetch handling in ContactForm so you first check
Content-Type (response.headers.get("content-type") includes "application/json")
or wrap response.json() in a try/catch/conditional to safely parse only when
JSON is present — if not JSON, fall back to using response.statusText or await
response.text() as the error payload; then use that payload with
getErrorMessage(result) and setSubmitError/setSubmitState as before and ensure
parsing errors don't bypass the !response.ok branch.
In `@src/components/ui/Input.tsx`:
- Around line 16-45: The component currently sets inputId = id ?? props.name
which breaks labeling/aria when both are absent; generate a stable fallback id
inside the Input component (e.g., const generatedId = useId() or
useRef(generateUnique()) on first render) and then set inputId = id ??
props.name ?? generatedId; update all uses (label htmlFor, input id,
aria-describedby, and error <p> id) to rely on this inputId so labeled/error
inputs remain accessible even if callers omit id and name.
In `@src/components/ui/Textarea.tsx`:
- Around line 16-33: The Textarea component can lose label/error associations
when both id and props.name are missing; generate a stable fallback id by
importing and calling React's useId and setting textareaId = id ?? props.name ??
useId(), then use that textareaId for htmlFor, id, and aria-describedby (and the
error element id like `${textareaId}-error`) so label and aria attributes always
point to a real element; update imports to include useId and ensure textareaId
is computed once at the top of the component.
---
Nitpick comments:
In `@src/components/blog/UpdatesList.tsx`:
- Line 75: The div in the UpdatesList component uses a non-approved spacing
token "space-y-10"; replace that token with an approved Tailwind spacing token
such as "space-y-8" or "space-y-12" in the className on the element in
UpdatesList.tsx (look for the div with className="space-y-10") to conform to the
design-system spacing set (4,8,12,16,20,24,32,48,64,96).
In `@src/components/layout/AdminSidebar.tsx`:
- Line 78: In the AdminSidebar component update the navigation entry that
currently reads { href: "/admin/blog", label: "Blog", icon: <DocumentIcon /> }
so the visible label is "Updates" while keeping the href as "/admin/blog";
locate the nav items array (inside AdminSidebar) and change the label value for
the object with href "/admin/blog" to "Updates" to conform to the project-wide
naming rule.
In `@src/components/layout/Footer.tsx`:
- Around line 97-117: The two footer section headings in Footer (the <h2>
elements for "Company" and "Products") are using the body font class
"font-sans"; replace that class with the project's heading-font utility for
H1–H4 (the JetBrains Mono heading font used across the app) so headings follow
the typography rule — update the className on both <h2> elements (currently
containing "font-sans text-sm font-medium text-[`#121F38`]") to use the heading
font utility instead while keeping the rest of the classes intact.
In `@src/components/layout/Navbar.tsx`:
- Line 40: The nav spacing utilities (gap-7 and gap-1.5) in the Navbar component
violate the approved Tailwind spacing scale; update the className(s) in
Navbar.tsx (the <nav ...> and the other occurrence around line 71) to use
nearest allowed spacing tokens (replace gap-7 with gap-6 or gap-8 and replace
gap-1.5 with gap-2 or gap-4) so all padding/margin/gap utilities conform to the
approved list for src/components/**/*.tsx.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 145ad31d-256b-4d28-92ef-e270daa097f0
📒 Files selected for processing (54)
AGENTS.mdCLAUDE.mdsrc/app/(public)/about/page.tsxsrc/app/(public)/blog/[slug]/page.tsxsrc/app/(public)/blog/page.tsxsrc/app/(public)/careers/page.tsxsrc/app/(public)/contact/page.tsxsrc/app/(public)/layout.tsxsrc/app/(public)/page.tsxsrc/app/(public)/products/[slug]/page.tsxsrc/app/(public)/products/page.tsxsrc/app/(public)/team/page.tsxsrc/app/admin/applications/page.tsxsrc/app/admin/blog/[id]/page.tsxsrc/app/admin/blog/new/page.tsxsrc/app/admin/blog/page.tsxsrc/app/admin/careers/[id]/page.tsxsrc/app/admin/careers/new/page.tsxsrc/app/admin/careers/page.tsxsrc/app/admin/dashboard/page.tsxsrc/app/admin/layout.tsxsrc/app/admin/messages/page.tsxsrc/app/admin/products/[id]/page.tsxsrc/app/admin/products/new/page.tsxsrc/app/admin/products/page.tsxsrc/app/admin/team/[id]/page.tsxsrc/app/admin/team/new/page.tsxsrc/app/admin/team/page.tsxsrc/app/globals.csssrc/app/layout.tsxsrc/app/not-found.tsxsrc/components/admin/AdminDeleteButton.tsxsrc/components/admin/ApplicationsManager.tsxsrc/components/admin/ImageUpload.tsxsrc/components/admin/MessagesManager.tsxsrc/components/admin/ResourceForms.tsxsrc/components/admin/RichTextEditor.tsxsrc/components/blog/PostContent.tsxsrc/components/blog/UpdatesList.tsxsrc/components/careers/ApplicationForm.tsxsrc/components/contact/ContactForm.tsxsrc/components/layout/AdminSidebar.tsxsrc/components/layout/Footer.tsxsrc/components/layout/Navbar.tsxsrc/components/sections/HackathonStrip.tsxsrc/components/sections/HeroSection.tsxsrc/components/sections/LatestReleasesSection.tsxsrc/components/sections/ProductsSection.tsxsrc/components/ui/Badge.tsxsrc/components/ui/Button.tsxsrc/components/ui/Card.tsxsrc/components/ui/Input.tsxsrc/components/ui/Textarea.tsxtailwind.config.ts
| <Link | ||
| href={`/products/${product.slug}`} | ||
| className="text-[#121F38] hover:text-[#1A2D4F]" | ||
| > | ||
| Learn more | ||
| </Link> |
There was a problem hiding this comment.
Use external new-tab behavior for the primary product link.
Line 77 currently routes to an internal slug page. This conflicts with the product-link requirement. Make the primary CTA open the product’s external URL in a new tab (target="_blank" rel="noopener noreferrer"), or conditionally hide the CTA when no external URL exists.
As per coding guidelines {src/components/sections/ProductsSection.tsx,src/app/(public)/products/**/*.tsx}: Product links must always open in a new tab with target="_blank" rel="noopener noreferrer".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`(public)/products/page.tsx around lines 77 - 82, The primary product
CTA currently uses an internal Link to `/products/${product.slug}`; change it to
open the product's external URL in a new tab (use an anchor or Link configured
with href={product.externalUrl} plus target="_blank" and rel="noopener
noreferrer") and, if product.externalUrl is missing/falsey, hide the CTA
entirely; update the component(s) referenced (the Link usage in page.tsx and the
ProductsSection component in src/components/sections/ProductsSection.tsx) to
check product.externalUrl before rendering and to apply target="_blank"
rel="noopener noreferrer" when rendering the CTA.
| <Link | ||
| href={`/products/${product.slug}`} | ||
| className="inline-block font-mono text-2xl font-semibold leading-[1.3] text-[#121F38] hover:text-[#1A2D4F]" |
There was a problem hiding this comment.
Product detail links need the required new-tab attributes.
Both product links should include target="_blank" and rel="noopener noreferrer" to match the product-link policy.
Suggested patch
<Link
href={`/products/${product.slug}`}
+ target="_blank"
+ rel="noopener noreferrer"
className="inline-block font-mono text-2xl font-semibold leading-[1.3] text-[`#121F38`] hover:text-[`#1A2D4F`]"
>
@@
<Button asChild variant="ghost">
- <Link href={`/products/${product.slug}`}>Read more →</Link>
+ <Link
+ href={`/products/${product.slug}`}
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ Read more →
+ </Link>
</Button>Also applies to: 76-77
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/sections/ProductsSection.tsx` around lines 51 - 53, The
product links in the ProductsSection component use the Link element for product
navigation but are missing the required new-tab attributes; update both Link
usages that render href={`/products/${product.slug}`} (and the other product
Link) to include target="_blank" and rel="noopener noreferrer" so product links
open in a new tab safely; locate the Link elements in ProductsSection (the ones
referencing product.slug) and add those attributes to each Link element.
What does this PR do?
Type of change
Checklist
Screenshots (if UI changes)
Notes for reviewer
Summary by CodeRabbit