import {ChangeDetectorRef, Component, OnInit} from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, forkJoin } from 'rxjs'; import { FormControl } from '@angular/forms'; import { map, startWith } from 'rxjs/operators'; import { DatePipe } from '@angular/common'; import { JoyrideService } from 'ngx-joyride'; import { MatDialog } from "@angular/material/dialog"; import { InputDialogComponent } from "./input-dialog/input-dialog.component"; import { ProgressBarMode, MatProgressBarModule } from '@angular/material/progress-bar'; import {DeleteModalComponent} from "../../../../shared/delete_modal/delete-modal/delete-modal.component"; import {ToastrService} from "ngx-toastr"; @Component({ selector: 'app-task-logs', templateUrl: './task-logs.component.html', styleUrls: ['./task-logs.component.css'] }) export class TaskLogsComponent implements OnInit { baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; mercureUrl: string = import.meta.env.NG_APP_OGCORE_MERCURE_BASE_URL; traces: any[] = []; groupedTraces: any[] = []; commands: any[] = []; clients: any[] = []; length: number = 0; itemsPerPage: number = 20; page: number = 0; loading: boolean = true; pageSizeOptions: number[] = [10, 20, 30, 50]; datePipe: DatePipe = new DatePipe('es-ES'); mode: ProgressBarMode = 'buffer'; progress = 0; bufferValue = 0; columns = [ { columnDef: 'id', header: 'ID', cell: (trace: any) => `${trace.id}`, }, { columnDef: 'command', header: 'Comando', cell: (trace: any) => `${trace.command}` }, { columnDef: 'client', header: 'Client', cell: (trace: any) => `${trace.client?.name}` }, { columnDef: 'status', header: 'Estado', cell: (trace: any) => `${trace.status}` }, { columnDef: 'jobId', header: 'Hilo de trabajo', cell: (trace: any) => `${trace.jobId}` }, { columnDef: 'input', header: 'Input', cell: (trace: any) => `${trace.input}` }, { columnDef: 'output', header: 'Logs', cell: (trace: any) => `${trace.output}` }, { columnDef: 'executedAt', header: 'Programación de ejecución', cell: (trace: any) => `${this.datePipe.transform(trace.executedAt, 'dd/MM/yyyy hh:mm:ss')}`, }, { columnDef: 'finishedAt', header: 'Finalización', cell: (trace: any) => `${this.datePipe.transform(trace.finishedAt, 'dd/MM/yyyy hh:mm:ss')}`, }, ]; displayedColumns = [...this.columns.map(column => column.columnDef)]; filters: { [key: string]: string } = {}; filteredClients!: Observable; clientControl = new FormControl(); filteredCommands!: Observable; commandControl = new FormControl(); constructor(private http: HttpClient, private joyrideService: JoyrideService, private dialog: MatDialog, private cdr: ChangeDetectorRef, private toastService: ToastrService ) { } ngOnInit(): void { this.loadTraces(); this.loadCommands(); //this.loadClients(); this.filteredCommands = this.commandControl.valueChanges.pipe( startWith(''), map(value => (typeof value === 'string' ? value : value?.name)), map(name => (name ? this._filterCommands(name) : this.commands.slice())) ); this.filteredClients = this.clientControl.valueChanges.pipe( startWith(''), map(value => (typeof value === 'string' ? value : value?.name)), map(name => (name ? this._filterClients(name) : this.clients.slice())) ); const eventSource = new EventSource(`${this.mercureUrl}?topic=` + encodeURIComponent(`traces`)); eventSource.onmessage = (event) => { const data = JSON.parse(event.data); if (data && data['@id']) { this.updateTracesStatus(data['@id'], data.status, data.progress); } } } private updateTracesStatus(clientUuid: string, newStatus: string, progress: Number): void { const traceIndex = this.traces.findIndex(trace => trace['@id'] === clientUuid); if (traceIndex !== -1) { const updatedTraces = [...this.traces]; updatedTraces[traceIndex] = { ...updatedTraces[traceIndex], status: newStatus, progress: progress }; this.traces = updatedTraces; this.cdr.detectChanges(); console.log(`Estado actualizado para la traza ${clientUuid}: ${newStatus}`); } else { console.warn(`Traza con UUID ${clientUuid} no encontrado en la lista.`); } } private _filterClients(name: string): any[] { const filterValue = name.toLowerCase(); return this.clients.filter(client => client.name.toLowerCase().includes(filterValue)); } private _filterCommands(name: string): any[] { const filterValue = name.toLowerCase(); return this.commands.filter(command => command.name.toLowerCase().includes(filterValue)); } displayFnClient(client: any): string { return client && client.name ? client.name : ''; } displayFnCommand(command: any): string { return command && command.name ? command.name : ''; } onOptionCommandSelected(selectedCommand: any): void { this.filters['command.id'] = selectedCommand.id; this.loadTraces(); } onOptionClientSelected(selectedClient: any): void { this.filters['client.id'] = selectedClient.id; this.loadTraces(); } openInputModal(inputData: any): void { this.dialog.open(InputDialogComponent, { width: '700px', data: { input: inputData } }); } cancelTrace(trace: any): void { this.dialog.open(DeleteModalComponent, { width: '300px', data: { name: trace.jobId }, }).afterClosed().subscribe((result) => { if (result) { this.http.post(`${this.baseUrl}/traces/server/${trace.uuid}/cancel`, {}).subscribe({ next: () => { this.toastService.success('Transmision de imagen cancelada'); this.loadTraces(); }, error: (error) => { this.toastService.error(error.error['hydra:description']); console.error(error.error['hydra:description']); } }); } }); } loadTraces(): void { this.loading = true; const url = `${this.baseUrl}/traces?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`; const params = { ...this.filters }; if (params['status'] === undefined) { delete params['status']; } this.http.get(url, { params }).subscribe( (data) => { this.traces = data['hydra:member']; this.length = data['hydra:totalItems']; this.groupedTraces = this.groupByCommandId(this.traces); this.loading = false; }, (error) => { console.error('Error fetching traces', error); this.loading = false; } ); } loadCommands() { this.loading = true; this.http.get(`${this.baseUrl}/commands?&page=1&itemsPerPage=10000`).subscribe( response => { this.commands = response['hydra:member']; console.log(this.commands); this.loading = false; }, error => { console.error('Error fetching commands:', error); this.loading = false; } ); } loadClients() { this.loading = true; this.http.get(`${this.baseUrl}/clients?&page=1&itemsPerPage=10000`).subscribe( response => { const clientIds = response['hydra:member'].map((client: any) => client['@id']); const clientDetailsRequests: Observable[] = clientIds.map((id: string) => this.http.get(`${this.baseUrl}${id}`)); forkJoin(clientDetailsRequests).subscribe( (clients: any[]) => { this.clients = clients; this.loading = false; }, (error: any) => { console.error('Error fetching client details:', error); this.loading = false; } ); }, (error: any) => { console.error('Error fetching clients:', error); this.loading = false; } ); } resetFilters() { this.loading = true; this.filters = {}; this.loadTraces(); } groupByCommandId(traces: any[]): any[] { const grouped: { [key: string]: any[] } = {}; traces.forEach(trace => { const commandId = trace.command.id; if (!grouped[commandId]) { grouped[commandId] = []; } grouped[commandId].push(trace); }); return Object.keys(grouped).map(key => ({ commandId: key, traces: grouped[key] })); } onPageChange(event: any): void { this.page = event.pageIndex; this.itemsPerPage = event.pageSize; this.length = event.length; this.loadTraces(); } iniciarTour(): void { this.joyrideService.startTour({ steps: [ 'titleStep', 'resetFiltersStep', 'clientSelectStep', 'commandSelectStep', 'tableStep', 'paginationStep' ], showPrevButton: true, themeColor: '#3f51b5' }); } }