Skip to content

Multi-page design-drift audit: sidebar nav, double-header fix, pricing/tools layout#39

Open
projectamazonph wants to merge 2 commits into
mainfrom
claude/session-vg077i
Open

Multi-page design-drift audit: sidebar nav, double-header fix, pricing/tools layout#39
projectamazonph wants to merge 2 commits into
mainfrom
claude/session-vg077i

Conversation

@projectamazonph

@projectamazonph projectamazonph commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to #38 (landing page design-drift fix). Audited the rest of the app — pricing, sign-in/sign-up, tools index, and the authenticated area generally — against the Field Manual spec in docs/stitch-prompts.md, and found two tiers of drift.

Structural (the bigger finding):

  • Every authenticated page used the same public marketing header (always "Sign in / Get started", no indication you're logged in), plus an inconsistent mobile bottom nav only 3 of 6 dashboard-area pages actually had. Spec calls for a 240px left sidebar + top bar on every authenticated screen — the pattern /admin already has, just never extended to the student side.
  • Building that surfaced a real, already-shipped bug: /admin has been rendering the public header above its own sidebar+TopBar this whole time, so a logged-in admin sees a redundant "Sign in / Get started" bar right next to their own name and a working "Sign out" button. Root cause: the root layout always rendered that header unconditionally, with nothing to hide it for shell routes.
  • Also found /dashboard was dead code: middleware redirected it (and only it, unconditionally) to /, apparently as over-broad legacy-redirect logic that was meant to catch /dashboard/<feature> bookmarks but also swallowed the bare path, making the actual dashboard page permanently unreachable.

Fixes:

  • New (dashboard)/layout.tsx gives every page in that route group a sidebar (Dashboard/Courses/Tools/Live classes/Certificates/Payments) + top bar, reusing and generalizing the admin NavSidebar/TopBar components instead of duplicating them.
  • New SiteHeader component hides the public header on shell routes (/admin and the (dashboard) group), fixing the double-header bug on both areas.
  • Narrowed the middleware's legacy redirect to /dashboard/<feature> sub-paths only, restoring the real /dashboard page.
  • Removed the now-redundant BottomNav from the pages that had it, fixed stale /dashboard/* links to their real routes.
  • Pricing page's three tiers rendered as equal-width columns; spec wants the featured Accelerated Mastery tier as a visually larger hero card with the other two smaller and quieter. Fixed via column widths and padding only — no changes to the checkout/payment form fields or logic.
  • Tools index rendered all 5 tools as an equal grid; spec wants the first tool as a full-width hero card with the remaining 4 in a 2x2 grid below.
  • Sign-in/sign-up card heading and subtitle were left-aligned; spec shows them centered.
  • Cleaned up a few em-dashes in pricing copy noticed while already in the file (AGENTS.md bans them repo-wide).

Test plan

  • Typecheck and lint clean
  • Verified via Playwright against a local Postgres + seeded admin account: landing, pricing, sign-in, sign-up, /dashboard, /courses, /tools, /admin — no double header anywhere, sidebar nav renders with correct active states on all authenticated pages, TopBar shows the real user role (not hardcoded "Admin")
  • Sign-out verified end-to-end: clears session, /dashboard correctly redirects to /auth/signin?redirect=%2Fdashboard afterward
  • Manual review in a real browser

Scope notes

  • Did not touch /courses/[slug], lesson pages, quiz pages, payments, certificates, or live-classes content/layout in detail — only confirmed they now sit correctly inside the new sidebar shell. Those may have their own drift against the spec's screens 6-8, 13-17; flagging as a possible follow-up, not covered here.
  • Left the middleware's route coverage alone beyond the /dashboard fix — noticed its matcher (/admin/:path*, /dashboard/:path*) doesn't cover /courses, /tools, /payments, /certificates, or /live-classes, so those routes rely solely on page-level requireAuth() with no edge-level defense-in-depth. That's a security-relevant change, not a design one, so left it as-is rather than bundling it in here.

Co-Authored-By: Claude Sonnet 5
Claude-Session: https://claude.ai/code/session_01Q2obMCR2P3RHTZMqph1QL1


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added a consistent dashboard layout with sidebar navigation and top bar.
    • Added a reusable marketing header for public pages.
    • Dashboard navigation now supports course and tools destinations directly.
  • Improvements

    • Tools page highlights the featured tool with updated responsive styling.
    • Dashboard displays the signed-in user’s role dynamically.
    • Improved pricing layout and sign-in page text alignment.
  • Bug Fixes

    • Updated legacy dashboard links and redirects for improved navigation.

claude added 2 commits July 17, 2026 22:45
The Field Manual design spec (docs/stitch-prompts.md) calls for a
240px left sidebar + top bar on every authenticated screen. The app
had no such layout: pages under (dashboard) rendered under the same
public marketing header used on the landing page (always showing
"Sign in / Get started" regardless of session state) plus an
inconsistent mobile bottom nav that only 3 of 6 pages actually used.

- New (dashboard)/layout.tsx wraps every page in that route group
  with a sidebar (Dashboard/Courses/Tools/Live classes/Certificates/
  Payments) and the existing admin TopBar component, mirroring the
  pattern /admin already uses.
- Generalized NavSidebar to accept item lists instead of hardcoding
  the admin nav, so both areas share one component.
- Fixed TopBar's role label, hardcoded to "Admin" — reusing it for
  students would have mislabeled every non-admin user.
- New SiteHeader component makes the public header hide itself on
  shell routes (/admin, /dashboard, /courses, /tools, /payments,
  /certificates, /live-classes). Root layout.tsx previously rendered
  that header unconditionally, so /admin has been showing a
  redundant "Sign in / Get started" bar above its own sidebar+TopBar
  (which correctly shows the logged-in user) this whole time — the
  same bug the new student sidebar would have inherited unfixed.
- Middleware was unconditionally redirecting bare /dashboard to /,
  intended for legacy /dashboard/<feature> bookmarks but written
  to also swallow /dashboard itself. That made the dashboard page
  component permanently unreachable. Narrowed the redirect to
  /dashboard/* sub-paths only.
- Removed the now-redundant BottomNav from tools/courses/dashboard
  and fixed their stale /dashboard/* links to the real routes.
- Pricing tiers rendered as three equal-width columns; spec calls
  for the featured Accelerated Mastery tier to read as a hero card
  (larger, more padding) with the other two as smaller, quieter
  side cards. Fixed via column-width and padding, no markup changes
  to the checkout flow.
- Sign-in/sign-up card heading and subtitle were left-aligned where
  spec shows them centered inside the card.
- Removed two em-dashes in pricing copy (AGENTS.md bans them
  repo-wide) noticed while already in the file.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds route-aware marketing and authenticated dashboard shells, makes sidebar navigation configurable, updates dashboard and tools routes, removes page-level bottom navigation, changes legacy redirect handling, and refines sign-in and pricing presentation.

Changes

Application shell and presentation

Layer / File(s) Summary
Route-aware marketing header
src/app/layout.tsx, src/components/SiteHeader.tsx
Moves the public header into SiteHeader, which hides itself on application-shell routes and renders brand and authentication links elsewhere.
Authenticated dashboard shell
src/app/(dashboard)/layout.tsx, src/app/(dashboard)/layout.module.css, src/components/ui/NavSidebar.tsx, src/components/ui/TopBar.tsx
Adds an authenticated responsive dashboard layout with student navigation, configurable sidebar properties, and role-based top-bar text.
Dashboard route and page migration
src/middleware.ts, src/app/(dashboard)/dashboard/page.tsx, src/app/(dashboard)/courses/page.tsx, src/app/(dashboard)/tools/*
Updates course and tools links, removes page-level bottom navigation, presents the first tool as a hero card, and changes legacy /dashboard/* redirect behavior.
Public page presentation updates
src/app/(public)/auth/signin/auth.module.css, src/app/(public)/pricing/*
Centers sign-in title text, adjusts pricing copy punctuation, and changes pricing tier alignment and featured-tier desktop sizing.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant Middleware
  participant DashboardLayout
  participant NavSidebar
  participant TopBar
  participant DashboardPage
  Browser->>Middleware: request dashboard route
  Middleware->>DashboardLayout: continue protected request
  DashboardLayout->>DashboardLayout: requireAuth()
  DashboardLayout->>NavSidebar: render student navigation
  DashboardLayout->>TopBar: render authenticated role
  DashboardLayout->>DashboardPage: render routed content
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: sidebar navigation, header fix, and pricing/tools layout updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/session-vg077i

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/`(dashboard)/tools/tools.module.css:
- Around line 5-7: Replace the unsupported :global(h3) selector in .heroCard
with a local class-based selector, and apply that class to the hero CardTitle
component so it retains the existing font-size styling while passing Stylelint.

In `@src/components/SiteHeader.tsx`:
- Around line 11-15: Add regression tests for SiteHeader covering a public route
that renders the header, an exact shell route such as /admin that returns null,
and a nested shell route such as /dashboard/tools that also returns null. Mock
or provide usePathname for each case and assert the rendered output preserves
the shell-route suppression behavior.

In `@src/middleware.ts`:
- Around line 52-56: Update the comment near the legacy /dashboard bookmark
handling to remove the em dash and replace it with a permitted punctuation mark,
such as a period, comma, or parentheses, without changing the comment’s meaning.
- Around line 57-59: Update the redirect handling in the pathname middleware
branch to clone request.nextUrl, assign the computed remainder to the clone’s
pathname, and redirect using that same-origin URL rather than constructing a URL
from the raw remainder. Preserve the original query parameters, and add
regression coverage for a double-slash path and a redirected URL containing a
query string.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: db26493e-aa82-4a3e-b83c-b77c6695c6f3

📥 Commits

Reviewing files that changed from the base of the PR and between 04329ee and 56638ff.

📒 Files selected for processing (14)
  • src/app/(dashboard)/courses/page.tsx
  • src/app/(dashboard)/dashboard/page.tsx
  • src/app/(dashboard)/layout.module.css
  • src/app/(dashboard)/layout.tsx
  • src/app/(dashboard)/tools/page.tsx
  • src/app/(dashboard)/tools/tools.module.css
  • src/app/(public)/auth/signin/auth.module.css
  • src/app/(public)/pricing/page.tsx
  • src/app/(public)/pricing/pricing.module.css
  • src/app/layout.tsx
  • src/components/SiteHeader.tsx
  • src/components/ui/NavSidebar.tsx
  • src/components/ui/TopBar.tsx
  • src/middleware.ts
💤 Files with no reviewable changes (1)
  • src/app/(dashboard)/courses/page.tsx

Comment on lines +5 to +7
.heroCard :global(h3) {
font-size: var(--text-2xl);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Replace the unsupported :global selector.

Stylelint reports Line 5 as selector-pseudo-class-no-unknown, so this stylesheet fails linting. Apply a local class to the hero CardTitle instead.

Proposed fix
-.heroCard :global(h3) {
+.heroTitle {
  font-size: var(--text-2xl);
}
-<CardTitle>{heroTool.name}</CardTitle>
+<CardTitle className={styles.heroTitle}>{heroTool.name}</CardTitle>
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 5-5: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)

(selector-pseudo-class-no-unknown)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(dashboard)/tools/tools.module.css around lines 5 - 7, Replace the
unsupported :global(h3) selector in .heroCard with a local class-based selector,
and apply that class to the hero CardTitle component so it retains the existing
font-size styling while passing Stylelint.

Source: Linters/SAST tools

Comment on lines +11 to +15
export function SiteHeader() {
const pathname = usePathname();
if (SHELL_PREFIXES.some((prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`))) {
return null;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add regression tests for shell-route header suppression.

Cover a public route, an exact shell route such as /admin, and a nested shell route such as /dashboard/tools. This behavior fixes the double-header regression and should stay protected.

As per coding guidelines, “New features must include tests; admin and business-layer features require tests.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/SiteHeader.tsx` around lines 11 - 15, Add regression tests for
SiteHeader covering a public route that renders the header, an exact shell route
such as /admin that returns null, and a nested shell route such as
/dashboard/tools that also returns null. Mock or provide usePathname for each
case and assert the rendered output preserves the shell-route suppression
behavior.

Source: Coding guidelines

Comment thread src/middleware.ts
Comment on lines +52 to +56
// Legacy /dashboard/<feature> bookmarks (real URLs for those features live
// at /courses, /payments, /tools, /live-classes, /certificates — the
// (dashboard) route group does not contribute to the URL). Strip the
// prefix and continue. Bare /dashboard is its own real page (the student
// home) and falls through to the auth check below instead of redirecting.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the em dash from this comment.

Line 53 violates the repository text policy. Use a period, comma, or parentheses instead.

As per coding guidelines, “Do not use emojis in code or commit messages, and do not use em-dashes. Use periods, commas, or parentheses instead.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/middleware.ts` around lines 52 - 56, Update the comment near the legacy
/dashboard bookmark handling to remove the em dash and replace it with a
permitted punctuation mark, such as a period, comma, or parentheses, without
changing the comment’s meaning.

Source: Coding guidelines

Comment thread src/middleware.ts
Comment on lines +57 to +59
if (pathname.startsWith('/dashboard/')) {
const remainder = pathname.slice('/dashboard'.length);
return NextResponse.redirect(new URL(remainder, request.url));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Keep legacy redirects on the current origin.

Line 59 treats remainder as a URL reference. /dashboard//attacker.example produces //attacker.example, which redirects off-site. This also drops the original query string. Clone request.nextUrl and assign its pathname instead.

Proposed fix
   if (pathname.startsWith('/dashboard/')) {
     const remainder = pathname.slice('/dashboard'.length);
-    return NextResponse.redirect(new URL(remainder, request.url));
+    const redirectUrl = request.nextUrl.clone();
+    redirectUrl.pathname = remainder;
+    return NextResponse.redirect(redirectUrl);
   }

Add regression coverage for a double-slash path and a redirected URL with query parameters.

📝 Committable suggestion

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

Suggested change
if (pathname.startsWith('/dashboard/')) {
const remainder = pathname.slice('/dashboard'.length);
return NextResponse.redirect(new URL(remainder, request.url));
if (pathname.startsWith('/dashboard/')) {
const remainder = pathname.slice('/dashboard'.length);
const redirectUrl = request.nextUrl.clone();
redirectUrl.pathname = remainder;
return NextResponse.redirect(redirectUrl);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/middleware.ts` around lines 57 - 59, Update the redirect handling in the
pathname middleware branch to clone request.nextUrl, assign the computed
remainder to the clone’s pathname, and redirect using that same-origin URL
rather than constructing a URL from the raw remainder. Preserve the original
query parameters, and add regression coverage for a double-slash path and a
redirected URL containing a query string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants