Skip to content

button navigation fix#156

Merged
aristachauhan merged 6 commits intomainfrom
abc/sidebar-button-fix
Feb 19, 2026
Merged

button navigation fix#156
aristachauhan merged 6 commits intomainfrom
abc/sidebar-button-fix

Conversation

@aristachauhan
Copy link
Copy Markdown
Contributor

@aristachauhan aristachauhan commented Feb 2, 2026

Summary by CodeRabbit

  • Refactor
    • Chat navigation now uses group-prefixed URLs for session routes, so new chats and first responses open under their group's path.
    • Navigation now relies on the app router and refresh mechanisms instead of direct browser history manipulation, improving routing consistency.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 2, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

MainContent's first-response detection and subsequent navigation were changed to use a group-prefixed path from aiState.group (e.g., /${aiState.group}/chat/new and /${aiState.group}/chat/{id}); direct use of window.history.pushState was removed in favor of router navigation.

Changes

Cohort / File(s) Summary
Chat Routing Enhancement
web/src/components/chat/main.tsx
First-response trigger and post-save navigation updated to use /${aiState.group}/chat/...; removed direct window.history.pushState usage and rely on router methods.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 I hopped through code with a careful tug,
Paths now wear a group-sized rug,
No more stray routes to mend,
We follow the group to the chat's end! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'button navigation fix' is vague and does not clearly describe the main change, which involves updating pathname routing logic and navigation to use group-prefixed chat paths. Consider revising the title to be more specific about the change, such as 'Update chat navigation to use group-prefixed paths' to better reflect the actual code modifications.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch abc/sidebar-button-fix

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.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@web/src/components/layout/collapsibleSidebar.tsx`:
- Around line 69-74: Update the onClick handler in collapsibleSidebar.tsx to
allow modifier-key and middle-click behavior and to normalize paths before
comparing: first guard out clicks that should open in a new tab/window (if
e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) and return
without preventDefault, then compare a normalized form of
window.location.pathname and newChatUrl (e.g., strip trailing slashes with a
small helper like normalize(path) => path.replace(/\/+$/, '') || '/') and only
call e.preventDefault() and window.location.assign(newChatUrl) when the
normalized paths match.

Comment on lines 69 to 74
onClick={(e) => {
if (window.location.pathname === newChatUrl) {
e.preventDefault();
window.location.assign(newChatUrl);
}
}}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Next.js Link onClick preventDefault modifier keys new tab behavior

💡 Result:

If you call e.preventDefault() in a next/link onClick handler unconditionally, you also cancel the browser’s default “open in new tab/window” behaviors (Cmd/Ctrl-click, middle-click, Shift-click). onClick fires for those clicks too, so you must only prevent default on a plain left click. Next.js’ docs also note that modifier-key clicks won’t run onNavigate because they’re treated as “new tab” navigation. [1]

Use this pattern:

import Link from "next/link";
import { useRouter } from "next/navigation";

function isPlainLeftClick(e: React.MouseEvent<HTMLAnchorElement>) {
  return (
    e.button === 0 &&                 // left click
    !e.defaultPrevented &&
    !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey
  );
}

export function MyLink() {
  const router = useRouter();

  return (
    <Link
      href="/dashboard"
      onClick={(e) => {
        // Let the browser handle new-tab/window gestures
        if (!isPlainLeftClick(e)) return;

        // Also don’t hijack if the link targets another browsing context
        const target = (e.currentTarget as HTMLAnchorElement).target;
        if (target && target !== "_self") return;

        e.preventDefault();
        router.push("/dashboard");
      }}
    >
      Dashboard
    </Link>
  );
}

Alternative (App Router): prefer onNavigate when your goal is “optionally cancel SPA navigation”, because it’s specifically for client-side navigation and supports e.preventDefault()—but it won’t run for Ctrl/Cmd-click new-tab gestures anyway. [1]

Sources: Next.js Link docs (onNavigate, modifier keys behavior, preventDefault). [1]

[1] https://nextjs.org/docs/app/api-reference/components/link


🏁 Script executed:

# Find and inspect the collapsibleSidebar.tsx file to understand context
find . -name "collapsibleSidebar.tsx" -type f

Repository: ucdavis/PolicyWonk

Length of output: 112


🏁 Script executed:

# Read the collapsibleSidebar.tsx file to understand the context
cat -n ./web/src/components/layout/collapsibleSidebar.tsx | head -100

Repository: ucdavis/PolicyWonk

Length of output: 3877


Guard modifier-key clicks and normalize the path comparison.

Right now a same-path click always calls preventDefault, which blocks Cmd/Ctrl‑click (new tab) and middle‑click behavior per Next.js best practices. Also, with trailingSlash enabled in next.config.js, /chat/new/ won't equal /chat/new, so the forced reload won't run. A small guard + normalization keeps UX intact and makes the equality check resilient.

Suggested fix
-                onClick={(e) => {
-                  if (window.location.pathname === newChatUrl) {
-                    e.preventDefault();
-                    window.location.assign(newChatUrl);
-                  }
-                }}
+                onClick={(e) => {
+                  if (
+                    e.metaKey ||
+                    e.ctrlKey ||
+                    e.shiftKey ||
+                    e.altKey ||
+                    e.button !== 0
+                  ) {
+                    return;
+                  }
+                  const currentPath = window.location.pathname.replace(/\/$/, '');
+                  const targetPath = newChatUrl.replace(/\/$/, '');
+                  if (currentPath === targetPath) {
+                    e.preventDefault();
+                    window.location.assign(newChatUrl);
+                  }
+                }}
🤖 Prompt for AI Agents
In `@web/src/components/layout/collapsibleSidebar.tsx` around lines 69 - 74,
Update the onClick handler in collapsibleSidebar.tsx to allow modifier-key and
middle-click behavior and to normalize paths before comparing: first guard out
clicks that should open in a new tab/window (if e.metaKey || e.ctrlKey ||
e.shiftKey || e.altKey || e.button !== 0) and return without preventDefault,
then compare a normalized form of window.location.pathname and newChatUrl (e.g.,
strip trailing slashes with a small helper like normalize(path) =>
path.replace(/\/+$/, '') || '/') and only call e.preventDefault() and
window.location.assign(newChatUrl) when the normalized paths match.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@web/src/components/chat/main.tsx`:
- Around line 28-35: The effect uses aiState.group in its condition but
hardcodes "/ucdavis" in window.history.pushState and omits aiState.group from
the dependency array; update the pushState target to use the dynamic group (use
`${aiState.group}`) and add aiState.group to the effect dependencies so the
effect reacts to group changes; keep the existing router.refresh() and
aiState.id checks in the effect (refer to aiState.group, aiState.id,
aiState.messages, router.refresh, and window.history.pushState).

