refactor(frontend): change API Settings from sidebar to horizontal tabs layout

Refactor ApiSettingsPage to use horizontal Tabs component like CodexLens page,
replacing the previous vertical sidebar navigation.

Changes:
- Replace Card-based sidebar with horizontal TabsList/TabsTrigger components
- Remove split-panel layout (sidebar + main panel)
- Use TabsContent to wrap each tab's content
- Import and use Tabs, TabsList, TabsTrigger, TabsContent from ui/Tabs
- Add api-settings.json import to en/zh locale index files for i18n support

Layout comparison:
- Before: Vertical sidebar (lg:w-64) with Card + nav buttons
- After: Horizontal tabs (TabsList) with tab triggers
This commit is contained in:
catlog22
2026-02-02 10:49:21 +08:00
parent c522681c4c
commit 1beb98366b
3 changed files with 59 additions and 98 deletions

View File

@@ -24,6 +24,7 @@ import cliManager from './cli-manager.json';
import cliMonitor from './cli-monitor.json'; import cliMonitor from './cli-monitor.json';
import mcpManager from './mcp-manager.json'; import mcpManager from './mcp-manager.json';
import codexlens from './codexlens.json'; import codexlens from './codexlens.json';
import apiSettings from './api-settings.json';
import theme from './theme.json'; import theme from './theme.json';
import executionMonitor from './execution-monitor.json'; import executionMonitor from './execution-monitor.json';
import cliHooks from './cli-hooks.json'; import cliHooks from './cli-hooks.json';
@@ -82,6 +83,7 @@ export default {
...flattenMessages(cliMonitor, 'cliMonitor'), ...flattenMessages(cliMonitor, 'cliMonitor'),
...flattenMessages(mcpManager, 'mcp'), ...flattenMessages(mcpManager, 'mcp'),
...flattenMessages(codexlens, 'codexlens'), ...flattenMessages(codexlens, 'codexlens'),
...flattenMessages(apiSettings, 'apiSettings'),
...flattenMessages(theme, 'theme'), ...flattenMessages(theme, 'theme'),
...flattenMessages(cliHooks, 'cliHooks'), ...flattenMessages(cliHooks, 'cliHooks'),
...flattenMessages(executionMonitor, 'executionMonitor'), ...flattenMessages(executionMonitor, 'executionMonitor'),

View File

@@ -24,6 +24,7 @@ import cliManager from './cli-manager.json';
import cliMonitor from './cli-monitor.json'; import cliMonitor from './cli-monitor.json';
import mcpManager from './mcp-manager.json'; import mcpManager from './mcp-manager.json';
import codexlens from './codexlens.json'; import codexlens from './codexlens.json';
import apiSettings from './api-settings.json';
import theme from './theme.json'; import theme from './theme.json';
import executionMonitor from './execution-monitor.json'; import executionMonitor from './execution-monitor.json';
import cliHooks from './cli-hooks.json'; import cliHooks from './cli-hooks.json';
@@ -82,6 +83,7 @@ export default {
...flattenMessages(cliMonitor, 'cliMonitor'), ...flattenMessages(cliMonitor, 'cliMonitor'),
...flattenMessages(mcpManager, 'mcp'), ...flattenMessages(mcpManager, 'mcp'),
...flattenMessages(codexlens, 'codexlens'), ...flattenMessages(codexlens, 'codexlens'),
...flattenMessages(apiSettings, 'apiSettings'),
...flattenMessages(theme, 'theme'), ...flattenMessages(theme, 'theme'),
...flattenMessages(cliHooks, 'cliHooks'), ...flattenMessages(cliHooks, 'cliHooks'),
...flattenMessages(executionMonitor, 'executionMonitor'), ...flattenMessages(executionMonitor, 'executionMonitor'),

View File

@@ -7,14 +7,10 @@ import { useState, useMemo } from 'react';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { import {
Server, Server,
Link,
Database,
Layers,
Settings as SettingsIcon,
RefreshCw, RefreshCw,
} from 'lucide-react'; } from 'lucide-react';
import { Card } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/Tabs';
import { import {
ProviderList, ProviderList,
ProviderModal, ProviderModal,
@@ -30,20 +26,10 @@ import {
} from '@/components/api-settings'; } from '@/components/api-settings';
import { useProviders, useEndpoints, useModelPools, useCliSettings } from '@/hooks/useApiSettings'; import { useProviders, useEndpoints, useModelPools, useCliSettings } from '@/hooks/useApiSettings';
import { useNotifications } from '@/hooks/useNotifications'; import { useNotifications } from '@/hooks/useNotifications';
import { cn } from '@/lib/utils';
// Tab type definitions // Tab type definitions
type TabType = 'providers' | 'endpoints' | 'cache' | 'modelPools' | 'cliSettings'; type TabType = 'providers' | 'endpoints' | 'cache' | 'modelPools' | 'cliSettings';
// Tab configuration
const TABS: { value: TabType; icon: React.ElementType }[] = [
{ value: 'providers', icon: Server },
{ value: 'endpoints', icon: Link },
{ value: 'cache', icon: Database },
{ value: 'modelPools', icon: Layers },
{ value: 'cliSettings', icon: SettingsIcon },
];
export function ApiSettingsPage() { export function ApiSettingsPage() {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const { showNotification } = useNotifications(); const { showNotification } = useNotifications();
@@ -192,52 +178,6 @@ export function ApiSettingsPage() {
} }
}; };
// Render the active tab's main content
const renderMainContent = () => {
switch (activeTab) {
case 'providers':
return (
<ProviderList
onAddProvider={handleAddProvider}
onEditProvider={handleEditProvider}
onMultiKeySettings={handleMultiKeySettings}
onSyncToCodexLens={handleSyncToCodexLens}
onManageModels={handleManageModels}
/>
);
case 'endpoints':
return (
<EndpointList
onAddEndpoint={handleAddEndpoint}
onEditEndpoint={handleEditEndpoint}
/>
);
case 'cache':
return <CacheSettings />;
case 'modelPools':
return (
<ModelPoolList
onAddPool={handleAddPool}
onEditPool={handleEditPool}
/>
);
case 'cliSettings':
return (
<CliSettingsList
onAddCliSettings={handleAddCliSettings}
onEditCliSettings={handleEditCliSettings}
/>
);
default:
return null;
}
};
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* Page Header */} {/* Page Header */}
@@ -257,44 +197,61 @@ export function ApiSettingsPage() {
</Button> </Button>
</div> </div>
{/* Split Panel Layout */} {/* Tabbed Interface */}
<div className="flex flex-col lg:flex-row gap-6"> <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as TabType)}>
{/* Left Sidebar - Tabs */} <TabsList>
<aside className="w-full lg:w-64 flex-shrink-0"> <TabsTrigger value="providers">
<Card className="p-2"> {formatMessage({ id: 'apiSettings.tabs.providers' })}
<nav className="space-y-1" aria-label="API Settings tabs"> </TabsTrigger>
{TABS.map((tab) => { <TabsTrigger value="endpoints">
const Icon = tab.icon; {formatMessage({ id: 'apiSettings.tabs.endpoints' })}
const isActive = activeTab === tab.value; </TabsTrigger>
return ( <TabsTrigger value="cache">
<button {formatMessage({ id: 'apiSettings.tabs.cache' })}
key={tab.value} </TabsTrigger>
onClick={() => setActiveTab(tab.value)} <TabsTrigger value="modelPools">
className={cn( {formatMessage({ id: 'apiSettings.tabs.modelPools' })}
'w-full flex items-center gap-3 px-3 py-2.5 rounded-md text-sm transition-colors', </TabsTrigger>
'hover:bg-muted hover:text-foreground', <TabsTrigger value="cliSettings">
isActive {formatMessage({ id: 'apiSettings.tabs.cliSettings' })}
? 'bg-primary/10 text-primary font-medium' </TabsTrigger>
: 'text-muted-foreground' </TabsList>
)}
aria-current={isActive ? 'page' : undefined}
>
<Icon className="w-5 h-5 flex-shrink-0" />
<span className="flex-1 text-left">
{formatMessage({ id: `apiSettings.tabs.${tab.value}` })}
</span>
</button>
);
})}
</nav>
</Card>
</aside>
{/* Right Main Panel - Content */} <TabsContent value="providers">
<main className="flex-1 min-w-0"> <ProviderList
{renderMainContent()} onAddProvider={handleAddProvider}
</main> onEditProvider={handleEditProvider}
</div> onMultiKeySettings={handleMultiKeySettings}
onSyncToCodexLens={handleSyncToCodexLens}
onManageModels={handleManageModels}
/>
</TabsContent>
<TabsContent value="endpoints">
<EndpointList
onAddEndpoint={handleAddEndpoint}
onEditEndpoint={handleEditEndpoint}
/>
</TabsContent>
<TabsContent value="cache">
<CacheSettings />
</TabsContent>
<TabsContent value="modelPools">
<ModelPoolList
onAddPool={handleAddPool}
onEditPool={handleEditPool}
/>
</TabsContent>
<TabsContent value="cliSettings">
<CliSettingsList
onAddCliSettings={handleAddCliSettings}
onEditCliSettings={handleEditCliSettings}
/>
</TabsContent>
</Tabs>
{/* Modals */} {/* Modals */}
<ProviderModal <ProviderModal