feat: refactor sidebar and settings to include agents and models; update routing and i18n messages
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { Book, Bot, Clock, Code, Cpu, House, Network, Puzzle, Settings } from 'lucide-react';
|
||||
import { Book, Clock, Code, House, Network, Puzzle, Settings } from 'lucide-react';
|
||||
import { useI18n } from '../../i18n';
|
||||
import { NAV_ITEMS, normalizeWorkspacePath } from '../../router/routes';
|
||||
|
||||
@@ -9,8 +9,6 @@ const MENU_MARKS: Record<string, typeof House> = {
|
||||
'/home': House,
|
||||
'/knowledge': Book,
|
||||
'/channels': Network,
|
||||
'/agents': Bot,
|
||||
'/models': Cpu,
|
||||
'/skills': Puzzle,
|
||||
'/cron': Clock,
|
||||
// '/scripts': Code,
|
||||
|
||||
@@ -416,6 +416,8 @@ export const messages: I18nMessages = {
|
||||
systemSettings: 'System Settings',
|
||||
account: 'Account',
|
||||
general: 'General',
|
||||
agents: 'Agents',
|
||||
models: 'Models',
|
||||
},
|
||||
account: {
|
||||
title: 'Account Settings',
|
||||
@@ -892,6 +894,8 @@ export const messages: I18nMessages = {
|
||||
systemSettings: '系统设置',
|
||||
account: '账号',
|
||||
general: '通用',
|
||||
agents: 'Agents',
|
||||
models: 'Models',
|
||||
},
|
||||
account: {
|
||||
title: '账号设置',
|
||||
@@ -1368,6 +1372,8 @@ export const messages: I18nMessages = {
|
||||
systemSettings: 'システム設定',
|
||||
account: 'アカウント',
|
||||
general: '一般',
|
||||
agents: 'Agents',
|
||||
models: 'Models',
|
||||
},
|
||||
account: {
|
||||
title: 'アカウント設定',
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { useI18n } from '../../../i18n';
|
||||
import { Settings, User } from 'lucide-react';
|
||||
import { Bot, Cpu, Settings, User } from 'lucide-react';
|
||||
|
||||
export type SettingView = 'account' | 'general';
|
||||
const SETTING_VIEWS = ['general', 'account', 'models', 'agents'] as const;
|
||||
|
||||
export type SettingView = (typeof SETTING_VIEWS)[number];
|
||||
|
||||
export function isSettingView(value: string | null): value is SettingView {
|
||||
return value !== null && SETTING_VIEWS.includes(value as SettingView);
|
||||
}
|
||||
|
||||
type SettingMenuProps = {
|
||||
currentView: SettingView;
|
||||
@@ -10,8 +16,12 @@ type SettingMenuProps = {
|
||||
|
||||
const MENU_ITEMS: Array<{
|
||||
id: SettingView;
|
||||
labelKey: 'settings.menu.account' | 'settings.menu.general';
|
||||
Icon: typeof User | typeof Settings;
|
||||
labelKey:
|
||||
| 'settings.menu.account'
|
||||
| 'settings.menu.general'
|
||||
| 'settings.menu.models'
|
||||
| 'settings.menu.agents';
|
||||
Icon: typeof User | typeof Settings | typeof Cpu | typeof Bot;
|
||||
}> = [
|
||||
{
|
||||
id: 'general',
|
||||
@@ -23,6 +33,16 @@ const MENU_ITEMS: Array<{
|
||||
labelKey: 'settings.menu.account',
|
||||
Icon: User,
|
||||
},
|
||||
{
|
||||
id: 'models',
|
||||
labelKey: 'settings.menu.models',
|
||||
Icon: Cpu,
|
||||
},
|
||||
{
|
||||
id: 'agents',
|
||||
labelKey: 'settings.menu.agents',
|
||||
Icon: Bot,
|
||||
},
|
||||
];
|
||||
|
||||
export default function SettingMenu({ currentView, onChange }: SettingMenuProps) {
|
||||
|
||||
@@ -1,18 +1,41 @@
|
||||
import { useState } from 'react';
|
||||
import { Suspense, lazy, useEffect } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useI18n } from '../../i18n';
|
||||
import { updateLanguage, updateThemeMode, useSettingsStore } from '../../stores';
|
||||
import type { LanguageCode, ThemeMode } from '../../types/runtime';
|
||||
import AccountSettingsPanel from './components/AccountSettingsPanel';
|
||||
import GeneralSettingsPanel from './components/GeneralSettingsPanel';
|
||||
import SettingMenu, { type SettingView } from './components/SettingMenu';
|
||||
import SettingMenu, { isSettingView, type SettingView } from './components/SettingMenu';
|
||||
import { useGatewaySettingState } from './useGatewaySettingState';
|
||||
import { useSettingUpdateState } from './useSettingUpdateState';
|
||||
|
||||
const AgentsPage = lazy(() => import('../Agents'));
|
||||
const ModelsPage = lazy(() => import('../Models'));
|
||||
|
||||
export default function SettingPage() {
|
||||
const [currentView, setCurrentView] = useState<SettingView>('general');
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useI18n();
|
||||
const themeMode = useSettingsStore((state) => state.themeMode);
|
||||
const language = useSettingsStore((state) => state.language);
|
||||
const updateState = useSettingUpdateState();
|
||||
const gatewayState = useGatewaySettingState();
|
||||
const rawView = new URLSearchParams(location.search).get('view');
|
||||
const currentView: SettingView = isSettingView(rawView) ? rawView : 'general';
|
||||
|
||||
useEffect(() => {
|
||||
if (isSettingView(rawView)) return;
|
||||
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
searchParams.set('view', 'general');
|
||||
navigate(
|
||||
{
|
||||
pathname: location.pathname,
|
||||
search: `?${searchParams.toString()}`,
|
||||
},
|
||||
{ replace: true },
|
||||
);
|
||||
}, [location.pathname, location.search, navigate, rawView]);
|
||||
|
||||
const handleThemeChange = async (nextTheme: ThemeMode) => {
|
||||
await updateThemeMode(nextTheme);
|
||||
@@ -22,24 +45,69 @@ export default function SettingPage() {
|
||||
await updateLanguage(nextLanguage);
|
||||
};
|
||||
|
||||
const handleViewChange = (nextView: SettingView) => {
|
||||
if (nextView === currentView) return;
|
||||
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
searchParams.set('view', nextView);
|
||||
navigate({
|
||||
pathname: location.pathname,
|
||||
search: `?${searchParams.toString()}`,
|
||||
});
|
||||
};
|
||||
|
||||
const loadingFallback = (
|
||||
<div className="flex h-full min-h-0 w-full items-center justify-center bg-white text-sm text-[#99A0AE] dark:bg-[#1b1b1d] dark:text-gray-500">
|
||||
{t('common.loading')}
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderContent = () => {
|
||||
switch (currentView) {
|
||||
case 'account':
|
||||
return (
|
||||
<div className="flex-1 min-w-0 min-h-0 overflow-y-auto">
|
||||
<AccountSettingsPanel />
|
||||
</div>
|
||||
);
|
||||
case 'models':
|
||||
return (
|
||||
<div className="flex-1 min-w-0 min-h-0 overflow-hidden">
|
||||
<Suspense fallback={loadingFallback}>
|
||||
<ModelsPage />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
case 'agents':
|
||||
return (
|
||||
<div className="flex-1 min-w-0 min-h-0 overflow-hidden">
|
||||
<Suspense fallback={loadingFallback}>
|
||||
<AgentsPage />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
case 'general':
|
||||
default:
|
||||
return (
|
||||
<div className="flex-1 min-w-0 min-h-0 overflow-y-auto">
|
||||
<GeneralSettingsPanel
|
||||
themeMode={themeMode}
|
||||
language={language}
|
||||
onThemeChange={handleThemeChange}
|
||||
onLanguageChange={handleLanguageChange}
|
||||
gatewayState={gatewayState}
|
||||
updateState={updateState}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-[#1b1b1d] box-border w-full h-full min-w-0 min-h-0 flex rounded-2xl overflow-hidden">
|
||||
<SettingMenu currentView={currentView} onChange={setCurrentView} />
|
||||
<SettingMenu currentView={currentView} onChange={handleViewChange} />
|
||||
|
||||
<div className="flex-1 min-w-0 min-h-0 overflow-y-auto">
|
||||
{currentView === 'account' ? (
|
||||
<AccountSettingsPanel />
|
||||
) : (
|
||||
<GeneralSettingsPanel
|
||||
themeMode={themeMode}
|
||||
language={language}
|
||||
onThemeChange={handleThemeChange}
|
||||
onLanguageChange={handleLanguageChange}
|
||||
gatewayState={gatewayState}
|
||||
updateState={updateState}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{renderContent()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import { onAuthLogout } from './auth-session';
|
||||
|
||||
const HomePage = lazy(() => import('../pages/Home'));
|
||||
const ChannelsPage = lazy(() => import('../pages/Channels'));
|
||||
const AgentsPage = lazy(() => import('../pages/Agents'));
|
||||
const ModelsPage = lazy(() => import('../pages/Models'));
|
||||
const SkillsPage = lazy(() => import('../pages/Skills'));
|
||||
const CronPage = lazy(() => import('../pages/Cron'));
|
||||
const ScriptsPage = lazy(() => import('../pages/Scripts'));
|
||||
@@ -32,6 +30,10 @@ function renderLazyPage(element: ReactNode) {
|
||||
);
|
||||
}
|
||||
|
||||
function renderSettingViewRedirect(view: 'agents' | 'models') {
|
||||
return <Navigate to={`/setting?view=${view}`} replace />;
|
||||
}
|
||||
|
||||
function AuthLogoutRedirector() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
@@ -65,8 +67,8 @@ export function AppRouter() {
|
||||
<Route element={<MainLayout />}>
|
||||
<Route path="/home" element={renderLazyPage(<HomePage />)} />
|
||||
<Route path="/channels" element={renderLazyPage(<ChannelsPage />)} />
|
||||
<Route path="/agents" element={renderLazyPage(<AgentsPage />)} />
|
||||
<Route path="/models" element={renderLazyPage(<ModelsPage />)} />
|
||||
<Route path="/agents" element={renderSettingViewRedirect('agents')} />
|
||||
<Route path="/models" element={renderSettingViewRedirect('models')} />
|
||||
<Route path="/skills" element={renderLazyPage(<SkillsPage />)} />
|
||||
<Route path="/cron" element={renderLazyPage(<CronPage />)} />
|
||||
<Route path="/scripts" element={renderLazyPage(<ScriptsPage />)} />
|
||||
|
||||
@@ -32,8 +32,6 @@ export const NAV_ITEMS: NavItem[] = [
|
||||
{ path: '/home', labelKey: 'sidebar.home' },
|
||||
{ path: '/knowledge', labelKey: 'sidebar.knowledge' },
|
||||
{ path: '/channels', labelKey: 'sidebar.channels' },
|
||||
{ path: '/agents', labelKey: 'sidebar.agents' },
|
||||
{ path: '/models', labelKey: 'sidebar.models' },
|
||||
{ path: '/skills', labelKey: 'sidebar.skills' },
|
||||
{ path: '/cron', labelKey: 'sidebar.cron' },
|
||||
// { path: '/scripts', labelKey: 'sidebar.scripts' },
|
||||
@@ -44,13 +42,14 @@ export function normalizeWorkspacePath(pathname: string): WorkspacePath {
|
||||
switch (pathname) {
|
||||
case '/knowledge':
|
||||
case '/channels':
|
||||
case '/agents':
|
||||
case '/models':
|
||||
case '/skills':
|
||||
case '/cron':
|
||||
case '/scripts':
|
||||
case '/setting':
|
||||
return pathname;
|
||||
case '/agents':
|
||||
case '/models':
|
||||
return '/setting';
|
||||
case '/home':
|
||||
default:
|
||||
return DEFAULT_PATH;
|
||||
|
||||
Reference in New Issue
Block a user