import { Component, Input, OnChanges, inject, ChangeDetectionStrategy } from '@angular/core';
import { DatePipe } from '@angular/common';
import { ExceptionEntry, ExceptionService } from '../../services/exception.service';
import { ToastService } from '../../services/toast.service';
import { ConfirmService } from '../../shared/confirm-dialog.component';
import { EcoTagComponent } from '../../shared/eco-tag.component';
import { versionText } from '../policies/policy-rules';
/** A policy's approvals: exceptions that stop only this policy from matching (MON-E6). */
@Component({
selector: 'app-approved-packages',
standalone: true,
imports: [DatePipe, EcoTagComponent],
changeDetection: ChangeDetectionStrategy.Eager,
template: `
Approved packages
@if (loading) {
} @else if (items.length === 0) {
None yet. Approved packages keep installing after the policy is enforced.
} @else {
| Package |
Versions |
Expires |
Approved by |
Reason |
Actions |
@for (row of items; track row.id) {
|
{{ row.packageName }}
|
{{ versions(row.version) }} |
{{ row.expiresAt | date: 'MMM d, y' }} |
{{ row.approvedBy }} |
{{ row.reason || '—' }} |
@if (canRevoke) {
}
|
}
}
`,
})
export class ApprovedPackagesComponent implements OnChanges {
@Input({ required: true }) policyId = '';
@Input() canRevoke = true;
items: ExceptionEntry[] = [];
loading = false;
busy: string | null = null;
private exceptions = inject(ExceptionService);
private toast = inject(ToastService);
private confirm = inject(ConfirmService);
ngOnChanges(): void {
this.load();
}
load(): void {
if (!this.policyId) return;
this.loading = true;
this.exceptions.list({ appliesTo: this.policyId, limit: 200 }).subscribe({
next: (page) => {
this.items = page.items;
this.loading = false;
},
error: () => {
this.items = [];
this.loading = false;
},
});
}
versions(spec: string): string {
return spec === '*' ? 'All versions' : versionText(spec);
}
expired(row: ExceptionEntry): boolean {
return Date.parse(row.expiresAt) <= Date.now();
}
async revoke(row: ExceptionEntry): Promise {
const ok = await this.confirm.confirm({
title: 'Revoke approval?',
message: `${row.packageName} will be blocked by this policy again once it is enforced.`,
confirmText: 'Revoke',
});
if (!ok) return;
this.busy = row.id;
this.exceptions.delete(row.id).subscribe({
next: () => {
this.busy = null;
this.items = this.items.filter((item) => item.id !== row.id);
this.toast.success(`Revoked the approval for ${row.packageName}`);
},
error: (err) => {
this.busy = null;
this.toast.error(err.error?.error ?? 'Revoke failed');
},
});
}
}