feat: homepage foundation with question cards and navigation polish#27
feat: homepage foundation with question cards and navigation polish#27michellepace merged 9 commits intomainfrom
Conversation
Notes: - Analyses current mock data patterns (DAL vs hard-coded) - Compares MongoDB, Neon PostgreSQL, and Convex - Recommends Convex for AI-assisted development and real-time features - Documents migration path from mock data to production database Planning documentation for transitioning from mock data to a real database. Covers pricing, feature comparisons, and integration considerations with Clerk and Vercel. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove implementation guidance, mock data patterns, and tutorial sections. Retain only the database comparison table and recommendation section. The document now focuses on decision-making rather than implementation detail. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Homepage: - Replace hard-coded [1,2,3,4].map() with getAllQuestions() data fetching - Render QuestionCard components from mock data Components: - Add QuestionCard component displaying title, body excerpt, tags, author, stats - Link question titles to /question/:id route Data layer: - Expand Question type with body, tags, author, votes, answerCount, viewCount - Add getAllQuestions() and update getTopQuestions() to sort by votes - Populate 7 realistic mock questions with varied metadata Utilities: - Add getRelativeTime() for human-readable timestamps - Add unit tests covering minute/hour/day/week/month/year boundaries Establishes the data abstraction pattern for swapping mock data with a database. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Layout: - Reduce top padding from 48px to 24px to align with right sidebar - Increase horizontal padding on smaller screens (24px → 40px) Navigation: - Move ThemeToggle from desktop top bar to left sidebar footer - Add GlobalSearch placeholder component in top bar Aligns page headings with right sidebar content. Theme toggle is now grouped with other persistent controls in the sidebar footer, keeping the top bar cleaner. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Navigation: - Reduce ThemeToggle button from 40px to 32px to match Clerk UserButton size - Update placeholder element to match new button dimensions Fixes horizontal misalignment between the theme toggle icon and user avatar when the left sidebar is expanded. Both elements now share the same 32px width.
Changed GlobalSearch visibility from lg:block to sm:block so it displays at 640px alongside the desktop topbar, rather than waiting until 1024px.
ThemeToggle: - Add size prop with sm/default/lg variants - Default to sm size (previously hardcoded) LeftSidebar: - Use size="lg" for theme toggle - Increase footer padding and element gap MobileNav: - Move ThemeToggle from topbar into sheet footer - Position alongside UserButton when signed in, above auth buttons when signed out MobileTopbar: - Remove ThemeToggle (now inside nav sheet) - Simplify to just logo and hamburger menu Unifies theme toggle sizing across desktop sidebar and mobile nav. Declutters mobile topbar by moving the toggle into the navigation sheet.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughHome page rendering is converted to server-side async data fetching for questions and a new QuestionCard component; question data model expanded. ThemeToggle is moved and enhanced with size variants; GlobalSearch placeholder added. Layout spacing and several navigation components are reorganised. Tests for relative-time utility added. Changes
Sequence DiagramsequenceDiagram
autonumber
participant HomePage as HomePage (async)
participant Data as getAllQuestions()
participant Card as QuestionCard (client)
participant Utils as getRelativeTime()
participant UI as Browser/UI
HomePage->>+Data: await getAllQuestions()
Data-->>-HomePage: Question[]
HomePage->>HomePage: map questions
loop per question
HomePage->>+Card: render with question prop
Card->>+Utils: getRelativeTime(createdAt)
Utils-->>-Card: relative time string
Card->>+UI: render card (title, author, tags, stats)
UI-->>-Card: DOM inserted
end
HomePage->>UI: render page shell with cards
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (13)
app/(app)/layout.tsxapp/(app)/page.tsxcomponents/navigation/desktop-topbar.tsxcomponents/navigation/left-sidebar.tsxcomponents/navigation/mobile-nav.tsxcomponents/navigation/mobile-topbar.tsxcomponents/navigation/theme-toggle.tsxcomponents/question-card.tsxcomponents/search/global-search.tsxlib/data/questions.tslib/utils.test.tslib/utils.tsx_docs/my_notes/what-db.md
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx,md}
📄 CodeRabbit inference engine (CLAUDE.md)
Use British English throughout the codebase (comments, strings, documentation)
Files:
lib/utils.tslib/utils.test.tscomponents/navigation/left-sidebar.tsxcomponents/question-card.tsxapp/(app)/page.tsxcomponents/navigation/mobile-topbar.tsxcomponents/navigation/mobile-nav.tsxlib/data/questions.tsapp/(app)/layout.tsxcomponents/search/global-search.tsxcomponents/navigation/theme-toggle.tsxx_docs/my_notes/what-db.mdcomponents/navigation/desktop-topbar.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Only add "use client" directive when interactivity is needed
Avoid manual useMemo/useCallback unless profiling shows need
Files:
lib/utils.tslib/utils.test.tscomponents/navigation/left-sidebar.tsxcomponents/question-card.tsxapp/(app)/page.tsxcomponents/navigation/mobile-topbar.tsxcomponents/navigation/mobile-nav.tsxlib/data/questions.tsapp/(app)/layout.tsxcomponents/search/global-search.tsxcomponents/navigation/theme-toggle.tsxcomponents/navigation/desktop-topbar.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always use @/ import aliases, even for sibling files (use @/app/fonts instead of ./fonts)
Files:
lib/utils.tslib/utils.test.tscomponents/navigation/left-sidebar.tsxcomponents/question-card.tsxapp/(app)/page.tsxcomponents/navigation/mobile-topbar.tsxcomponents/navigation/mobile-nav.tsxlib/data/questions.tsapp/(app)/layout.tsxcomponents/search/global-search.tsxcomponents/navigation/theme-toggle.tsxcomponents/navigation/desktop-topbar.tsx
🧠 Learnings (8)
📚 Learning: 2025-12-25T15:46:08.787Z
Learnt from: michellepace
Repo: michellepace/devflow PR: 18
File: lib/utils.ts:17-19
Timestamp: 2025-12-25T15:46:08.787Z
Learning: In Tailwind CSS v4, the shorthand bg-(image:--gradient-primary) with parentheses is valid and equivalent to bg-[image:var(--gradient-primary)]. The michellepace/devflow repo standardizes on using the v4 shorthand syntax with parentheses for CSS variable references; when you need a Tailwind utility that references a CSS variable, prefer the shorthand form to maintain consistency across TypeScript files in the project (e.g., lib/**/*.ts).
Applied to files:
lib/utils.tslib/utils.test.tslib/data/questions.ts
📚 Learning: 2025-12-25T13:56:02.914Z
Learnt from: michellepace
Repo: michellepace/devflow PR: 18
File: components/navigation/content-top-bar.tsx:23-30
Timestamp: 2025-12-25T13:56:02.914Z
Learning: In a Grok-style layout, move authenticated user controls (Clerk's UserButton) out of the top navigation and into the sidebar footer (e.g., components/app-sidebar.tsx). Ensure components/navigation/content-top-bar.tsx only renders SignedOut options (Sign in/Sign up). Apply this guideline to all TSX files under components/navigation to enforce consistent placement of authentication UI across the navigation layer.
Applied to files:
components/navigation/left-sidebar.tsxcomponents/navigation/mobile-topbar.tsxcomponents/navigation/mobile-nav.tsxcomponents/navigation/theme-toggle.tsxcomponents/navigation/desktop-topbar.tsx
📚 Learning: 2025-12-10T20:20:46.607Z
Learnt from: michellepace
Repo: michellepace/devflow PR: 7
File: components/navigation/navbar/index.tsx:1-12
Timestamp: 2025-12-10T20:20:46.607Z
Learning: Clerk's Next.js components (SignedIn, SignedOut, SignInButton, SignUpButton, UserButton) from clerk/nextjs can be used inside Server Components without adding 'use client' in the consuming component. They manage client/server boundary internally. When reviewing code, prefer omitting 'use client' in server components that render these Clerk components and avoid introducing client directives solely for these components. This guideline helps maintain server/server boundary and reduce client bundle size.
Applied to files:
components/navigation/left-sidebar.tsxcomponents/question-card.tsxapp/(app)/page.tsxcomponents/navigation/mobile-topbar.tsxcomponents/navigation/mobile-nav.tsxapp/(app)/layout.tsxcomponents/search/global-search.tsxcomponents/navigation/theme-toggle.tsxcomponents/navigation/desktop-topbar.tsx
📚 Learning: 2025-12-28T22:41:43.692Z
Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to app/**/[*]/page.{ts,tsx} : Next.js 16: Dynamic route params must be awaited as they are now a Promise - use { params }: { params: Promise<{ id: string }> }
Applied to files:
app/(app)/page.tsx
📚 Learning: 2025-12-28T22:41:43.692Z
Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to components/auth/clerk-signin.tsx : Sign In component (components/auth/clerk-signin.tsx) should be a client component with theme-aware logo
Applied to files:
components/navigation/mobile-nav.tsxcomponents/navigation/desktop-topbar.tsx
📚 Learning: 2026-01-01T18:42:30.416Z
Learnt from: michellepace
Repo: michellepace/devflow PR: 24
File: lib/data/questions.ts:23-23
Timestamp: 2026-01-01T18:42:30.416Z
Learning: In the devflow project, mock data (such as question titles in lib/data/questions.ts) intentionally includes grammatical errors, capitalisation inconsistencies, and informal language to reflect realistic user input for testing purposes.
Applied to files:
lib/data/questions.ts
📚 Learning: 2025-12-28T22:41:43.692Z
Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to components/auth/clerk-signup.tsx : Sign Up component (components/auth/clerk-signup.tsx) should use a static logo
Applied to files:
components/navigation/desktop-topbar.tsx
📚 Learning: 2025-12-28T22:41:43.692Z
Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to components/clerk-provider.tsx : ClerkProvider should be implemented in components/clerk-provider.tsx and apply shadcn theme and Inter font
Applied to files:
components/navigation/desktop-topbar.tsx
🧬 Code graph analysis (6)
lib/utils.test.ts (1)
lib/utils.ts (1)
getRelativeTime(32-64)
components/question-card.tsx (4)
lib/data/questions.ts (1)
Question(1-15)lib/utils.ts (1)
getRelativeTime(32-64)components/tag-link.tsx (1)
TagLink(13-40)components/right-sidebar/question-link.tsx (1)
QuestionLink(9-21)
components/navigation/mobile-topbar.tsx (1)
components/navigation/mobile-nav.tsx (1)
MobileNav(31-132)
components/navigation/mobile-nav.tsx (2)
components/ui/sheet.tsx (2)
SheetFooter(136-136)SheetFooter(94-102)components/navigation/theme-toggle.tsx (1)
ThemeToggle(20-45)
app/(app)/layout.tsx (1)
components/ui/sidebar.tsx (1)
SidebarInset(321-333)
components/navigation/desktop-topbar.tsx (1)
components/search/global-search.tsx (1)
GlobalSearch(5-7)
🪛 LanguageTool
x_docs/my_notes/what-db.md
[style] ~21-~21: This phrasing can be overused. Try elevating your writing with a more formal alternative.
Context: ...llent | ### Best Fit by Use Case 🎯 | If You Want... | Choose | |----------------|-------...
(IF_YOU_WANT)
[grammar] ~309-~309: Consider using “to” with “prefer”.
Context: ... with AI assistance (Claude Code) - You prefer TypeScript over SQL - You value developer experience over e...
(PREFER_OVER_TO)
[uncategorized] ~336-~336: A comma may be missing after the conjunctive/linking adverb ‘However’.
Context: ...vex has documented Clerk integration However, if you prefer SQL and want the massi...
(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)
🔇 Additional comments (16)
components/search/global-search.tsx (1)
1-7: LGTM!Simple placeholder component correctly omits
"use client"since there's no interactivity. The TODO comment clearly documents the future intent.x_docs/my_notes/what-db.md (1)
1-336: LGTM!Comprehensive and well-structured database strategy documentation. The comparison tables and code examples provide clear guidance. Minor stylistic suggestions from static analysis (e.g., comma placement) are negligible for internal notes.
lib/data/questions.ts (2)
1-15: LGTM!Well-structured type definition with all necessary fields for the question card component. The optional
avatarfield is appropriately marked.
104-114: LGTM!Good implementation choices:
getAllQuestionsis async, preparing for future database integrationgetTopQuestionscorrectly creates a shallow copy with the spread operator before sorting, avoiding mutation ofMOCK_QUESTIONScomponents/navigation/mobile-topbar.tsx (1)
4-15: LGTM!Clean simplification. ThemeToggle has been appropriately relocated to the
MobileNavsheet footer (visible in relevant snippet at lines 89-93 of mobile-nav.tsx), keeping the top bar minimal while maintaining theme toggle accessibility.components/navigation/desktop-topbar.tsx (1)
1-30: Correct omission of "use client" directive.As per learnings, Clerk's Next.js components (
SignedOut,SignInButton,SignUpButton) manage the client/server boundary internally, so this component correctly remains a Server Component.app/(app)/layout.tsx (1)
24-26: LGTM!Layout spacing adjustments align with the PR's navigation polish objectives. The reduced top padding (
pt-6frompt-12) and adjusted horizontal padding provide a tighter content area.lib/utils.ts (1)
28-64: Well-implemented relative time utility.The implementation correctly handles all time granularities with proper singular/plural forms. The "yesterday" special case improves readability.
Minor consideration: If a future date is passed (e.g., due to clock skew or data issues),
diffInMsbecomes negative, and the function will return "just now". Verify this behaviour is acceptable for your use case, or consider adding explicit handling:if (diffInMs < 0) return "just now"; // or "in the future"lib/utils.test.ts (1)
1-37: Excellent test coverage forgetRelativeTime.The parameterised test suite comprehensively covers all time ranges and boundary conditions, including singular/plural forms and transitions between units. The use of fake timers ensures deterministic testing.
components/question-card.tsx (1)
12-63: Well-structured question card component.The component provides a clear, semantic layout with author metadata, linked title, body preview, tags, and statistics. The implementation correctly utilises the extended Question type and integrates well with the relative time utility.
components/navigation/mobile-nav.tsx (1)
87-126: ThemeToggle integration looks good.The footer layout properly adapts for both authenticated and unauthenticated states, with ThemeToggle consistently accessible. The structure maintains good spacing and alignment.
components/navigation/left-sidebar.tsx (1)
99-118: Sidebar footer layout handles responsive states well.The ThemeToggle integration maintains proper spacing and alignment in both expanded and icon-collapsed modes. The responsive classes correctly switch between horizontal and vertical layouts, ensuring usability across different sidebar states.
app/(app)/page.tsx (1)
1-20: Excellent server-side data fetching implementation.The conversion to an async server component with dynamic data fetching follows Next.js best practices. The component properly awaits data, maps over results, and delegates rendering to the QuestionCard component whilst maintaining a clean structure.
components/navigation/theme-toggle.tsx (3)
1-6: LGTM: Client directive and imports are correct.The "use client" directive is appropriate here, as the component requires client-side interactivity (hooks and event handlers). All imports correctly use the @/ alias.
8-18: Excellent type-safe configuration structure.The implementation provides strong type safety with
ThemeToggleSizeand const-assertedsizeConfig. The mapping of button, icon, and skeleton classes for each size variant is clean and maintainable. The default size "sm" (32px) correctly aligns with the UserButton as mentioned in the PR objectives.
20-45: Well-implemented component with proper SSR handling.The component correctly derives configuration from the
sizeprop, handles SSR hydration with a skeleton placeholder, and maintains accessibility with appropriate ARIA attributes. The theme toggle logic and icon styling are correct.
| @@ -0,0 +1,63 @@ | |||
| "use client"; | |||
There was a problem hiding this comment.
Remove unnecessary client directive.
This component contains no client-side interactivity (no state, effects, or event handlers). The getRelativeTime utility is a pure function that can execute on the server, and Next.js Link works in server components. Removing the directive will reduce client bundle size and enable server-side optimisation.
As per coding guidelines: only add "use client" when interactivity is needed.
🔎 Proposed fix
-"use client";
-
import Link from "next/link";🤖 Prompt for AI Agents
components/question-card.tsx lines 1-1: The file begins with a "use client"
directive but the component has no client-side interactivity, so remove that
directive to avoid unnecessary client bundling. Delete the "use client" line at
the top of the file so the component becomes a server component; confirm there
are no hooks, event handlers, or browser-only APIs used elsewhere in the file
and run the app/build to verify no runtime errors.
There was a problem hiding this comment.
Hello @coderabbitai
I am skipping this because if I do your change, when I run npm run dev I get an error.
This is because: getRelativeTime() calls new Date() which returns different values on the server vs the client. When rendered as a Server Component:
- Server renders "5 minutes ago" at time T
- Client hydrates moments later at time T+Δ
- The text has shifted → hydration mismatch error
The "use client" directive ensures consistent rendering by running the time calculation only on the client.
To properly make this a Server Component, I'd need to either pass pre-formatted relative time strings from the server, or use a stable timestamp and format client-side with useEffect. That's over-engineering for minimal bundle savings.
There was a problem hiding this comment.
@michellepace You're absolutely right! I apologize for the confusion. Your explanation is spot on—calling new Date() during render creates a hydration mismatch because the server and client will compute different relative times, even if only milliseconds apart. React will throw an error when the server-rendered HTML doesn't match the client's initial render.
The "use client" directive is indeed the pragmatic solution here. The alternative approaches you mentioned (suppressing hydration warnings, using useEffect, or passing pre-computed strings) would add unnecessary complexity for minimal gain.
Thank you for the clear explanation and for testing it! I've noted this pattern for future reviews.
✏️ Learnings added
Learnt from: michellepace
Repo: michellepace/devflow PR: 27
File: components/question-card.tsx:1-1
Timestamp: 2026-01-04T14:21:37.960Z
Learning: Components that display relative time by calling `new Date()` during render (e.g., via getRelativeTime utility) require the "use client" directive to avoid React hydration mismatch errors, since server and client will compute different timestamps.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to components/auth/clerk-signin.tsx : Sign In component (components/auth/clerk-signin.tsx) should be a client component with theme-aware logo
Learnt from: michellepace
Repo: michellepace/devflow PR: 7
File: components/navigation/navbar/index.tsx:1-12
Timestamp: 2025-12-10T20:20:54.402Z
Learning: Clerk's Next.js components (SignedIn, SignedOut, SignInButton, SignUpButton, UserButton) exported from clerk/nextjs are designed to work in Server Components without requiring a "use client" directive in the consuming component, as they handle the client/server boundary internally with their own directives.
Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to **/*.{ts,tsx} : Only add "use client" directive when interactivity is needed
- Bump @biomejs/biome minimum version in package.json - Update biome.json schema reference to match CLI version Keeps linting toolchain current with latest patch release.
…lSearch Desktop Topbar: - Remove `hidden sm:block` wrapper around GlobalSearch component The parent header already uses `hidden sm:flex`, making the inner visibility classes redundant — child elements inherit the parent's hidden state below the sm breakpoint. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
QuestionCardcomponent with full data layer (getAllQuestions,getTopQuestions)getRelativeTimeutility with unit testsGlobalSearchcomponent to desktop top bar atsmbreakpointTest plan
🤖 Generated with Claude Code