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
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:
@@ -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)}
|
||||
|
||||
27
apps/web/src/lib/__tests__/product-ia.test.ts
Normal file
27
apps/web/src/lib/__tests__/product-ia.test.ts
Normal 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',
|
||||
]))
|
||||
})
|
||||
})
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user