From c0511f3628b8af068d192497d783bf34227e5f00 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Wed, 14 Jan 2026 09:22:37 +1000 Subject: [PATCH] feat: add env vars tab and URL-based tab linking - Move environment variables to dedicated tab on agent and collection pages - Add URL query param support for deep linking to tabs (?tab=env-vars) - Remove env vars from settings tab on both pages --- .../src/app/dashboard/agents/[id]/page.tsx | 1008 ++++++++--------- .../app/dashboard/collections/[id]/page.tsx | 626 ++++++---- 2 files changed, 870 insertions(+), 764 deletions(-) diff --git a/apps/web/src/app/dashboard/agents/[id]/page.tsx b/apps/web/src/app/dashboard/agents/[id]/page.tsx index cbf8bc2..ce641c8 100644 --- a/apps/web/src/app/dashboard/agents/[id]/page.tsx +++ b/apps/web/src/app/dashboard/agents/[id]/page.tsx @@ -3,6 +3,7 @@ import type { AIProvider } from '@tpmjs/types/agent'; import { PROVIDER_MODELS, SUPPORTED_PROVIDERS } from '@tpmjs/types/agent'; import type { ExecutorConfig } from '@tpmjs/types/executor'; +import { Badge } from '@tpmjs/ui/Badge/Badge'; import { Button } from '@tpmjs/ui/Button/Button'; import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock'; import { Icon } from '@tpmjs/ui/Icon/Icon'; @@ -18,12 +19,16 @@ import { } from '@tpmjs/ui/Table/Table'; import { Tabs } from '@tpmjs/ui/Tabs/Tabs'; import Link from 'next/link'; -import { useParams, useRouter } from 'next/navigation'; +import { useParams, useRouter, useSearchParams } from 'next/navigation'; import { useCallback, useEffect, useRef, useState } from 'react'; import { DashboardLayout } from '~/components/dashboard/DashboardLayout'; import { EnvVarsEditor } from '~/components/EnvVarsEditor'; import { ExecutorConfigPanel } from '~/components/ExecutorConfigPanel'; +type PageTabId = 'tools' | 'collections' | 'api' | 'env-vars' | 'settings'; + +const VALID_TABS: PageTabId[] = ['tools', 'collections', 'api', 'env-vars', 'settings']; + interface Agent { id: string; uid: string; @@ -285,14 +290,35 @@ conv = resp.json() # conv['data']['messages']`, export default function AgentDetailPage(): React.ReactElement { const params = useParams(); const router = useRouter(); + const searchParams = useSearchParams(); const agentId = params.id as string; + // Get initial tab from URL or default to 'tools' + const tabFromUrl = searchParams.get('tab') as PageTabId | null; + const initialTab = tabFromUrl && VALID_TABS.includes(tabFromUrl) ? tabFromUrl : 'tools'; + const [agent, setAgent] = useState(null); const [isLoading, setIsLoading] = useState(true); - const [isEditing, setIsEditing] = useState(false); const [isSaving, setIsSaving] = useState(false); const [error, setError] = useState(null); const [executorConfig, setExecutorConfig] = useState(null); + const [activeTab, setActiveTab] = useState(initialTab); + + // Update URL when tab changes + const handleTabChange = (tabId: string) => { + const newTab = tabId as PageTabId; + setActiveTab(newTab); + const newParams = new URLSearchParams(searchParams.toString()); + if (newTab === 'tools') { + newParams.delete('tab'); + } else { + newParams.set('tab', newTab); + } + const queryString = newParams.toString(); + router.replace(`/dashboard/agents/${agentId}${queryString ? `?${queryString}` : ''}`, { + scroll: false, + }); + }; // Tools state const [agentTools, setAgentTools] = useState([]); @@ -665,7 +691,6 @@ export default function AgentDetailPage(): React.ReactElement { if (result.success) { setAgent(result.data); - setIsEditing(false); } else { throw new Error(result.error || 'Failed to update agent'); } @@ -728,6 +753,16 @@ export default function AgentDetailPage(): React.ReactElement { ); } + const envVarsCount = envVars ? Object.keys(envVars).length : 0; + + const pageTabs = [ + { id: 'tools' as const, label: 'Tools', count: agentTools.length }, + { id: 'collections' as const, label: 'Collections', count: agentCollections.length }, + { id: 'api' as const, label: 'API' }, + { id: 'env-vars' as const, label: 'Env Vars', count: envVarsCount > 0 ? envVarsCount : undefined }, + { id: 'settings' as const, label: 'Settings' }, + ]; + return ( } > - {/* Configuration */} -
-
-

Configuration

- {!isEditing && ( - - )} -
+ {/* Status badges */} +
+ + {agent.isPublic ? 'Public' : 'Private'} + + {executorConfig?.type === 'custom_url' && Custom Executor} +
- {isEditing ? ( -
-
-
- - -
-
- - -
-
+ {/* Tabs */} + -
- -