Skip to content

Fix back navigation from My History tab to My Work tab#302

Merged
henrique221 merged 2 commits into
mainfrom
fix/back-nav-my-history-290
Jun 11, 2026
Merged

Fix back navigation from My History tab to My Work tab#302
henrique221 merged 2 commits into
mainfrom
fix/back-nav-my-history-290

Conversation

@henrique221

@henrique221 henrique221 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes back navigation for translators: pressing Back from the My History tab no longer dumps an authenticated user on the Login page — it returns to My Work, as expected.

Root cause

  1. The My Work / My History tabs lived only in the persisted zustand store, so switching tabs never created a browser history entry — Back always left the dashboard entirely.
  2. On successful login, window.location.href = returnTo pushed a new entry, leaving /login in the history stack directly beneath the dashboard — exactly where Back landed.
  3. The /login route had no auth guard, so the authenticated user was shown the login form.

Changes

  • Tab state moved into the URL (/?tab=my-history) via validateSearch on /_authenticated/; each tab switch is a real history entry, so Back from My History lands on My Work. The userDashboardTab store field is gone. Returning from a chapter via Back also restores the tab you left from, since it is part of the URL.
  • /login is now guarded: a beforeLoad redirect (for in-app navigation) plus a session-aware effect in LoginPage (for hard loads, mirroring AuthenticatedLayout) send authenticated visitors to a sanitized returnTo or the app home.
  • Login success uses window.location.replace(returnTo) so /login no longer lingers in the history stack.
  • returnTo values are sanitized to same-origin paths (no open redirect, never back into /login).

Screenshots

Screenshots live locally at /home/henrique/gitdocs/eten-tech-foundation/.playwright-mcp/pr-302-v1/ (not committed). Drag each PNG from that folder into the matching slot below — GitHub will replace the placeholder line with the uploaded image.

1) Login page (fresh session)

Unauthenticated visit to / redirects to /login?returnTo=%2F — unchanged for logged-out users.

01-login-page

2) My Work after login

Translator t@fluent.local lands on / with the My Work tab active.

02-my-work-after-login

3) My History tab is a real history entry

Clicking My History navigates to /?tab=my-history (note the Submitted Date column) — the tab switch is now part of browser history.

03-my-history-tab

4) Back from My History lands on My Work (the fix)

Pressing the browser Back button from My History returns to / with My Work active — not the Login page.

04-back-lands-on-my-work

5) Authenticated visit to /login bounces into the app

Navigating straight to /login with a live session (hard load) redirects to the dashboard instead of showing the login form.

05-login-redirects-to-app

