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:
@@ -12,14 +12,21 @@ import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject, signal,
|
|||||||
import { PageActionService } from '../../core/services/page-action.service';
|
import { PageActionService } from '../../core/services/page-action.service';
|
||||||
import { PageActionOutletComponent } from '../../shared/components/page-action-outlet/page-action-outlet.component';
|
import { PageActionOutletComponent } from '../../shared/components/page-action-outlet/page-action-outlet.component';
|
||||||
import { UpperCasePipe, SlicePipe } from '@angular/common';
|
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 { 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 { PaginationComponent, PageChangeEvent } from '../../shared/components/pagination/pagination.component';
|
||||||
import { TableColumn } from '../../shared/components/data-table/data-table.component';
|
import { TableColumn } from '../../shared/components/data-table/data-table.component';
|
||||||
import { ReleaseManagementStore } from '../release-orchestrator/releases/release.store';
|
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 { PlatformContextStore } from '../../core/context/platform-context.store';
|
||||||
import type { ReleaseWorkflowStatus } from '../../core/api/release-management.models';
|
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 ──────────────────────────────────────────────────────────────
|
// ── Data model ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export interface PipelineRelease {
|
export interface PipelineRelease {
|
||||||
@@ -52,8 +59,10 @@ export interface PipelineRelease {
|
|||||||
SlicePipe,
|
SlicePipe,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
StellaFilterChipComponent,
|
StellaFilterChipComponent,
|
||||||
|
StellaPageTabsComponent,
|
||||||
PaginationComponent,
|
PaginationComponent,
|
||||||
PageActionOutletComponent,
|
PageActionOutletComponent,
|
||||||
|
ReleaseListComponent,
|
||||||
],
|
],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
template: `
|
template: `
|
||||||
@@ -61,11 +70,15 @@ export interface PipelineRelease {
|
|||||||
<header class="rup__header">
|
<header class="rup__header">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="rup__title">Releases</h1>
|
<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>
|
</div>
|
||||||
<app-page-action-outlet />
|
<app-page-action-outlet />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<stella-page-tabs [tabs]="releaseTabs" [activeTab]="activeTab()" urlParam="tab"
|
||||||
|
(tabChange)="activeTab.set($event)" ariaLabel="Releases tabs" />
|
||||||
|
|
||||||
|
@if (activeTab() === 'pipeline') {
|
||||||
<!-- Pipeline -->
|
<!-- Pipeline -->
|
||||||
<div class="rup__filters">
|
<div class="rup__filters">
|
||||||
<div class="rup__search">
|
<div class="rup__search">
|
||||||
@@ -242,6 +255,11 @@ export interface PipelineRelease {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
} <!-- end pipeline tab -->
|
||||||
|
|
||||||
|
@if (activeTab() === 'versions') {
|
||||||
|
<app-release-list />
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
styles: [`
|
styles: [`
|
||||||
@@ -361,6 +379,10 @@ export class ReleasesUnifiedPageComponent implements OnInit, OnDestroy {
|
|||||||
private readonly pageAction = inject(PageActionService);
|
private readonly pageAction = inject(PageActionService);
|
||||||
private readonly store = inject(ReleaseManagementStore);
|
private readonly store = inject(ReleaseManagementStore);
|
||||||
private readonly context = inject(PlatformContextStore);
|
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 {
|
ngOnInit(): void {
|
||||||
this.pageAction.set({ label: 'New Release', route: '/releases/new' });
|
this.pageAction.set({ label: 'New Release', route: '/releases/new' });
|
||||||
|
|||||||
@@ -76,14 +76,11 @@ export const RELEASES_ROUTES: Routes = [
|
|||||||
(m) => m.ReleaseDetailPageComponent,
|
(m) => m.ReleaseDetailPageComponent,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
// Versions merged into Releases as a tab — redirect for bookmarks
|
||||||
{
|
{
|
||||||
path: 'versions',
|
path: 'versions',
|
||||||
title: 'Release Versions',
|
redirectTo: '/releases?tab=versions',
|
||||||
data: { breadcrumb: 'Release Versions', semanticObject: 'version' },
|
pathMatch: 'full' as const,
|
||||||
loadComponent: () =>
|
|
||||||
import('../features/release-orchestrator/releases/release-list/release-list.component').then(
|
|
||||||
(m) => m.ReleaseListComponent,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'new',
|
path: 'new',
|
||||||
|
|||||||
Reference in New Issue
Block a user