import { Component, OnInit, OnDestroy, inject, signal, DestroyRef } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { CommonModule } from '@angular/common'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { MatCardModule } from '@angular/material/card'; import { MatTableModule } from '@angular/material/table'; import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatChipsModule } from '@angular/material/chips'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { PageHeaderComponent } from '../../../shared/components/page-header/page-header.component'; import { EmptyStateComponent } from '../../../shared/components/empty-state/empty-state.component'; import { WebhookService } from '../services/webhook.service'; import { WebhookLogEntryDto } from '../../../api/models'; @Component({ selector: 'app-webhook-logs', standalone: true, imports: [ CommonModule, RouterModule, MatCardModule, MatTableModule, MatPaginatorModule, MatButtonModule, MatIconModule, MatChipsModule, MatProgressSpinnerModule, PageHeaderComponent, EmptyStateComponent, ], template: `
@if (loading()) {
} @else if (logs().length === 0) { } @else {
Timestamp {{ row.timestamp | date: 'medium' }} Event {{ formatEvent(row.event) }} Status {{ row.statusCode }} Response Time {{ row.responseTime }}ms Retries {{ row.retryCount }}
}
`, styles: [ ` .loading-container { display: flex; justify-content: center; padding: 48px; } table { width: 100%; } .status-code { font-family: monospace; font-weight: 500; padding: 4px 8px; border-radius: 4px; background-color: #ffcdd2; color: #c62828; &.success { background-color: #c8e6c9; color: #2e7d32; } } `, ], }) export class WebhookLogsComponent implements OnInit, OnDestroy { private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); private readonly webhookService = inject(WebhookService); private readonly destroyRef = inject(DestroyRef); readonly loading = signal(true); readonly hasError = signal(false); readonly logs = signal([]); readonly totalItems = signal(0); readonly pageSize = signal(20); readonly pageIndex = signal(0); readonly displayedColumns = ['timestamp', 'event', 'status', 'responseTime', 'retries']; private webhookId: string | null = null; ngOnInit(): void { this.webhookId = this.route.snapshot.paramMap.get('id'); if (!this.webhookId) { this.router.navigate(['/webhooks']); return; } this.loadLogs(); } loadLogs(): void { if (!this.webhookId) return; this.loading.set(true); this.hasError.set(false); this.webhookService .getWebhookLogs(this.webhookId, this.pageIndex() + 1, this.pageSize()) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: (response) => { this.logs.set(response?.data ?? []); this.totalItems.set(response?.total ?? 0); this.loading.set(false); }, error: () => { this.hasError.set(true); this.loading.set(false); }, }); } onPageChange(event: PageEvent): void { this.pageIndex.set(event.pageIndex); this.pageSize.set(event.pageSize); this.loadLogs(); } formatEvent(event: string): string { return event?.replace(/_/g, ' ').toLowerCase() ?? ''; } isSuccess(statusCode: number): boolean { return statusCode >= 200 && statusCode < 300; } ngOnDestroy(): void { // Cleanup handled by DestroyRef/takeUntilDestroyed } /** Retry loading data - clears error state */ retryLoad(): void { this.loadLogs(); } }