Skip to content

🐛 Fixed staff profile URL not updating after changing a user's slug#29041

Merged
sagzy merged 1 commit into
mainfrom
fix-staff-slug-url-forwarding
Jul 2, 2026
Merged

🐛 Fixed staff profile URL not updating after changing a user's slug#29041
sagzy merged 1 commit into
mainfrom
fix-staff-slug-url-forwarding

Conversation

@sagzy

@sagzy sagzy commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

ref https://linear.app/ghost/issue/ONC-1873

Problem

A customer reported that the admin URL for a staff user's profile used to follow slug changes automatically, but no longer does.

The old Ember staff screen rewrote the browser URL via replaceState whenever a user's slug was saved, so refresh, back button and bookmarks kept working after a rename. That behaviour was lost when staff management moved to the React settings app (#18740):

  • After saving a slug change, the address bar stayed on #/settings/staff/old-slug — a dead URL on the next refresh, which rendered a silent blank (the modal component returns null when the slug lookup fails).
  • Changing your own slug was worse: the modal decides how to load data by comparing currentUser.slug against the URL param. Saving updates the shared current-user cache immediately, the comparison flips, the fetch by the (now stale) URL slug finds nothing, and the profile modal silently unmounts right after hitting Save.

Solution

  • Added a replace option to the legacy routing provider's updateRoute, implemented with history.replaceState + a synthetic hashchange, mirroring the old Ember behaviour so a slug rename swaps the history entry instead of pushing a dead one.
  • After a successful save, the user detail modal updates the route to the server-returned slug (the API may sanitize the submitted value), preserving the active tab.
  • The modal keeps showing the last loaded user while the refetch by the new slug is in flight, so it no longer unmounts mid-save.
  • Opening a stale/unknown staff slug URL now shows a "User not found" toast and navigates back to the staff list instead of rendering nothing.

Testing

  • Added three acceptance tests: slug change on another user updates the URL (incl. surviving a reload), slug change on your own profile keeps the modal open, and unknown slugs show an error and return to the staff list.
  • All 42 users/routing acceptance tests, framework unit tests (432) and lint pass locally.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

InternalLink now supports a replace flag, and RoutingProvider.updateRoute uses it to choose between history.replaceState plus a manual hashchange event or direct window.location.hash updates. UserDetailModal now keeps the last loaded user during refetches, derives a display user, updates the staff profile URL when a saved slug changes, and shows a one-time “User not found” toast with redirect behavior. Acceptance tests cover slug updates and missing-user navigation.

Sequence Diagram(s)

Diagrams are included in the hidden review stack artifact above.

Suggested reviewers: 9larsons

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: fixing staff profile URLs after a user's slug changes.
Description check ✅ Passed The description accurately matches the changeset, covering route replacement, slug syncing, missing-user handling, and tests.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-staff-slug-url-forwarding

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.

@nx-cloud

nx-cloud Bot commented Jul 2, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 699f3e0

Command Status Duration Result
nx run @tryghost/admin-x-settings:test:acceptance ✅ Succeeded 10m 10s View ↗
nx run-many --target=build --projects=tag:publi... ✅ Succeeded <1s View ↗
nx run-many -t test:unit -p @tryghost/admin-x-f... ✅ Succeeded 2m 39s View ↗
nx run @tryghost/activitypub:test:acceptance ✅ Succeeded 36s View ↗
nx run @tryghost/admin:build ✅ Succeeded 5s View ↗
nx run ghost:build:assets ✅ Succeeded 2s View ↗
nx run-many -t lint -p @tryghost/admin-x-framew... ✅ Succeeded 1s View ↗
nx run ghost:build:tsc ✅ Succeeded 6s View ↗
nx run ghost-admin:test ✅ Succeeded 1s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-07-02 14:11:24 UTC

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6128b2a7d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/admin-x-settings/src/components/settings/general/user-detail-modal.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/admin-x-settings/test/acceptance/general/users/profile.test.ts (1)

1014-1028: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the not-found path after a profile is already retained.

