fix(web): restore secondary navigation routes
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 19s
CD Pipeline / build-and-deploy (push) Successful in 3m40s
CD Pipeline / post-deploy-checks (push) Successful in 1m20s

This commit is contained in:
Your Name
2026-06-29 19:39:45 +08:00
parent 7acf48a35b
commit d14a25f93c
3 changed files with 55 additions and 14 deletions

View File

@@ -10,9 +10,10 @@
* - 單色圖示
*
* IA D0 (2026-06-27):
* - 側欄只放高頻 primary 工作台。
* - Runs / Approvals / Contracts 等流程細節保留深連結,不再攤平成主導航。
* - 頁面內以同頁 sections 呈現關鍵狀態,減少二層分頁藏內容
* - 側欄保留 primary 工作台與 secondary 深連結
* - Runs / Approvals / Contracts / Alerts 等流程細節以二級樣式顯示,
* 避免路由被移除後只剩隱藏連結
* - 頁面內仍以同頁 sections 呈現關鍵狀態,減少二層分頁藏內容。
*
* Phase 19: 使用 Z_INDEX.SIDEBAR (40)
* @see lib/constants/z-index.ts
@@ -126,8 +127,7 @@ export function Sidebar({
{/* 導航列表 - Operator workflow sections */}
<nav className="flex-1 py-2 overflow-y-auto">
{PRODUCT_NAV_SECTIONS.map(section => {
const visibleItems = section.items.filter(item => item.surface !== 'secondary')
if (visibleItems.length === 0) return null
if (section.items.length === 0) return null
return (
<div key={section.sectionKey} style={{ marginBottom: 4 }}>
@@ -145,9 +145,10 @@ export function Sidebar({
{tSection(section.sectionKey)}
</div>
)}
{visibleItems.map(item => {
{section.items.map(item => {
const active = isActive(item)
const count = item.badge && mounted ? pendingCount : 0
const secondary = item.surface === 'secondary'
return (
<Link
key={item.id}
@@ -157,21 +158,21 @@ export function Sidebar({
style={{
display: 'flex',
alignItems: 'center',
gap: collapsed ? 0 : 8,
gap: collapsed ? 0 : secondary ? 7 : 8,
justifyContent: collapsed ? 'center' : 'flex-start',
padding: collapsed ? '8px 0' : '6px 12px',
fontSize: 14,
color: active ? '#141413' : '#87867f',
padding: collapsed ? '8px 0' : secondary ? '4px 12px 4px 30px' : '6px 12px',
fontSize: secondary ? 13 : 14,
color: active ? '#141413' : secondary ? '#77736a' : '#87867f',
fontWeight: active ? 600 : 400,
borderRight: active ? '2px solid #d97757' : '2px solid transparent',
background: active ? 'rgba(217,119,87,0.08)' : 'transparent',
textDecoration: 'none',
transition: 'all 0.15s',
position: 'relative' as const,
minHeight: collapsed ? 36 : 32,
minHeight: collapsed ? 36 : secondary ? 28 : 32,
}}
>
<item.Icon size={15} aria-hidden="true" />
<item.Icon size={secondary ? 14 : 15} aria-hidden="true" />
{!collapsed && (
<span style={{ minWidth: 0, overflowWrap: 'anywhere' }}>
{t(item.labelKey)}

View File

@@ -0,0 +1,27 @@
import { describe, expect, it } from 'vitest'
import { PRODUCT_NAV_SECTIONS } from '../navigation/product-ia'
const navIds = PRODUCT_NAV_SECTIONS.flatMap(section => section.items.map(item => item.id))
describe('product IA navigation', () => {
it('keeps secondary operational routes visible in the canonical nav map', () => {
expect(navIds).toEqual(expect.arrayContaining([
'awooop-runs',
'awooop-approvals',
'awooop-contracts',
'alerts',
'reports',
'terminal',
'classic',
]))
})
it('keeps IwoooS related security routes integrated instead of removing them', () => {
expect(navIds).toEqual(expect.arrayContaining([
'iwooos-security',
'iwooos-security-compliance',
'governance-security',
]))
})
})

View File

@@ -134,8 +134,21 @@ export const PRODUCT_NAV_SECTIONS: ProductNavSection[] = [
href: '/iwooos',
labelKey: 'iwooos',
Icon: ShieldCheck,
aliases: ['/security-compliance'],
relatedPaths: ['/security', '/compliance', '/governance'],
},
{
id: 'iwooos-security-compliance',
href: '/security-compliance',
labelKey: 'iwooosSecurityCompliance',
Icon: ShieldCheck,
aliases: ['/security', '/compliance'],
surface: 'secondary',
},
{
id: 'governance-security',
href: '/governance',
labelKey: 'governanceSecurity',
Icon: Radar,
surface: 'secondary',
},
{
id: 'operations',