import { Component, Input, OnInit, inject, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { StatusBadgeComponent } from '../../../shared/components/status-badge/status-badge.component'; import { EmptyStateComponent } from '../../../shared/components/empty-state/empty-state.component'; import { ApprovalService } from '../services/approval.service'; import { ApprovalResponseDto } from '../../../api/models'; @Component({ selector: 'app-approval-history', standalone: true, imports: [ CommonModule, MatCardModule, MatIconModule, MatProgressSpinnerModule, StatusBadgeComponent, EmptyStateComponent, ], template: `

Approval History

@if (loading()) {
} @else if (approvals().length === 0) { } @else {
@for (approval of approvals(); track approval.id) {
{{ getStatusIcon(approval.status) }}
{{ approval.departmentName }}
@if (approval.remarks) {

{{ approval.remarks }}

} @if (approval.rejectionReason) {

Reason: {{ formatReason(approval.rejectionReason) }}

}
{{ approval.updatedAt | date: 'medium' }}
}
}
`, styles: [ ` .approval-history { margin-top: 24px; h3 { margin: 0 0 16px; font-size: 1.125rem; font-weight: 500; } } .loading-container { display: flex; justify-content: center; padding: 32px; } .timeline { position: relative; padding-left: 32px; &::before { content: ''; position: absolute; left: 12px; top: 0; bottom: 0; width: 2px; background-color: #e0e0e0; } } .timeline-item { position: relative; margin-bottom: 16px; &:last-child { margin-bottom: 0; } } .timeline-marker { position: absolute; left: -32px; top: 16px; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background-color: #e0e0e0; z-index: 1; mat-icon { font-size: 14px; width: 14px; height: 14px; color: white; } &.approved { background-color: #4caf50; } &.rejected { background-color: #f44336; } &.pending { background-color: #ff9800; } &.changes { background-color: #2196f3; } } .timeline-content { padding: 16px; } .timeline-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } .department { font-weight: 500; } .remarks { margin: 8px 0; color: rgba(0, 0, 0, 0.7); font-size: 0.875rem; } .rejection-reason { margin: 8px 0; color: #f44336; font-size: 0.875rem; } .timeline-meta { font-size: 0.75rem; color: rgba(0, 0, 0, 0.54); } `, ], }) export class ApprovalHistoryComponent implements OnInit { @Input({ required: true }) requestId!: string; private readonly approvalService = inject(ApprovalService); readonly loading = signal(true); readonly approvals = signal([]); ngOnInit(): void { this.loadHistory(); } private loadHistory(): void { this.approvalService.getApprovalHistory(this.requestId).subscribe({ next: (data) => { this.approvals.set(data); this.loading.set(false); }, error: () => { this.loading.set(false); }, }); } getStatusIcon(status: string): string { switch (status) { case 'APPROVED': return 'check'; case 'REJECTED': return 'close'; case 'CHANGES_REQUESTED': return 'edit'; default: return 'hourglass_empty'; } } getMarkerClass(status: string): string { switch (status) { case 'APPROVED': return 'approved'; case 'REJECTED': return 'rejected'; case 'CHANGES_REQUESTED': return 'changes'; default: return 'pending'; } } formatReason(reason: string): string { return reason.replace(/_/g, ' ').toLowerCase().replace(/^\w/, (c) => c.toUpperCase()); } }