Comment on lines +28 to 35
pathname === `/${aiState.group}/chat/new` &&
aiState.id !== '' // id is set only once the chat has been saved to the db
) {
// reloads the sidebar, which repulls from the db now that the chat has been saved
router.refresh();
window.history.pushState({}, '', `/chat/${aiState.id}`);
window.history.pushState({}, '', `/ucdavis/chat/${aiState.id}`); // change to group
}
}, [aiState.messages, router, aiState.id, pathname]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Hardcoded group value and missing dependency will break navigation for non-ucdavis groups.

Two issues in this effect:

  1. Inconsistent group handling: Line 28 dynamically checks aiState.group, but line 33 hardcodes /ucdavis/ instead of using ${aiState.group}. This breaks navigation for any group other than "ucdavis".

  2. Missing dependency: aiState.group is used in the condition but not included in the dependency array, violating React's exhaustive-deps rule and potentially causing stale closure bugs.

🐛 Proposed fix
     if (
       // on first response from AI
       pathname === `/${aiState.group}/chat/new` &&
       aiState.id !== '' // id is set only once the chat has been saved to the db
     ) {
       // reloads the sidebar, which repulls from the db now that the chat has been saved
       router.refresh();
-      window.history.pushState({}, '', `/ucdavis/chat/${aiState.id}`); // change to group
+      window.history.pushState({}, '', `/${aiState.group}/chat/${aiState.id}`);
     }
