{navItems.find(n => n.id === activeTab)?.label}
{navItems.find(n => n.id === activeTab)?.description}
import { useCallback, useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import {
LayoutDashboard,
Upload,
Cloud,
Settings as SettingsIcon,
LogOut,
Menu,
X,
Server,
Users,
Globe
} from 'lucide-react'
import clsx from 'clsx'
import AccountManagerContainer from '../features/account/AccountManagerContainer'
import ApiTesterContainer from '../features/apiTester/ApiTesterContainer'
import BatchImport from '../components/BatchImport'
import VercelSyncContainer from '../features/vercel/VercelSyncContainer'
import SettingsContainer from '../features/settings/SettingsContainer'
import ProxyManagerContainer from '../features/proxy/ProxyManagerContainer'
import LanguageToggle from '../components/LanguageToggle'
import { useI18n } from '../i18n'
export default function DashboardShell({ token, onLogout, config, fetchConfig, showMessage, message, onForceLogout, isVercel }) {
const { t } = useI18n()
const location = useLocation()
const navigate = useNavigate()
const [sidebarOpen, setSidebarOpen] = useState(false)
const navItems = [
{ id: 'accounts', label: t('nav.accounts.label'), icon: Users, description: t('nav.accounts.desc') },
{ id: 'proxies', label: t('nav.proxies.label'), icon: Globe, description: t('nav.proxies.desc') },
{ id: 'test', label: t('nav.test.label'), icon: Server, description: t('nav.test.desc') },
{ id: 'import', label: t('nav.import.label'), icon: Upload, description: t('nav.import.desc') },
{ id: 'vercel', label: t('nav.vercel.label'), icon: Cloud, description: t('nav.vercel.desc') },
{ id: 'settings', label: t('nav.settings.label'), icon: SettingsIcon, description: t('nav.settings.desc') },
]
const tabIds = new Set(navItems.map(item => item.id))
const pathSegments = location.pathname.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean)
const routeSegments = pathSegments[0] === 'admin' ? pathSegments.slice(1) : pathSegments
const pathTab = routeSegments[0] || ''
const activeTab = tabIds.has(pathTab) ? pathTab : 'accounts'
const adminBasePath = pathSegments[0] === 'admin' ? '/admin' : ''
const navigateToTab = useCallback((tabID) => {
const nextPath = tabID === 'accounts'
? `${adminBasePath || ''}/`
: `${adminBasePath}/${tabID}`
navigate(nextPath)
setSidebarOpen(false)
}, [adminBasePath, navigate])
const authFetch = useCallback(async (url, options = {}) => {
const headers = {
...options.headers,
'Authorization': `Bearer ${token}`
}
const res = await fetch(url, { ...options, headers })
if (res.status === 401) {
onLogout()
throw new Error(t('auth.expired'))
}
return res
}, [onLogout, t, token])
const [versionInfo, setVersionInfo] = useState(null)
useEffect(() => {
let disposed = false
async function loadVersion() {
try {
const res = await authFetch('/admin/version')
const data = await res.json()
if (!disposed) {
setVersionInfo(data)
}
} catch (_err) {
if (!disposed) {
setVersionInfo(null)
}
}
}
loadVersion()
return () => {
disposed = true
}
}, [authFetch])
const renderTab = () => {
switch (activeTab) {
case 'accounts':
return
{navItems.find(n => n.id === activeTab)?.description}