Phase C: Merge Versions into Releases as a tab

Unify the Releases page with Pipeline + Versions tabs:
- Add stella-page-tabs with Pipeline (default) and Versions tabs
- Pipeline tab shows the existing release pipeline table
- Versions tab renders the ReleaseListComponent (version catalog) inline
- /releases/versions redirects to /releases?tab=versions for bookmarks
- Updated subtitle to "Versions, deployments, approvals, and promotion pipeline."

The Versions sidebar item was already removed in Phase A. This completes
the unification — one place for all release-related views.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-03-27 16:20:02 +02:00
parent 71e9d1c7fa
commit 9d5f33e450
2 changed files with 27 additions and 8 deletions

View File

@@ -12,14 +12,21 @@ import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject, signal,
import { PageActionService } from '../../core/services/page-action.service';
import { PageActionOutletComponent } from '../../shared/components/page-action-outlet/page-action-outlet.component';
import { UpperCasePipe, SlicePipe } from '@angular/common';
import { RouterLink } from '@angular/router';
import { RouterLink, ActivatedRoute } from '@angular/router';
import { StellaFilterChipComponent, FilterChipOption } from '../../shared/components/stella-filter-chip/stella-filter-chip.component';
import { StellaPageTabsComponent, StellaPageTab } from '../../shared/components/stella-page-tabs/stella-page-tabs.component';
import { PaginationComponent, PageChangeEvent } from '../../shared/components/pagination/pagination.component';
import { TableColumn } from '../../shared/components/data-table/data-table.component';
import { ReleaseManagementStore } from '../release-orchestrator/releases/release.store';
import { ReleaseListComponent } from '../release-orchestrator/releases/release-list/release-list.component';
import { PlatformContextStore } from '../../core/context/platform-context.store';
import type { ReleaseWorkflowStatus } from '../../core/api/release-management.models';
const RELEASE_TABS: readonly StellaPageTab[] = [
{ id: 'pipeline', label: 'Pipeline', icon: 'M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z' },
{ id: 'versions', label: 'Versions', icon: 'M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5' },
];
// ── Data model ──────────────────────────────────────────────────────────────
export interface PipelineRelease {
@@ -52,8 +59,10 @@ export interface PipelineRelease {
SlicePipe,
RouterLink,
StellaFilterChipComponent,
StellaPageTabsComponent,
PaginationComponent,
PageActionOutletComponent,
ReleaseListComponent,
],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
@@ -61,11 +70,15 @@ export interface PipelineRelease {
<header class="rup__header">
<div>
<h1 class="rup__title">Releases</h1>
<p class="rup__subtitle">Unified pipeline view — versions, deployments, hotfixes, and approvals.</p>
<p class="rup__subtitle">Versions, deployments, approvals, and promotion pipeline.</p>
</div>
<app-page-action-outlet />
</header>
<stella-page-tabs [tabs]="releaseTabs" [activeTab]="activeTab()" urlParam="tab"
(tabChange)="activeTab.set($event)" ariaLabel="Releases tabs" />
@if (activeTab() === 'pipeline') {
<!-- Pipeline -->
<div class="rup__filters">
<div class="rup__search">
@@ -242,6 +255,11 @@ export interface PipelineRelease {
/>
</div>
}
} <!-- end pipeline tab -->
@if (activeTab() === 'versions') {
<app-release-list />
}
</div>
`,
styles: [`
@@ -361,6 +379,10 @@ export class ReleasesUnifiedPageComponent implements OnInit, OnDestroy {
private readonly pageAction = inject(PageActionService);
private readonly store = inject(ReleaseManagementStore);
private readonly context = inject(PlatformContextStore);
private readonly route = inject(ActivatedRoute);
readonly releaseTabs = RELEASE_TABS;
readonly activeTab = signal<string>(this.route.snapshot.queryParamMap.get('tab') || 'pipeline');
ngOnInit(): void {
this.pageAction.set({ label: 'New Release', route: '/releases/new' });

View File

@@ -76,14 +76,11 @@ export const RELEASES_ROUTES: Routes = [
(m) => m.ReleaseDetailPageComponent,
),
},
// Versions merged into Releases as a tab — redirect for bookmarks
{
path: 'versions',
title: 'Release Versions',
data: { breadcrumb: 'Release Versions', semanticObject: 'version' },
loadComponent: () =>
import('../features/release-orchestrator/releases/release-list/release-list.component').then(
(m) => m.ReleaseListComponent,
),
redirectTo: '/releases?tab=versions',
pathMatch: 'full' as const,
},
{
path: 'new',