fix(ui): redirect agent detail page to new URL key after rename

Agent detail URLs use a name-derived key, so renaming an agent (or
rolling back a config revision that restores an old name) invalidated
the reference in the URL. The save handler then refetched the detail
query for the stale reference, which 404s and rendered "Agent not
found".

On save/rollback success, compare the previous and updated route refs;
when the key changed, drop the stale cached detail queries and replace
the URL with the new canonical reference instead of refetching the old
one.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Claude authored and Devin Foley committed 2026-07-10 08:14:43 -07:00
1 parent a4993a72a6
commit cba11ea168
1 file changed
+37 -6
+37 -6
View File
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useState, useRef } from "react";
import { useParams, useNavigate, Link, Navigate, useBeforeUnload } from "@/lib/router";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { useParams, useNavigate, Link, Navigate, useBeforeUnload, type NavigateFunction } from "@/lib/router";
import { useQuery, useMutation, useQueryClient, type QueryClient } from "@tanstack/react-query";
import {
agentsApi,
type AgentKey,
@@ -1638,6 +1638,29 @@ function CostsSection({
/* ---- Agent Configure Page ---- */
/**
* Agent detail URLs use a name-derived key, so updates that change the agent's
* name (a rename or a config-revision rollback) can invalidate the reference
* currently in the URL. When that happens, refetching the old reference would
* 404 with "Agent not found". Instead, drop the stale cached queries and
* replace the URL with the new canonical reference. Returns true when a
* redirect happened.
*/
function syncAgentRouteAfterRename(
queryClient: QueryClient,
navigate: NavigateFunction,
previous: { id: string; urlKey?: string | null; name?: string | null },
updated: { id: string; urlKey?: string | null; name?: string | null },
tab: string,
): boolean {
const previousRef = agentRouteRef(previous);
const nextRef = agentRouteRef(updated);
if (nextRef === previousRef) return false;
queryClient.removeQueries({ queryKey: queryKeys.agents.detail(previousRef) });
navigate(`/agents/${nextRef}/${tab}`, { replace: true });
return true;
}
function AgentConfigurePage({
agent,
agentId,
@@ -1658,6 +1681,8 @@ function AgentConfigurePage({
updatePermissions: { mutate: (permissions: AgentPermissionUpdate) => void; isPending: boolean };
}) {
const queryClient = useQueryClient();
const navigate = useNavigate();
const { tab: urlTab } = useParams<{ tab?: string }>();
const [revisionsOpen, setRevisionsOpen] = useState(false);
const { data: configRevisions } = useQuery({
@@ -1667,10 +1692,12 @@ function AgentConfigurePage({
const rollbackConfig = useMutation({
mutationFn: (revisionId: string) => agentsApi.rollbackConfigRevision(agent.id, revisionId, companyId),
onSuccess: () => {
onSuccess: (updated) => {
queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.id) });
queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.urlKey) });
queryClient.invalidateQueries({ queryKey: queryKeys.agents.configRevisions(agent.id) });
if (!syncAgentRouteAfterRename(queryClient, navigate, agent, updated, urlTab ?? "configuration")) {
queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.urlKey) });
}
},
});
@@ -1770,6 +1797,8 @@ function ConfigurationTab({
hideInstructionsFile?: boolean;
}) {
const queryClient = useQueryClient();
const navigate = useNavigate();
const { tab: urlTab } = useParams<{ tab?: string }>();
const { pushToast } = useToastActions();
const [awaitingRefreshAfterSave, setAwaitingRefreshAfterSave] = useState(false);
const lastAgentRef = useRef(agent);
@@ -1804,11 +1833,13 @@ function ConfigurationTab({
onMutate: () => {
setAwaitingRefreshAfterSave(true);
},
onSuccess: () => {
onSuccess: (updated) => {
queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.id) });
queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.urlKey) });
queryClient.invalidateQueries({ queryKey: queryKeys.agents.configRevisions(agent.id) });
queryClient.invalidateQueries({ queryKey: queryKeys.agents.list(agent.companyId) });
if (!syncAgentRouteAfterRename(queryClient, navigate, agent, updated, urlTab ?? "configuration")) {
queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.urlKey) });
}
pushToast({ title: "Agent saved", tone: "success" });
},
onError: (err) => {