Restructure sidebar navigation to match product workflows
Major IA restructure based on product-level analysis of Stella Ops core workflows (release lifecycle, policy gates, evidence chain, security posture). New 7-group structure (was 5): - Dashboard: new ungrouped home link (no group header) - Release Control: Deployments, Releases, Environments (moved from Ops) - Security: Vulnerabilities, Security Posture (+children), Scan Image (Reports removed — will merge into Posture in Phase B) - Policy: NEW GROUP — Packs, Governance, Simulation, VEX & Exceptions, Release Gates, Policy Audit (promoted from buried Operations item) - Operations: slimmed from 11→8 items (Hub, Jobs, Scripts, Signals, Diagnostics, Notifications, Feeds, Watchlist) - Audit & Evidence: Overview, Capsules, Replay, Export, Audit Log, Bundles (route fixed to /evidence/bundles), Trust (merged name) - Settings: unchanged Rationale: - Policy is a release-blocking gate with 6 deep sub-workflows — not an ops utility - Environments define the promotion graph — belongs in Release Control - Trust Audit + Trust Analytics merged into single "Trust" item - Reports removed (duplicated Security Posture content) - Versions removed from sidebar (will merge into Releases as tab in Phase C) - Dashboard link ensures users can always navigate home without logo click Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -116,7 +116,7 @@ interface NavSectionGroup {
|
||||
role="group"
|
||||
[attr.aria-label]="group.label"
|
||||
>
|
||||
@if (!effectiveCollapsed) {
|
||||
@if (!effectiveCollapsed && group.id !== 'home') {
|
||||
<button
|
||||
type="button"
|
||||
class="sb-group__header"
|
||||
@@ -626,10 +626,19 @@ export class AppSidebarComponent implements AfterViewInit {
|
||||
private readonly pendingApprovalsBadgeLoading = signal(false);
|
||||
|
||||
/**
|
||||
* Navigation sections - canonical 5-group IA.
|
||||
* Groups: Release Control, Security, Operations, Audit & Evidence, Setup & Admin.
|
||||
* Navigation sections - canonical 7-group IA.
|
||||
* Groups: Home (ungrouped), Release Control, Security, Policy, Operations, Audit & Evidence, Setup & Admin.
|
||||
*/
|
||||
readonly navSections: NavSection[] = [
|
||||
// ── Home (ungrouped) ───────────────────────────────────────────
|
||||
{
|
||||
id: 'dashboard',
|
||||
label: 'Dashboard',
|
||||
icon: 'home',
|
||||
route: '/',
|
||||
menuGroupId: 'home',
|
||||
menuGroupLabel: '',
|
||||
},
|
||||
// ── Group 1: Release Control ─────────────────────────────────────
|
||||
{
|
||||
id: 'deployments',
|
||||
@@ -660,18 +669,7 @@ export class AppSidebarComponent implements AfterViewInit {
|
||||
StellaOpsScopes.RELEASE_PUBLISH,
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'versions',
|
||||
label: 'Versions',
|
||||
icon: 'layers',
|
||||
route: '/releases/versions',
|
||||
menuGroupId: 'release-control',
|
||||
menuGroupLabel: 'Release Control',
|
||||
requireAnyScope: [
|
||||
StellaOpsScopes.RELEASE_READ,
|
||||
StellaOpsScopes.RELEASE_WRITE,
|
||||
],
|
||||
},
|
||||
// Versions merged into Releases page as a tab
|
||||
// Approvals nav item removed — merged into Deployments page as a tab
|
||||
// ── Group 2: Security ────────────────────────────────────────────
|
||||
{
|
||||
@@ -720,18 +718,7 @@ export class AppSidebarComponent implements AfterViewInit {
|
||||
menuGroupLabel: 'Security',
|
||||
requireAnyScope: [StellaOpsScopes.SCANNER_READ],
|
||||
},
|
||||
{
|
||||
id: 'sec-reports',
|
||||
label: 'Reports',
|
||||
icon: 'book-open',
|
||||
route: '/security/reports',
|
||||
menuGroupId: 'security',
|
||||
menuGroupLabel: 'Security',
|
||||
requireAnyScope: [
|
||||
StellaOpsScopes.SCANNER_READ,
|
||||
StellaOpsScopes.FINDINGS_READ,
|
||||
],
|
||||
},
|
||||
// Reports merged into Security Posture (export actions moved inline)
|
||||
// ── Group 3: Operations ──────────────────────────────────────────
|
||||
{
|
||||
id: 'ops',
|
||||
@@ -791,8 +778,8 @@ export class AppSidebarComponent implements AfterViewInit {
|
||||
label: 'Environments',
|
||||
icon: 'globe',
|
||||
route: '/environments/regions',
|
||||
menuGroupId: 'operations',
|
||||
menuGroupLabel: 'Operations',
|
||||
menuGroupId: 'release-control',
|
||||
menuGroupLabel: 'Release Control',
|
||||
requireAnyScope: [
|
||||
StellaOpsScopes.ORCH_READ,
|
||||
StellaOpsScopes.ORCH_OPERATE,
|
||||
@@ -800,13 +787,58 @@ export class AppSidebarComponent implements AfterViewInit {
|
||||
},
|
||||
{
|
||||
id: 'ops-policy',
|
||||
label: 'Policy',
|
||||
label: 'Packs',
|
||||
icon: 'clipboard',
|
||||
route: '/ops/policy',
|
||||
menuGroupId: 'operations',
|
||||
menuGroupLabel: 'Operations',
|
||||
route: '/ops/policy/packs',
|
||||
menuGroupId: 'policy',
|
||||
menuGroupLabel: 'Policy',
|
||||
requireAnyScope: [StellaOpsScopes.POLICY_READ],
|
||||
},
|
||||
{
|
||||
id: 'ops-policy-governance',
|
||||
label: 'Governance',
|
||||
icon: 'shield',
|
||||
route: '/ops/policy/governance',
|
||||
menuGroupId: 'policy',
|
||||
menuGroupLabel: 'Policy',
|
||||
requireAnyScope: [StellaOpsScopes.POLICY_READ],
|
||||
},
|
||||
{
|
||||
id: 'ops-policy-simulation',
|
||||
label: 'Simulation',
|
||||
icon: 'play',
|
||||
route: '/ops/policy/simulation',
|
||||
menuGroupId: 'policy',
|
||||
menuGroupLabel: 'Policy',
|
||||
requireAnyScope: [StellaOpsScopes.POLICY_SIMULATE],
|
||||
},
|
||||
{
|
||||
id: 'ops-policy-vex',
|
||||
label: 'VEX & Exceptions',
|
||||
icon: 'file-text',
|
||||
route: '/ops/policy/vex',
|
||||
menuGroupId: 'policy',
|
||||
menuGroupLabel: 'Policy',
|
||||
requireAnyScope: [StellaOpsScopes.VEX_READ, StellaOpsScopes.EXCEPTION_READ],
|
||||
},
|
||||
{
|
||||
id: 'ops-policy-gates',
|
||||
label: 'Release Gates',
|
||||
icon: 'check-square',
|
||||
route: '/ops/policy/gates',
|
||||
menuGroupId: 'policy',
|
||||
menuGroupLabel: 'Policy',
|
||||
requireAnyScope: [StellaOpsScopes.POLICY_READ],
|
||||
},
|
||||
{
|
||||
id: 'ops-policy-audit',
|
||||
label: 'Policy Audit',
|
||||
icon: 'list',
|
||||
route: '/ops/policy/audit',
|
||||
menuGroupId: 'policy',
|
||||
menuGroupLabel: 'Policy',
|
||||
requireAnyScope: [StellaOpsScopes.POLICY_AUDIT],
|
||||
},
|
||||
{
|
||||
id: 'ops-diagnostics',
|
||||
label: 'Diagnostics',
|
||||
@@ -846,15 +878,7 @@ export class AppSidebarComponent implements AfterViewInit {
|
||||
menuGroupLabel: 'Operations',
|
||||
requireAnyScope: [StellaOpsScopes.SIGNER_READ],
|
||||
},
|
||||
{
|
||||
id: 'ops-trust-analytics',
|
||||
label: 'Trust Analytics',
|
||||
icon: 'trending-up',
|
||||
route: '/ops/operations/trust-analytics',
|
||||
menuGroupId: 'operations',
|
||||
menuGroupLabel: 'Operations',
|
||||
requireAnyScope: [StellaOpsScopes.SIGNER_READ],
|
||||
},
|
||||
// Trust Analytics merged into Trust (Audit & Evidence group)
|
||||
// ── Group 4: Audit & Evidence ────────────────────────────────────
|
||||
{
|
||||
id: 'evidence-overview',
|
||||
@@ -923,7 +947,7 @@ export class AppSidebarComponent implements AfterViewInit {
|
||||
id: 'evidence-bundles',
|
||||
label: 'Bundles',
|
||||
icon: 'inbox',
|
||||
route: '/triage/audit-bundles',
|
||||
route: '/evidence/bundles',
|
||||
menuGroupId: 'audit-evidence',
|
||||
menuGroupLabel: 'Audit & Evidence',
|
||||
requireAnyScope: [
|
||||
@@ -932,8 +956,8 @@ export class AppSidebarComponent implements AfterViewInit {
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'evidence-trust-audit',
|
||||
label: 'Trust Audit',
|
||||
id: 'evidence-trust',
|
||||
label: 'Trust',
|
||||
icon: 'shield-check',
|
||||
route: '/evidence/audit-log/trust',
|
||||
menuGroupId: 'audit-evidence',
|
||||
@@ -1013,7 +1037,7 @@ export class AppSidebarComponent implements AfterViewInit {
|
||||
/** Menu groups rendered in deterministic order for scanability */
|
||||
readonly displaySectionGroups = computed<NavSectionGroup[]>(() => {
|
||||
const orderedGroups = new Map<string, NavSectionGroup>();
|
||||
const groupOrder = ['release-control', 'security', 'operations', 'audit-evidence', 'setup-admin', 'misc'];
|
||||
const groupOrder = ['home', 'release-control', 'security', 'policy', 'operations', 'audit-evidence', 'setup-admin', 'misc'];
|
||||
|
||||
for (const groupId of groupOrder) {
|
||||
orderedGroups.set(groupId, {
|
||||
@@ -1126,10 +1150,14 @@ export class AppSidebarComponent implements AfterViewInit {
|
||||
|
||||
private resolveMenuGroupLabel(groupId: string): string {
|
||||
switch (groupId) {
|
||||
case 'home':
|
||||
return '';
|
||||
case 'release-control':
|
||||
return 'Release Control';
|
||||
case 'security':
|
||||
return 'Security';
|
||||
case 'policy':
|
||||
return 'Policy';
|
||||
case 'operations':
|
||||
return 'Operations';
|
||||
case 'audit-evidence':
|
||||
|
||||
Reference in New Issue
Block a user