Test plan

  • Unit: /login guard — loading no-op, unauthenticated pass-through, authenticated redirect, returnTo sanitization (external / // / /login loop targets)
  • Unit: UserHomePage — tab derived from URL search param, tab clicks navigate with ?tab=
  • Unit: LoginPagelocation.replace(returnTo) on success, authenticated visitor redirected away, no redirect while session loads
  • pnpm lint, pnpm format:check, pnpm typecheck, pnpm test (38 passing)
  • E2E (Playwright against the docker compose stack): login as translator → My History → Back → My Work; Back again does not reach /login; direct /login visit while authenticated bounces to the dashboard

Closes #290

Summary by CodeRabbit

  • Bug Fixes

    • Safer login redirects with validated, internal-only return destinations and corrected post-login navigation behavior.
    • Authenticated users are redirected appropriately and login redirects never target the login page itself.
  • New Features

    • Dashboard tabs now driven by the URL, enabling browser history, back/forward navigation, and shareable links.
    • Login route now guards authenticated access and respects validated return destinations.
  • Tests

    • Expanded tests for login redirects, route guards, and dashboard tab behavior.
    • Test environment: added a no-op ResizeObserver to stabilize UI tests.

Pressing Back from the My History tab sent authenticated translators to
the Login page. Three changes, one per root cause:

- Put the dashboard tab in the URL (/?tab=my-history) so each tab switch
  is a real history entry; Back from My History now lands on My Work.
  The tab no longer needs to live in the persisted zustand store.
- Guard /login: an authenticated user navigating there (e.g. via Back)
  is redirected to a sanitized returnTo or the app home. A session-aware
  effect in LoginPage covers hard loads, mirroring AuthenticatedLayout.
- Replace (not push) the history entry on successful login so /login no
  longer sits underneath the dashboard in the history stack.

returnTo values are now sanitized to same-origin paths to keep the login
flow from acting as an open redirect, and never point back at /login.

Fixes #290
@henrique221 henrique221 self-assigned this Jun 10, 2026
@henrique221

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

Copilot AI 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.

Pull request overview

This PR fixes an authenticated back-navigation trap where translators could land on /login (and see the login form) when navigating back from My History. It does this by making dashboard tab selection part of the URL (so tab switches create real browser history entries) and by guarding /login for authenticated users, including hard-load cases.

Changes:

  • Moved translator dashboard tab state into the home route’s URL search params (/?tab=my-history) via validateSearch on /_authenticated/, removing the persisted zustand userDashboardTab.
  • Added a /login beforeLoad redirect for authenticated users and added session-aware redirect logic in LoginPage for hard loads; sanitized returnTo to prevent open redirects and /login redirect loops.
  • Switched post-login navigation to window.location.replace(returnTo) to keep /login out of the browser history stack.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/test/setup.ts Adds a ResizeObserver stub for jsdom to support components that read it during tests.
src/store/store.ts Removes persisted userDashboardTab state now that tabs are URL-driven.
src/routes/login.tsx Guards /login with a beforeLoad redirect for authenticated users using a sanitized returnTo.
src/routes/_authenticated/index.tsx Defines tab as a validated optional search param so tab switches become history entries.
src/features/dashboard/user/UserHomePage.tsx Derives the active tab from useSearch and navigates by updating ?tab= rather than zustand state.
src/features/dashboard/user/UserHomePage.test.tsx Adds unit coverage for URL-driven tab selection and tab-click navigation.
src/features/auth/route-guards.test.ts Adds unit coverage for the new /login guard behavior and returnTo sanitization expectations.
src/features/auth/return-to.ts Introduces resolveReturnTo helper to sanitize returnTo targets and avoid redirect loops.
src/features/auth/LoginPage.tsx Redirects authenticated visitors away from /login after session resolution and uses location.replace on successful login.
src/features/auth/LoginPage.test.tsx Adds coverage for location.replace, returnTo sanitization, and session-based redirect behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@henrique221 henrique221 requested a review from kaseywright June 10, 2026 19:45
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 70262245-87b8-40f5-904c-2b099c1adc10

📥 Commits

Reviewing files that changed from the base of the PR and between 9d82088 and 445e057.

📒 Files selected for processing (1)
  • src/features/dashboard/user/UserHomePage.tsx

📝 Walkthrough

Walkthrough

The PR implements session-aware login route protection with safe returnTo validation, and migrates the user dashboard tab selection from Zustand app state to URL search parameters. This fixes back/forward browser navigation for tab switching and prevents authenticated users from being redirected to login on in-app back navigation.

Changes

Login Route Guards and Safe Redirect Handling

Layer / File(s) Summary
Safe returnTo path validation
src/features/auth/return-to.ts
resolveReturnTo helper validates and sanitizes optional returnTo strings, rejecting unsafe paths, external URLs, and redirect loops back to /login, defaulting to '/' for disallowed inputs.
Login route guard with authentication check
src/routes/login.tsx
Added beforeLoad hook to the /login route that prevents redirects during auth loading and redirects already-authenticated users to a safe destination computed from the validated search.returnTo parameter.
LoginPage component session guard and redirect
src/features/auth/LoginPage.tsx
LoginPage now derives returnTo via resolveReturnTo from router search params, adds a session-aware useEffect that hard-navigates authenticated users away, and updates post-login redirect to use window.location.replace instead of href assignment.
Login and route guard test coverage
src/features/auth/LoginPage.test.tsx, src/features/auth/route-guards.test.ts
Enhanced LoginPage tests with router and session state mocks; expanded route-guards tests covering /login route security (loading state pass-through, unauthenticated access, authenticated redirects with/without returnTo, redirect-loop prevention), and redirect behavior with unsafe/internal returnTo values.

Dashboard Tab State Migration to URL

Layer / File(s) Summary
Route search validation schema for tab parameter
src/routes/_authenticated/index.tsx
Authenticated route now validates optional tab search parameter restricted to 'my-work' or 'my-history' using Zod schema.
UserHomePage URL-based tab state management
src/features/dashboard/user/UserHomePage.tsx
Component refactored to derive tab state from URL search params via useSearch instead of app store, computes isHistory flag from URL state, updates filter reset logic dependency, and changes tab switching to navigate with URL parameters instead of store setters.
UserHomePage tab behavior test suite
src/features/dashboard/user/UserHomePage.test.tsx
New Vitest suite for UserHomePage mocking router hooks and chapter assignments, verifying default "My Work" tab rendering, history tab switching via URL params, and navigation behavior when clicking tab buttons.
Remove tab state from persistent store
src/store/store.ts
Removes userDashboardTab field and setUserDashboardTab setter from AppState interface, persisted store initialization, and storage partialize function, completing the migration to URL-based state.
Test environment ResizeObserver shim
src/test/setup.ts
Adds a global window.ResizeObserver no-op shim to the test setup to satisfy jsdom and components relying on ResizeObserver during tests.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A guard stops the logged-in folk
From visiting login—no joke!
Tabs now live in the URL's home,
Back button lets users safely roam,
No state lost, just clean forward flow. ✨

🚥 Pre-merge checks | ✅ 4
✅ 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 accurately describes the main fix: back navigation from My History tab now returns to My Work instead of Login.
Linked Issues check ✅ Passed All linked issue #290 requirements are met: tab state moved to URL with validateSearch [#290], login guard prevents auth redirect [#290], and location.replace preserves history [#290].
Out of Scope Changes check ✅ Passed All changes directly address the linked issue: URL-based tab state, login route guard, ReturnTo sanitization, authenticated redirect, UserHomePage URL integration, and related test coverage.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/back-nav-my-history-290

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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

@henrique221

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@henrique221 henrique221 merged commit 2e936d1 into main Jun 11, 2026
2 checks passed
@github-actions github-actions Bot deleted the fix/back-nav-my-history-290 branch June 11, 2026 22:07
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.

Fix Back Navigation from My History Tab to My Work Tab

3 participants