Address review findings on the agents navigation

- Opening a folder dropped the active filter. agentsListPath() always
  built off the bare /agents/manage root, so from /agents/manage/mine it
  navigated to /agents/manage?folder=..., and filterFromPath() then read
  back `all`. A regression from making the filters routable: they used
  to be component state, which a replace-navigation left alone. The
  helper now takes the filter and builds off agentsFilterPath(), which
  also fixes the other half — switching filter while inside a folder
  re-navigates to the filtered path with the folder kept, instead of the
  sync effect pulling it back to the unfiltered URL.

- Both manage-agents handlers cleared the selected agent and closed the
  mobile nav before returning early for a modifier-click, so opening the
  list in a new tab mutated the tab you stayed on. The modifier check
  and preventDefault() now run first.

- The detail breadcrumbs had the name precedence inverted. Tools.tsx
  shows `customName || displayName` throughout, so a renamed tool kept
  its old name in the breadcrumb alone. ToolConfig now matches, and
  RemoteDeviceConfig leads with device?.name as its own heading does.
This commit is contained in:
Alex committed 2026-09-22 00:42:17 +01:00
1 parent 448de3c48a
commit f658515ca3
6 files changed
+36 -19

No files matched your search

+2 -2
View File
@@ -615,11 +615,11 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
to={AGENTS_MANAGE_ROOT}
end
onClick={(event) => {
dispatch(setSelectedAgent(null));
closeNavOnMobile();
if (event.metaKey || event.ctrlKey || event.shiftKey)
return;
event.preventDefault();
dispatch(setSelectedAgent(null));
closeNavOnMobile();
goToLevel(AGENTS_MANAGE_ROOT);
}}
className={({ isActive }) =>
+8 -9
View File
@@ -61,6 +61,10 @@ export default function AgentsList() {
const [showAgentTypeModal, setShowAgentTypeModal] = useState(false);
const [modalFolderId, setModalFolderId] = useState<string | null>(null);
// The list's filter is a route, so it is linkable and survives a reload;
// the sidebar nav (and the pill row below `lg`) does the navigating.
const activeFilter = filterFromPath(location.pathname);
// Sync folder path with URL
useEffect(() => {
const currentFolderInUrl = searchParams.get('folder');
@@ -68,19 +72,14 @@ export default function AgentsList() {
folderPath.length > 0 ? folderPath[folderPath.length - 1] : null;
if (currentFolderId !== currentFolderInUrl) {
const newUrl = currentFolderId
? agentsListPath(currentFolderId)
: agentsListPath();
navigate(newUrl, { replace: true });
navigate(agentsListPath(currentFolderId, activeFilter), {
replace: true,
});
}
}, [folderPath, searchParams, navigate]);
}, [folderPath, searchParams, navigate, activeFilter]);
const { isLoading, refetchFolders, refetchUserAgents } = useAgentsFetch();
// The list's filter is a route, so it is linkable and survives a reload;
// the sidebar nav (and the pill row below `lg`) does the navigating.
const activeFilter = filterFromPath(location.pathname);
const {
searchQuery,
setSearchQuery,
+8
View File
@@ -31,6 +31,14 @@ describe('agentsListPath', () => {
expect(agentsListPath('a b')).toBe('/agents/manage?folder=a%20b');
expect(agentsListPath(null)).toBe('/agents/manage');
});
it('keeps the active filter, so opening a folder does not widen the list', () => {
expect(agentsListPath('f1', 'user')).toBe('/agents/manage/mine?folder=f1');
expect(agentsListPath(null, 'user')).toBe('/agents/manage/mine');
expect(filterFromPath(agentsListPath('f1', 'team').split('?')[0])).toBe(
'team',
);
});
});
describe('agentEditPathFor', () => {
+14 -6
View File
@@ -32,18 +32,26 @@ const SLUG_FILTERS = Object.fromEntries(
export const isWorkflowAgent = (agent: Pick<Agent, 'agent_type'>): boolean =>
agent.agent_type === 'workflow';
/** The agent list, optionally scoped to a folder. */
export const agentsListPath = (folderId?: string | null): string =>
folderId
? `${AGENTS_MANAGE_ROOT}?folder=${encodeURIComponent(folderId)}`
: AGENTS_MANAGE_ROOT;
/** The list narrowed to one filter; each filter is its own linkable route. */
export const agentsFilterPath = (filter: AgentFilterTab): string =>
filter === 'all'
? AGENTS_MANAGE_ROOT
: `${AGENTS_MANAGE_ROOT}/${FILTER_SLUGS[filter]}`;
/**
* The agent list, optionally scoped to a folder and to a filter. The filter
* has to be carried explicitly: it lives in the path now, so building a
* folder URL off the bare root would silently widen the list back to every
* section the moment someone opened a folder from a filtered view.
*/
export const agentsListPath = (
folderId?: string | null,
filter: AgentFilterTab = 'all',
): string => {
const base = agentsFilterPath(filter);
return folderId ? `${base}?folder=${encodeURIComponent(folderId)}` : base;
};
/** Which filter a list route selects; `all` for anything unrecognised. */
export const filterFromPath = (pathname: string): AgentFilterTab => {
const slug = pathname.startsWith(`${AGENTS_MANAGE_ROOT}/`)
+3 -1
View File
@@ -197,7 +197,9 @@ export default function RemoteDeviceConfig({ tool, handleGoBack }: Props) {
<div className="mb-4 flex items-center justify-between gap-3">
<DetailBreadcrumb
parentLabel={t('settings.tools.label')}
currentLabel={tool.displayName || tool.customName || tool.name}
currentLabel={
device?.name || tool.customName || tool.displayName || tool.name
}
onParentClick={handleGoBack}
/>
<Button
+1 -1
View File
@@ -333,7 +333,7 @@ export default function ToolConfig({
<div className="mb-4 flex items-center justify-between gap-3">
<DetailBreadcrumb
parentLabel={t('settings.tools.label')}
currentLabel={tool.displayName || tool.customName || tool.name}
currentLabel={tool.customName || tool.displayName || tool.name}
onParentClick={handleBackClick}
/>
<Button