diff --git a/src/Web/StellaOps.Web/src/app/features/releases/releases-activity.component.ts b/src/Web/StellaOps.Web/src/app/features/releases/releases-activity.component.ts index 3ff6aa66f..db35e30db 100644 --- a/src/Web/StellaOps.Web/src/app/features/releases/releases-activity.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/releases/releases-activity.component.ts @@ -204,14 +204,30 @@ function deriveOutcomeIcon(status: string): string { } @else if (filteredApprovals().length === 0) {
No approvals match the active gate filters.
} @else { + +
+
+ {{ countByStatus('pending') }} + Pending Approvals +
+
+ {{ countByGateResult(false) }} + Gate Failures +
+
+ {{ countByGateResult(true) }} + Gates Passed +
+
+ + - - + @@ -226,9 +242,11 @@ function deriveOutcomeIcon(status: string): string { } + - - + } @empty { - + }
Release PromotionGate Type StatusUrgencyGatesReason Requested Actions
{{ apr.sourceEnvironment }} → {{ apr.targetEnvironment }}{{ deriveGateType(apr) }} {{ apr.status }}{{ apr.urgency }}{{ apr.gatesPassed ? 'Passed' : 'Failed' }} + {{ apr.gatesPassed ? 'All gates passed' : 'Gates blocking — review required' }} + {{ formatDate(apr.requestedAt) }}
by {{ apr.requestedBy }}
@@ -236,12 +254,15 @@ function deriveOutcomeIcon(status: string): string { } + @if (!apr.gatesPassed) { + Policy + }
No approvals match the active gate filters.
No approvals or gate evaluations match the current filters.
@@ -395,6 +416,14 @@ function deriveOutcomeIcon(status: string): string { .apc__status-row{display:flex;gap:.35rem;padding:0 .6rem .45rem;flex-wrap:wrap} + .gate-summary{display:flex;gap:1rem;margin-bottom:1rem} + .gate-summary__card{flex:1;padding:0.75rem 1rem;border:1px solid var(--color-border-primary);border-radius:var(--radius-md);display:flex;align-items:center;gap:0.5rem} + .gate-summary__card strong{font-size:1.25rem} + .gate-summary__card span{font-size:0.75rem;color:var(--color-text-muted);text-transform:uppercase;letter-spacing:0.04em} + .gate-summary__card--pending{border-left:3px solid var(--color-status-warning)} + .gate-summary__card--blocked{border-left:3px solid var(--color-severity-high)} + .gate-summary__card--passed{border-left:3px solid var(--color-status-success)} + .gate-type-chip{font-size:0.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:0.03em;padding:0.125rem 0.5rem;border-radius:var(--radius-sm);background:var(--color-surface-tertiary)} .pending-lane{margin-bottom:1rem;padding:1rem;border:1px solid var(--color-status-warning-border);border-radius:var(--radius-lg);background:var(--color-status-warning-bg);animation:lane-enter 300ms ease-out both;overflow:hidden} .pending-lane--exiting{animation:lane-exit 250ms ease-in forwards} @keyframes lane-enter{from{opacity:0;max-height:0;padding:0;margin:0;border-width:0}to{opacity:1;max-height:500px}} @@ -523,6 +552,14 @@ export class ReleasesActivityComponent implements OnInit, OnDestroy { readonly approvalSearchQuery = signal(''); + countByStatus(status: string): number { + return this.filteredApprovals().filter(a => a.status === status).length; + } + + countByGateResult(passed: boolean): number { + return this.filteredApprovals().filter(a => a.gatesPassed === passed).length; + } + readonly gateToggleState = signal>({ gated: false, policy: false, diff --git a/src/Web/StellaOps.Web/src/app/layout/app-sidebar/app-sidebar.component.ts b/src/Web/StellaOps.Web/src/app/layout/app-sidebar/app-sidebar.component.ts index 7e066904d..5a98b0533 100644 --- a/src/Web/StellaOps.Web/src/app/layout/app-sidebar/app-sidebar.component.ts +++ b/src/Web/StellaOps.Web/src/app/layout/app-sidebar/app-sidebar.component.ts @@ -777,7 +777,7 @@ export class AppSidebarComponent implements AfterViewInit { id: 'ops-environments', label: 'Environments', icon: 'globe', - route: '/environments/regions', + route: '/environments/overview', menuGroupId: 'release-control', menuGroupLabel: 'Release Control', requireAnyScope: [ @@ -821,15 +821,7 @@ export class AppSidebarComponent implements AfterViewInit { 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], - }, + // Release Gates absorbed into Deployments > Approvals tab { id: 'ops-policy-audit', label: 'Policy Audit',