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 1a2ed83c0..f72299796 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 @@ -70,8 +70,8 @@ function deriveOutcomeIcon(status: string): string {

Deployment runs, approvals, and promotion activity.

- - @if (pendingApprovals().length > 0 && viewMode() !== 'approvals') { + + @if (showPendingLane()) {

@@ -197,49 +197,47 @@ function deriveOutcomeIcon(status: string): string { } @else if (filteredApprovals().length === 0) {
No approvals match the active gate filters.
} @else { -
- @if (showApcLeft()) { - - } -
+ + + + + + + + + + + + + @for (apr of filteredApprovals(); track apr.id) { -
-
-
{{ apr.releaseName }}@if (apr.releaseVersion && apr.releaseVersion !== apr.releaseName) {{{ apr.releaseVersion }}}
- {{ apr.urgency }} -
-
- {{ apr.sourceEnvironment }} - - {{ apr.targetEnvironment }} -
-
- by {{ apr.requestedBy }} - {{ apr.gatesPassed ? 'Gates OK' : 'Gates fail' }} - {{ timeRemaining(apr.expiresAt) }} -
-
- {{ apr.status }} - {{ deriveGateType(apr) }} -
-
- @if (apr.status === 'pending') { - - +
+ + + + + + + + + } @empty { + } - - @if (showApcRight()) { - - } - + +
ReleasePromotionStatusUrgencyGatesRequestedActions
+ {{ apr.releaseName }} + @if (apr.releaseVersion && apr.releaseVersion !== apr.releaseName) { +
{{ apr.releaseVersion }} } - - - +
{{ apr.sourceEnvironment }} → {{ apr.targetEnvironment }}{{ apr.status }}{{ apr.urgency }}{{ apr.gatesPassed ? 'Passed' : 'Failed' }}{{ formatDate(apr.requestedAt) }}
by {{ apr.requestedBy }}
+
+ @if (apr.status === 'pending') { + + + } + +
+
No approvals match the active gate filters.
} } @else { @@ -733,12 +731,17 @@ export class ReleasesActivityComponent implements OnInit, OnDestroy { } readonly pendingLaneExiting = signal(false); + readonly showPendingLane = computed(() => + this.pendingApprovals().length > 0 && this.viewMode() === 'timeline' && !this.pendingLaneHidden() + ); + private readonly pendingLaneHidden = signal(false); onTabChange(tab: string): void { // Animate pending lane out before switching to approvals - if (tab === 'approvals' && this.pendingApprovals().length > 0 && this.viewMode() !== 'approvals') { + if (tab === 'approvals' && this.pendingApprovals().length > 0 && this.viewMode() === 'timeline') { this.pendingLaneExiting.set(true); setTimeout(() => { + this.pendingLaneHidden.set(true); this.pendingLaneExiting.set(false); this.viewMode.set('approvals'); this.loadApprovals(); @@ -746,6 +749,10 @@ export class ReleasesActivityComponent implements OnInit, OnDestroy { }, 250); return; } + // When switching back to pipeline, show pending lane again + if (tab === 'timeline') { + this.pendingLaneHidden.set(false); + } this.viewMode.set(tab as 'timeline' | 'approvals'); if (tab === 'approvals') this.loadApprovals(); this.applyFilters();