This starts from a cold unknown URL, so lastUserRef is empty. Add a case that opens a valid staff modal first, then navigates to an unknown/outdated slug and asserts the toast + staff-list redirect.

🤖 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 `@apps/admin-x-settings/test/acceptance/general/users/profile.test.ts` around
lines 1014 - 1028, The current test only covers a cold start with an unknown
slug, so it never exercises the retained-profile not-found path. Update the
acceptance test in the user profile suite to first open a valid staff member
through the existing profile/modal flow, then navigate to an unknown or outdated
slug and assert the “User not found” toast, redirect back to the staff list, and
that the detail modal closes; use the existing profile navigation helpers and
the user-detail-modal assertions to keep the scenario aligned with the retained
state behavior.
🤖 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 `@apps/admin-x-settings/src/components/settings/general/user-detail-modal.tsx`:
- Around line 516-518: The fallback in displayUser is letting
lastUserRef.current mask a later failed lookup, so notFoundSlug never becomes
truthy for an unknown or outdated slug. Update the logic in user-detail-modal’s
displayUser/notFoundSlug flow to only use the retained last user while the
current fetch is still pending, and stop falling back once fetchedUserData has
resolved with an error or a completed no-user result so the 404 redirect can
trigger.

---

Nitpick comments:
In `@apps/admin-x-settings/test/acceptance/general/users/profile.test.ts`:
- Around line 1014-1028: The current test only covers a cold start with an
unknown slug, so it never exercises the retained-profile not-found path. Update
the acceptance test in the user profile suite to first open a valid staff member
through the existing profile/modal flow, then navigate to an unknown or outdated
slug and assert the “User not found” toast, redirect back to the staff list, and
that the detail modal closes; use the existing profile navigation helpers and
the user-detail-modal assertions to keep the scenario aligned with the retained
state behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f63a41ac-8aae-43df-b71d-8d7339a20207

📥 Commits

Reviewing files that changed from the base of the PR and between 9e2f320 and d6128b2.

📒 Files selected for processing (3)
  • apps/admin-x-framework/src/providers/routing-provider.tsx
  • apps/admin-x-settings/src/components/settings/general/user-detail-modal.tsx
  • apps/admin-x-settings/test/acceptance/general/users/profile.test.ts

Comment thread apps/admin-x-settings/src/components/settings/general/user-detail-modal.tsx Outdated
@sagzy sagzy force-pushed the fix-staff-slug-url-forwarding branch from 929945b to 9bb273f Compare July 2, 2026 09:07
@sagzy sagzy added the preview Deploy a PR preview environment label Jul 2, 2026
@Ghost-Slimer Ghost-Slimer temporarily deployed to pr-preview-29041 July 2, 2026 09:57 Destroyed
ref https://linear.app/ghost/issue/ONC-1873

- the old Ember staff screen rewrote the browser URL (via replaceState)
  when a user's slug was saved, so refresh/back/bookmarks kept working;
  this behaviour was lost when staff management moved to the React
  settings app, leaving the address bar pointing at a dead slug
- changing your own slug was even worse: the modal compares the current
  user's slug against the URL param, so after saving, the profile modal
  silently unmounted
- added a `replace` option to the (legacy) routing provider's
  updateRoute so the modal can swap the history entry without adding a
  new one, mirroring the old Ember behaviour
- the user detail modal now updates the route to the server-returned
  slug after a save, keeps showing the last loaded user while the
  refetch by new slug is in flight, and shows a "User not found" toast
  (navigating back to the staff list) instead of rendering nothing when
  a stale/unknown slug URL is opened
@sagzy sagzy force-pushed the fix-staff-slug-url-forwarding branch from 95ff0ce to dc1284f Compare July 2, 2026 13:58
@sagzy sagzy enabled auto-merge (squash) July 2, 2026 14:06
@sagzy sagzy merged commit e22e6a8 into main Jul 2, 2026
42 checks passed
@sagzy sagzy deleted the fix-staff-slug-url-forwarding branch July 2, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Deploy a PR preview environment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants