feat: merge services sidebar into panel and remove sidebar from services page
This commit is contained in:
parent
87424d069c
commit
60ec70f9ac
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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: '正在加载你的专属空间…',
|
||||
|
||||
@ -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,
|
||||
]
|
||||
|
||||
66
src/modules/extensions/builtin/infra/index.ts
Normal file
66
src/modules/extensions/builtin/infra/index.ts
Normal 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 },
|
||||
},
|
||||
],
|
||||
}
|
||||
22
src/modules/extensions/builtin/infra/routes/placeholder.tsx
Normal file
22
src/modules/extensions/builtin/infra/routes/placeholder.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@ -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: '个性化主题设置',
|
||||
|
||||
@ -27,6 +27,7 @@ export interface ExtensionMenuItem {
|
||||
}
|
||||
|
||||
export interface ExtensionRoute {
|
||||
id?: string
|
||||
path: string
|
||||
label: string
|
||||
description?: string
|
||||
|
||||
Loading…
Reference in New Issue
Block a user