feat: merge services sidebar into panel and remove sidebar from services page

This commit is contained in:
Haitao Pan 2026-02-02 16:29:06 +08:00
parent 87424d069c
commit 60ec70f9ac
8 changed files with 173 additions and 8 deletions

View File

@ -4,21 +4,24 @@ import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useMemo, type ComponentType } from 'react'
import { Plus, type LucideIcon } from 'lucide-react'
import { getExtensionRegistry } from '@extensions/loader'
import { useLanguage } from '@i18n/LanguageProvider'
import { translations } from '@i18n/translations'
import { resolveAccess } from '@lib/accessControl'
import { useUserStore } from '@lib/userStore'
import { SidebarHeader, SidebarContent } from '../../../components/layout/SidebarRoot'
import { SidebarHeader, SidebarContent, SidebarFooter } from '../../../components/layout/SidebarRoot'
const registry = getExtensionRegistry()
const PlaceholderIcon: ComponentType<{ className?: string }> = () => null
interface NavItem {
id?: string
href: string
label: string
description: string
Icon: ComponentType<{ className?: string }>
Icon: ComponentType<{ className?: string }> | LucideIcon
disabled: boolean
}
@ -68,6 +71,7 @@ export function PanelSidebarContent({ onNavigate, collapsed = false }: PanelSide
const Icon = route.icon ?? PlaceholderIcon
return {
id: route.id,
href: route.path,
label: route.label,
description: route.description ?? '',
@ -75,7 +79,7 @@ export function PanelSidebarContent({ onNavigate, collapsed = false }: PanelSide
disabled,
}
})
.filter((value): value is NavItem => Boolean(value))
.filter((value) => Boolean(value)) as NavItem[]
if (items.length === 0) {
return null
@ -87,7 +91,7 @@ export function PanelSidebarContent({ onNavigate, collapsed = false }: PanelSide
items,
}
})
.filter((value): value is NavSection => Boolean(value))
.filter((value) => Boolean(value)) as NavSection[]
}, [requiresSetup, user])
return (
@ -193,7 +197,9 @@ export function PanelSidebarContent({ onNavigate, collapsed = false }: PanelSide
<Icon className="h-4 w-4" />
</span>
<span className={`flex flex-1 flex-col truncate transition-all duration-300 ${collapsed ? 'w-0 opacity-0 invisible overflow-hidden' : 'w-auto opacity-100 visible'}`}>
<span className="font-semibold text-left">{item.label}</span>
<span className="font-semibold text-left">
{(item.id && translations[language].userCenter.items[item.id as keyof typeof translations.en.userCenter.items]) || item.label}
</span>
<span className={`${descriptionClasses.join(' ')} text-left`}>{item.description}</span>
</span>
</div>
@ -218,6 +224,18 @@ export function PanelSidebarContent({ onNavigate, collapsed = false }: PanelSide
)
})}
</SidebarContent>
<SidebarFooter className="p-4 border-t border-[color:var(--color-surface-border)]">
<button
className={`group w-full flex items-center justify-center gap-2 py-3 px-4 bg-[var(--color-primary)] text-[var(--color-primary-foreground)] rounded-xl font-bold text-sm shadow-[var(--shadow-sm)] hover:opacity-90 transition-all duration-300 ${collapsed ? 'px-0' : ''}`}
title={collapsed ? (language === 'zh' ? '创建项目' : 'New Project') : undefined}
>
<Plus className={`size-5 transition-transform group-hover:rotate-90`} />
<span className={`transition-all duration-300 ${collapsed ? 'w-0 opacity-0 overflow-hidden' : 'w-auto opacity-100'}`}>
{language === 'zh' ? '创建项目' : 'New Project'}
</span>
</button>
</SidebarFooter>
</>
)
}

View File