-  }, [aiState.messages, router, aiState.id, pathname]);
+  }, [aiState.messages, router, aiState.id, aiState.group, pathname]);
📝 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
pathname === `/${aiState.group}/chat/new` &&
aiState.id !== '' // id is set only once the chat has been saved to the db
) {
// reloads the sidebar, which repulls from the db now that the chat has been saved
router.refresh();
window.history.pushState({}, '', `/chat/${aiState.id}`);
window.history.pushState({}, '', `/ucdavis/chat/${aiState.id}`); // change to group
}
}, [aiState.messages, router, aiState.id, pathname]);
pathname === `/${aiState.group}/chat/new` &&
aiState.id !== '' // id is set only once the chat has been saved to the db
) {
// reloads the sidebar, which repulls from the db now that the chat has been saved
router.refresh();
window.history.pushState({}, '', `/${aiState.group}/chat/${aiState.id}`);
}
}, [aiState.messages, router, aiState.id, aiState.group, pathname]);
🤖 Prompt for AI Agents
In `@web/src/components/chat/main.tsx` around lines 28 - 35, The effect uses
aiState.group in its condition but hardcodes "/ucdavis" in
window.history.pushState and omits aiState.group from the dependency array;
update the pushState target to use the dynamic group (use `${aiState.group}`)
and add aiState.group to the effect dependencies so the effect reacts to group
changes; keep the existing router.refresh() and aiState.id checks in the effect
(refer to aiState.group, aiState.id, aiState.messages, router.refresh, and
window.history.pushState).

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/components/chat/main.tsx`:
- Around line 32-33: The navigation uses router.push which adds a history entry
and causes a back-navigation redirect loop; change the call to router.replace
when canonicalizing the URL (the code that currently calls
router.push(`/${aiState.group}/chat/${aiState.id}`) inside the effect that
checks aiState.group and aiState.id) so the navigation doesn't create a new
history entry, preserving router.refresh() behavior if still needed.

---

Duplicate comments:
In `@web/src/components/chat/main.tsx`:
- Line 35: The useEffect callback reads aiState.group but the dependency array
currently lacks it (only has aiState.messages, router, aiState.id, pathname),
creating a stale-closure and violating exhaustive-deps; update the dependency
array for the effect (the useEffect that references aiState.messages, router,
aiState.id, pathname) to include aiState.group so the effect re-runs when the
group changes.

Comment on lines 32 to 33
router.push(`/${aiState.group}/chat/${aiState.id}`);
router.refresh();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use router.replace instead of router.push to avoid a redirect loop on back-navigation.

router.push adds a new entry to the browser's history stack, so pressing Back returns the user to /${aiState.group}/chat/new. The effect condition will match again (since aiState.id is still set), triggering another router.push — creating an infinite redirect cycle between /chat/new and /chat/{id}.

router.replace performs the same navigation but without a new history entry, which is the correct semantic for this URL-canonicalization use case.

🐛 Proposed fix
-      router.push(`/${aiState.group}/chat/${aiState.id}`);
+      router.replace(`/${aiState.group}/chat/${aiState.id}`);
       router.refresh();
📝 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
router.push(`/${aiState.group}/chat/${aiState.id}`);
router.refresh();
router.replace(`/${aiState.group}/chat/${aiState.id}`);
router.refresh();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/chat/main.tsx` around lines 32 - 33, The navigation uses
router.push which adds a history entry and causes a back-navigation redirect
loop; change the call to router.replace when canonicalizing the URL (the code
that currently calls router.push(`/${aiState.group}/chat/${aiState.id}`) inside
the effect that checks aiState.group and aiState.id) so the navigation doesn't
create a new history entry, preserving router.refresh() behavior if still
needed.

@aristachauhan aristachauhan merged commit dff63b8 into main Feb 19, 2026
8 checks passed
@aristachauhan aristachauhan deleted the abc/sidebar-button-fix branch February 19, 2026 00:32
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