import { Component, OnInit, ElementRef, ViewChild, ChangeDetectorRef, Inject, PLATFORM_ID } from '@angular/core'; import { CommonModule, isPlatformBrowser } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { HttpErrorResponse } from '@angular/common/http'; import { CustomSelectComponent } from '../../components/custom-select/custom-select'; import { HistoricoService, AuditLogDto, AuditChangeType, HistoricoQuery } from '../../services/historico.service'; interface SelectOption { value: string; label: string; } @Component({ selector: 'app-historico', standalone: true, imports: [CommonModule, FormsModule, CustomSelectComponent], templateUrl: './historico.html', styleUrls: ['./historico.scss'], }) export class Historico implements OnInit { @ViewChild('successToast', { static: false }) successToast!: ElementRef; logs: AuditLogDto[] = []; loading = false; error = false; errorMsg = ''; toastMessage = ''; expandedLogId: string | null = null; page = 1; pageSize = 10; pageSizeOptions = [10, 20, 50, 100]; total = 0; filterPageName = ''; filterAction = ''; filterUserId = ''; filterSearch = ''; dateFrom = ''; dateTo = ''; readonly pageOptions: SelectOption[] = [ { value: '', label: 'Todas as páginas' }, { value: 'Geral', label: 'Geral' }, { value: 'Mureg', label: 'Mureg' }, { value: 'Faturamento', label: 'Faturamento' }, { value: 'Parcelamentos', label: 'Parcelamentos' }, { value: 'Dados e Usuários', label: 'Dados PF/PJ' }, { value: 'Vigência', label: 'Vigência' }, { value: 'Chips Virgens e Recebidos', label: 'Chips Virgens e Recebidos' }, { value: 'Troca de número', label: 'Troca de número' }, ]; readonly actionOptions: SelectOption[] = [ { value: '', label: 'Todas as ações' }, { value: 'CREATE', label: 'Criação' }, { value: 'UPDATE', label: 'Atualização' }, { value: 'DELETE', label: 'Exclusão' }, ]; private searchTimer: any = null; constructor( private historicoService: HistoricoService, private cdr: ChangeDetectorRef, @Inject(PLATFORM_ID) private platformId: object ) {} ngOnInit(): void { this.fetch(1); } refresh(): void { this.fetch(1); } applyFilters(): void { this.page = 1; this.fetch(); } clearFilters(): void { this.filterPageName = ''; this.filterAction = ''; this.filterUserId = ''; this.filterSearch = ''; this.dateFrom = ''; this.dateTo = ''; this.page = 1; this.fetch(); } clearSearch(): void { this.filterSearch = ''; this.page = 1; this.fetch(); } onSearchChange(): void { if (this.searchTimer) clearTimeout(this.searchTimer); this.searchTimer = setTimeout(() => { this.page = 1; this.fetch(); }, 300); } onPageSizeChange(): void { this.page = 1; this.fetch(); } goToPage(p: number): void { this.page = Math.max(1, Math.min(this.totalPages, p)); this.fetch(); } get totalPages(): number { return Math.ceil((this.total || 0) / this.pageSize) || 1; } get pageNumbers(): number[] { const total = this.totalPages; const current = this.page; const max = 5; let start = Math.max(1, current - 2); let end = Math.min(total, start + (max - 1)); start = Math.max(1, end - (max - 1)); const pages: number[] = []; for (let i = start; i <= end; i++) pages.push(i); return pages; } get pageStart(): number { return this.total === 0 ? 0 : (this.page - 1) * this.pageSize + 1; } get pageEnd(): number { if (this.total === 0) return 0; return Math.min(this.page * this.pageSize, this.total); } toggleDetails(log: AuditLogDto, event?: Event): void { if (event) event.stopPropagation(); this.expandedLogId = this.expandedLogId === log.id ? null : log.id; } formatDateTime(value?: string | null): string { if (!value) return '-'; const d = new Date(value); if (isNaN(d.getTime())) return '-'; return d.toLocaleString('pt-BR'); } displayUserName(log: AuditLogDto): string { const name = (log.userName || '').trim(); return name ? name : 'SISTEMA'; } displayEntity(log: AuditLogDto): string { const label = (log.entityLabel || '').trim(); if (label) return label; return log.entityName || '-'; } formatAction(action?: string | null): string { const value = (action || '').toUpperCase(); if (!value) return '-'; if (value === 'CREATE') return 'Criação'; if (value === 'UPDATE') return 'Atualização'; if (value === 'DELETE') return 'Exclusão'; return 'Outro'; } actionClass(action?: string | null): string { const value = (action || '').toUpperCase(); if (value === 'CREATE') return 'action-create'; if (value === 'UPDATE') return 'action-update'; if (value === 'DELETE') return 'action-delete'; return 'action-default'; } changeTypeLabel(type?: AuditChangeType | string | null): string { if (!type) return 'Alterado'; if (type === 'added') return 'Adicionado'; if (type === 'removed') return 'Removido'; return 'Alterado'; } changeTypeClass(type?: AuditChangeType | string | null): string { if (type === 'added') return 'change-added'; if (type === 'removed') return 'change-removed'; if (type === 'modified') return 'change-modified'; return 'change-modified'; } formatChangeValue(value?: string | null): string { if (value === undefined || value === null || value === '') return '-'; return String(value); } trackByLog(_: number, log: AuditLogDto): string { return log.id; } trackByField(_: number, change: { field: string }): string { return change.field; } private fetch(goToPage?: number): void { if (goToPage) this.page = goToPage; this.loading = true; this.error = false; this.errorMsg = ''; this.expandedLogId = null; const query: HistoricoQuery = { page: this.page, pageSize: this.pageSize, pageName: this.filterPageName || undefined, action: this.filterAction || undefined, userId: this.filterUserId?.trim() || undefined, search: this.filterSearch?.trim() || undefined, dateFrom: this.toIsoDate(this.dateFrom, false) || undefined, dateTo: this.toIsoDate(this.dateTo, true) || undefined, }; this.historicoService.list(query).subscribe({ next: (res) => { this.logs = res.items || []; this.total = res.total || 0; this.page = res.page || this.page; this.pageSize = res.pageSize || this.pageSize; this.loading = false; }, error: (err: HttpErrorResponse) => { this.error = true; if (err?.status === 403) { this.errorMsg = 'Acesso restrito.'; } else { this.errorMsg = 'Erro ao carregar histórico. Tente novamente.'; } this.loading = false; }, }); } private toIsoDate(value: string, endOfDay: boolean): string | null { if (!value) return null; const time = endOfDay ? '23:59:59' : '00:00:00'; const date = new Date(`${value}T${time}`); if (isNaN(date.getTime())) return null; return date.toISOString(); } private async showToast(message: string) { if (!isPlatformBrowser(this.platformId)) return; this.toastMessage = message; this.cdr.detectChanges(); if (!this.successToast?.nativeElement) return; try { const bs = await import('bootstrap'); const toastInstance = bs.Toast.getOrCreateInstance(this.successToast.nativeElement, { autohide: true, delay: 3000 }); toastInstance.show(); } catch (error) { console.error(error); } } }