@ -1,7 +1,6 @@
'use client'
import React from 'react'
import { Sidebar } from '../../components/Sidebar'
import { Header } from '../../components/Header'
interface Material3LayoutProps {
@ -11,7 +10,6 @@ interface Material3LayoutProps {
export function Material3Layout({ children }: Material3LayoutProps) {
return (
<div className="flex h-screen overflow-hidden bg-background text-text">
<Sidebar />
<div className="flex-1 flex flex-col overflow-y-auto">
<Header />
<main className="p-8 max-w-6xl mx-auto w-full">

View File

@ -478,9 +478,25 @@ type UserCenterTranslation = {
sections: {
workspace: string
productivity: string
infra: string
management: string
preferences: string
}
items: {
dashboard: string
mail: string
agents: string
apis: string
accounts: string
subscription: string
ldp: string
appearance: string
deployments: string
resources: string
apiKeys: string
logs: string
settings: string
}
overview: UserCenterOverviewTranslation
mfa: UserCenterMfaTranslation
}
@ -950,9 +966,25 @@ export const translations: Record<'en' | 'zh', Translation> = {
sections: {
workspace: 'Workspace',
productivity: 'Productivity Tools',
infra: 'Infrastructure',
management: 'Accounts & Access',
preferences: 'Preferences',
},
items: {
dashboard: 'Dashboard',
mail: 'Mail',
agents: 'Agents',
apis: 'APIs',
accounts: 'Accounts',
subscription: 'Subscription',
ldp: 'LDP',
appearance: 'Appearance',
deployments: 'Deployments',
resources: 'Resources',
apiKeys: 'API Keys',
logs: 'Logs',
settings: 'Settings',
},
overview: {
heading: 'User Center',
loading: 'Loading your personalized space…',
@ -1702,9 +1734,25 @@ export const translations: Record<'en' | 'zh', Translation> = {
sections: {
workspace: '工作台',
productivity: '生产力工具',
infra: '资源运维',
management: '账户与权限',
preferences: '偏好设置',
},
items: {
dashboard: '仪表盘',
mail: '邮箱服务',
agents: '运行节点',
apis: '接口集成',
accounts: '账户中心',
subscription: '订阅计划',
ldp: '身份平面',
appearance: '个性化',
deployments: '部署管理',
resources: '资源列表',
apiKeys: '接口密钥',
logs: '运行日志',
settings: '系统设置',
},
overview: {
heading: '用户中心',
loading: '正在加载你的专属空间…',

View File

@ -1,5 +1,9 @@
import type { DashboardExtension } from '../types'
import { userCenterExtension } from './user-center'
import { infraExtension } from './infra'
export const builtinExtensions: DashboardExtension[] = [userCenterExtension]
export const builtinExtensions: DashboardExtension[] = [
userCenterExtension,
infraExtension,
]

View File

@ -0,0 +1,66 @@
import { Database, History, Key, Rocket, Settings } from 'lucide-react'
import type { DashboardExtension } from '../../types'
export const infraExtension: DashboardExtension = {
id: 'builtin.infra',
meta: {
title: '基础设施管理',
description: '云基础设施、部署、资源与日志管理。',
version: '1.0.0',
author: 'Cloud-Neutral',
keywords: ['infrastructure', 'deployments', 'resources', 'logs'],
},
routes: [
{
id: 'deployments',
path: '/panel/deployments',
label: 'Deployments',
description: '部署任务与运行状态',
icon: Rocket,
loader: () => import('./routes/placeholder'),
guard: { requireLogin: true },
sidebar: { section: 'infra', order: 0 },
},
{
id: 'resources',
path: '/panel/resources',
label: 'Resources',
description: '云资源与数据库实例',
icon: Database,
loader: () => import('./routes/placeholder'),
guard: { requireLogin: true },
sidebar: { section: 'infra', order: 1 },
},
{
id: 'apiKeys',
path: '/panel/api-keys',
label: 'API Keys',
description: '接口密钥与访问凭证',
icon: Key,
loader: () => import('./routes/placeholder'),
guard: { requireLogin: true },
sidebar: { section: 'infra', order: 2 },
},
{
id: 'logs',
path: '/panel/logs',
label: 'Logs',
description: '系统流水与审计日志',
icon: History,
loader: () => import('./routes/placeholder'),
guard: { requireLogin: true },
sidebar: { section: 'infra', order: 3 },
},
{
id: 'settings',
path: '/panel/settings',
label: 'Settings',
description: '全局系统配置',
icon: Settings,
loader: () => import('./routes/placeholder'),
guard: { requireLogin: true, roles: ['admin', 'operator'] },
sidebar: { section: 'preferences', order: 99 },
},
],
}

View File

@ -0,0 +1,22 @@
'use client'
import React from 'react'
import { usePathname } from 'next/navigation'
export default function PlaceholderPage() {
const pathname = usePathname()
const title = pathname.split('/').pop()?.replace(/-/g, ' ') || 'Page'
return (
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center p-8">
<div className="size-16 bg-primary/10 rounded-2xl flex items-center justify-center text-primary mb-6">
<span className="text-2xl font-bold uppercase">{title.charAt(0)}</span>
</div>
<h1 className="text-3xl font-bold text-heading mb-3 capitalize">{title}</h1>
<p className="text-text-muted max-w-md">
This feature is currently under active development.
It will soon provide a powerful interface for managing your {title.toLowerCase()}.
</p>
</div>
)
}

View File

@ -13,6 +13,7 @@ export const userCenterExtension: DashboardExtension = {
},
routes: [
{
id: 'dashboard',
path: '/panel',
label: 'Dashboard',
description: '专属于你的信息总览',
@ -24,6 +25,7 @@ export const userCenterExtension: DashboardExtension = {
sidebar: { section: 'workspace', order: 0 },
},
{
id: 'mail',
path: '/panel/mail',
label: 'Mail',
description: '租户邮件与 AI 助理',
@ -34,6 +36,7 @@ export const userCenterExtension: DashboardExtension = {
sidebar: { section: 'workspace', order: 1 },
},
{
id: 'agents',
path: '/panel/agent',
label: 'Agents',
description: '管理运行节点',
@ -51,6 +54,7 @@ export const userCenterExtension: DashboardExtension = {
},
},
{
id: 'apis',
path: '/panel/api',
label: 'APIs',
description: '洞察后端服务',
@ -68,6 +72,7 @@ export const userCenterExtension: DashboardExtension = {
},
},
{
id: 'accounts',
path: '/panel/account',
label: 'Accounts',
description: '目录与多因素设置',
@ -78,6 +83,7 @@ export const userCenterExtension: DashboardExtension = {
sidebar: { section: 'management', order: 20 },
},
{
id: 'subscription',
path: '/panel/subscription',
label: 'Subscription',
description: '订阅方案与计费规则',
@ -95,6 +101,7 @@ export const userCenterExtension: DashboardExtension = {
},
},
{
id: 'ldp',
path: '/panel/ldp',
label: 'LDP',
description: '低时延身份平面',
@ -112,6 +119,7 @@ export const userCenterExtension: DashboardExtension = {
},
},
{
id: 'appearance',
path: '/panel/appearance',
label: 'Appearance',
description: '个性化主题设置',

View File

@ -27,6 +27,7 @@ export interface ExtensionMenuItem {
}
export interface ExtensionRoute {
id?: string
path: string
label: string
description?: string