feat(web): consolidate navigation IA shell
Some checks failed
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Successful in 1m45s
CD Pipeline / build-and-deploy (push) Successful in 5m15s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-25 11:43:21 +08:00
parent c3631c35a2
commit 93c4b81cca
7 changed files with 216 additions and 45 deletions

View File

@@ -8,7 +8,7 @@
"use client";
import { AppLayout } from "@/components/layout";
import { usePathname } from "@/i18n/routing";
import { Link, usePathname } from "@/i18n/routing";
import {
BrainCircuit,
CalendarDays,
@@ -115,6 +115,30 @@ export default function AwoooPLayout({
<span>{t("status.budget")}</span>
<span>{t("status.score")}</span>
</div>
<nav
aria-label={t("workflowRailLabel")}
className="flex max-w-full flex-wrap gap-1 overflow-hidden border-t border-[#e0ddd4] px-3 py-2 sm:px-5"
>
{navItems.map((item) => {
const active = activeItem.href === item.href;
return (
<Link
key={item.href}
href={item.href}
aria-current={active ? "page" : undefined}
className={[
"shrink-0 rounded-button border px-3 py-1.5 text-xs font-semibold transition-colors",
active
? "border-[#d97757] bg-[#fff7ed] text-[#141413]"
: "border-[#d8d3c7] bg-white text-[#6f6b62] hover:border-[#c8c1b3] hover:text-[#141413]",
].join(" ")}
>
{t(`nav.${item.labelKey}`)}
</Link>
);
})}
</nav>
</div>
<div className="w-full max-w-full">

View File

@@ -42,6 +42,7 @@ interface PaletteItem {
icon: React.ReactNode
action: () => void
keywords?: string[]
secondary?: boolean
}
export function CommandPalette() {
@@ -88,6 +89,7 @@ export function CommandPalette() {
icon: <Zap size={14} />,
action: () => nav('/authorizations'),
keywords: ['授權', 'approve', '批准', 'authorization'],
secondary: true,
},
{
id: 'observability',
@@ -105,6 +107,22 @@ export function CommandPalette() {
action: () => nav('/awooop'),
keywords: ['awooop', 'operator', 'console', '操作控制台', '總覽'],
},
{
id: 'knowledge-automation',
label: t('actionGoKnowledgeAutomation'),
group: t('groupNav'),
icon: <BookOpen size={14} />,
action: () => nav('/knowledge-base'),
keywords: ['knowledge', 'automation', '自動化', '知識', 'kb', 'playbook', 'runbook'],
},
{
id: 'governance-security',
label: t('actionGoGovernanceSecurity'),
group: t('groupNav'),
icon: <ShieldCheck size={14} />,
action: () => nav('/governance'),
keywords: ['governance', 'security', 'iwooos', 'code review', '治理', '安全', '資安', '審查'],
},
{
id: 'awooop-work-items',
label: t('actionGoWorkItems'),
@@ -112,6 +130,7 @@ export function CommandPalette() {
icon: <ClipboardList size={14} />,
action: () => nav('/awooop/work-items'),
keywords: ['work items', '工作鏈路', '工作項', 'recurrence'],
secondary: true,
},
{
id: 'awooop-runs',
@@ -120,6 +139,7 @@ export function CommandPalette() {
icon: <Activity size={14} />,
action: () => nav('/awooop/runs'),
keywords: ['runs', 'run monitor', '執行監控', 'run 監控'],
secondary: true,
},
{
id: 'awooop-approvals',
@@ -128,6 +148,7 @@ export function CommandPalette() {
icon: <ShieldCheck size={14} />,
action: () => nav('/awooop/approvals'),
keywords: ['approval queue', '審批佇列', '人工閘門', 'approve'],
secondary: true,
},
{
id: 'automation',
@@ -136,6 +157,7 @@ export function CommandPalette() {
icon: <Wrench size={14} />,
action: () => nav('/automation'),
keywords: ['automation', '自動化', 'auto'],
secondary: true,
},
{
id: 'operations',
@@ -162,14 +184,16 @@ export function CommandPalette() {
'資安網',
'資安態勢',
],
secondary: true,
},
{
id: 'knowledge',
label: t('actionGoKnowledge'),
group: t('groupNav'),
icon: <BookOpen size={14} />,
action: () => nav('/knowledge'),
action: () => nav('/knowledge-base'),
keywords: ['knowledge', '知識', '殿堂', 'kb'],
secondary: true,
},
{
id: 'settings',
@@ -178,6 +202,7 @@ export function CommandPalette() {
icon: <Settings size={14} />,
action: () => nav('/settings'),
keywords: ['settings', '設定', 'config'],
secondary: true,
},
{
id: 'terminal-page',
@@ -186,11 +211,12 @@ export function CommandPalette() {
icon: <Terminal size={14} />,
action: () => nav('/terminal'),
keywords: ['terminal', '終端', 'shell'],
secondary: true,
},
]
const filtered = query.trim() === ''
? items
? items.filter(item => !item.secondary)
: items.filter(item => {
const q = query.toLowerCase()
return item.label.toLowerCase().includes(q) ||

View File

@@ -26,28 +26,18 @@ import { usePathname } from 'next/navigation'
import Link from 'next/link'
import { cn } from '@/lib/utils'
import {
Activity,
Bell,
BookOpen,
BrainCircuit,
Building2,
ChevronLeft,
ChevronRight,
ClipboardList,
DollarSign,
FileText,
GitBranch,
HelpCircle,
LayoutDashboard,
Monitor,
Package,
Radar,
Route,
Settings,
ShieldCheck,
Terminal,
Ticket,
Wrench,
} from 'lucide-react'
// Phase 8.0 #15: 改用 approval store SSE (移除 polling)
import { useApprovalStore } from '@/stores/approval.store'
@@ -70,6 +60,7 @@ type NavItemConfig = {
labelKey: string
Icon: typeof LayoutDashboard
aliases?: string[]
relatedPaths?: string[]
exact?: boolean
badge?: boolean // 是否顯示動態徽章
}
@@ -88,45 +79,63 @@ type NavSection = {
const NAV_SECTIONS: NavSection[] = [
{
sectionKey: 'queues',
sectionKey: 'workspaces',
items: [
{ id: 'command-center', href: '/', labelKey: 'commandCenter', Icon: LayoutDashboard },
{ id: 'awooop-home', href: '/awooop', labelKey: 'awooopHome', Icon: BrainCircuit, exact: true },
{ id: 'awooop-work-items', href: '/awooop/work-items', labelKey: 'workItems', Icon: ClipboardList },
{ id: 'awooop-runs', href: '/awooop/runs', labelKey: 'runMonitor', Icon: Activity },
{ id: 'awooop-approvals', href: '/awooop/approvals', labelKey: 'approvalQueue', Icon: ShieldCheck, badge: true },
{ id: 'awooop-contracts', href: '/awooop/contracts', labelKey: 'contracts', Icon: FileText },
{ id: 'awooop-tenants', href: '/awooop/tenants', labelKey: 'tenants', Icon: Building2 },
{ id: 'alerts', href: '/alerts', labelKey: 'alerts', Icon: Bell },
{
id: 'awooop-workbench',
href: '/awooop',
labelKey: 'awooopWorkbench',
Icon: BrainCircuit,
exact: true,
badge: true,
aliases: [
'/awooop/work-items',
'/awooop/runs',
'/awooop/approvals',
'/awooop/contracts',
'/awooop/tenants',
'/alerts',
'/authorizations',
],
},
{
id: 'observability',
href: '/observability',
labelKey: 'observability',
Icon: Monitor,
aliases: ['/monitoring', '/apm', '/errors', '/apps', '/services', '/topology'],
},
{
id: 'knowledge-automation',
href: '/knowledge-base',
labelKey: 'knowledgeAutomation',
Icon: BookOpen,
aliases: ['/knowledge', '/automation', '/auto-repair', '/drift', '/neural-command'],
},
{
id: 'iwooos-security',
href: '/governance',
labelKey: 'iwooos',
Icon: ShieldCheck,
aliases: ['/security-compliance'],
relatedPaths: ['/iwooos', '/code-review', '/security', '/compliance'],
},
{
id: 'operations',
href: '/operations',
labelKey: 'operationsOverview',
Icon: Package,
aliases: ['/deployments', '/tickets', '/cost', '/billing', '/action-logs', '/reports'],
},
],
},
{
sectionKey: 'truth',
items: [
{ id: 'observability', href: '/observability', labelKey: 'observability', Icon: Monitor },
{ id: 'automation', href: '/automation', labelKey: 'automation', Icon: Wrench },
{ id: 'governance', href: '/governance', labelKey: 'governance', Icon: ShieldCheck },
{ id: 'knowledge', href: '/knowledge', labelKey: 'knowledge', Icon: BookOpen },
{ id: 'iwooos-security', href: '/iwooos', labelKey: 'iwooos', Icon: Radar, aliases: ['/security-compliance'] },
],
},
{
sectionKey: 'ops',
items: [
{ id: 'operations', href: '/operations', labelKey: 'operationsOverview', Icon: Package },
{ id: 'topology', href: '/topology', labelKey: 'topology', Icon: Route },
{ id: 'deployments', href: '/deployments', labelKey: 'deployments', Icon: GitBranch },
{ id: 'tickets', href: '/tickets', labelKey: 'tickets', Icon: Ticket },
{ id: 'cost', href: '/cost', labelKey: 'cost', Icon: DollarSign },
],
},
{
// Legacy: 經典 AI 中心 (既有決策保留)
sectionKey: 'legacy',
sectionKey: 'system',
items: [
{ id: 'terminal', href: '/terminal', labelKey: 'terminal', Icon: Terminal },
{ id: 'settings', href: '/settings', labelKey: 'settings', Icon: Settings },
{ id: 'classic', href: '/classic', labelKey: 'classicAICenter', Icon: LayoutDashboard },
{ id: 'classic', href: '/classic', labelKey: 'classicAICenter', Icon: Radar },
],
},
]
@@ -173,7 +182,9 @@ export function Sidebar({
}
const isActive = (item: NavItemConfig) => (
isRouteActive(item.href, item.exact) || item.aliases?.some(alias => isRouteActive(alias)) === true
isRouteActive(item.href, item.exact) ||
item.aliases?.some(alias => isRouteActive(alias)) === true ||
item.relatedPaths?.some(path => isRouteActive(path)) === true
)
const sidebarWidth = compact && collapsed ? 48 : collapsed ? 64 : 224