Fix approvals table actions, add search, and hide duplicate Versions header

Approvals tab:
- Default gate toggles all OFF (shows all approvals, not just "gated" type)
- Add search input field that filters by release name, version, environment,
  requestedBy, and status
- Approve/Reject buttons now visible for all pending approvals (gate filter
  was hiding them by defaulting to only "gated" type)

Versions tab:
- Hide the ReleaseListComponent's own header (title + page-action-outlet)
  when embedded=true, eliminating the duplicate "New Version" button
- Parent Release Control page already provides the header and dynamic button

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-03-27 18:53:35 +02:00
parent b6862190d0
commit 971869affd
2 changed files with 35 additions and 11 deletions

View File

@@ -24,13 +24,15 @@ import { PageActionOutletComponent } from '../../../../shared/components/page-ac
imports: [RouterModule, StellaFilterChipComponent, PaginationComponent, PageActionOutletComponent],
template: `
<div class="release-list">
<header class="list-header">
<div class="list-header__title">
<h1>Release Versions</h1>
<p class="subtitle">Digest-first release version catalog across standard and hotfix lanes</p>
</div>
<app-page-action-outlet />
</header>
@if (!embedded) {
<header class="list-header">
<div class="list-header__title">
<h1>Release Versions</h1>
<p class="subtitle">Digest-first release version catalog across standard and hotfix lanes</p>
</div>
<app-page-action-outlet />
</header>
}
<div class="filters">
<div class="filter-search">

View File

@@ -173,7 +173,14 @@ function deriveOutcomeIcon(status: string): string {
@if (viewMode() === 'approvals') {
<!-- Approvals tab content -->
<div class="approvals-gate-toggles">
<div class="activity-filters">
<div class="filter-search">
<svg class="filter-search__icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.3-4.3"></path>
</svg>
<input type="text" class="filter-search__input" placeholder="Search approvals..."
[value]="approvalSearchQuery()" (input)="approvalSearchQuery.set($any($event.target).value)" />
</div>
@for (toggle of gateToggles; track toggle.id) {
<button type="button"
class="gate-toggle"
@@ -514,18 +521,33 @@ export class ReleasesActivityComponent implements OnInit, OnDestroy {
{ id: 'security', label: 'Security' },
] as const;
readonly approvalSearchQuery = signal('');
readonly gateToggleState = signal<Record<string, boolean>>({
gated: true,
gated: false,
policy: false,
ops: false,
security: false,
});
readonly filteredApprovals = computed(() => {
let result = this.allApprovals();
const toggles = this.gateToggleState();
const activeGates = Object.entries(toggles).filter(([, v]) => v).map(([k]) => k);
if (activeGates.length === 0) return this.allApprovals();
return this.allApprovals().filter((apr) => activeGates.includes(this.deriveGateType(apr)));
if (activeGates.length > 0) {
result = result.filter((apr) => activeGates.includes(this.deriveGateType(apr)));
}
const q = this.approvalSearchQuery().toLowerCase().trim();
if (q) {
result = result.filter((apr) =>
apr.releaseName.toLowerCase().includes(q) ||
apr.releaseVersion.toLowerCase().includes(q) ||
apr.targetEnvironment.toLowerCase().includes(q) ||
apr.requestedBy.toLowerCase().includes(q) ||
apr.status.toLowerCase().includes(q)
);
}
return result;
});
// Lane filter reads from global context (header toggle)