331 lines
10 KiB
TypeScript
331 lines
10 KiB
TypeScript
import { inject } from '@angular/core';
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
import { Component, OnInit, Inject, ChangeDetectorRef } from '@angular/core';
|
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
|
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
|
import { DatePipe } from '@angular/common';
|
|
import { ConfigService } from '@services/config.service';
|
|
import { ToastrService } from 'ngx-toastr';
|
|
import { TranslationService } from '@services/translation.service';
|
|
import { OutputDialogComponent } from '../output-dialog/output-dialog.component';
|
|
import { InputDialogComponent } from '../input-dialog/input-dialog.component';
|
|
import { JoyrideService } from 'ngx-joyride';
|
|
import { FormControl } from '@angular/forms';
|
|
import { Observable } from 'rxjs';
|
|
import { COMMAND_TYPES } from 'src/app/shared/constants/command-types';
|
|
import { DeleteModalComponent } from 'src/app/shared/delete_modal/delete-modal/delete-modal.component';
|
|
|
|
@Component({
|
|
selector: 'app-client-pending-tasks',
|
|
templateUrl: './client-pending-tasks.component.html',
|
|
styleUrls: ['./client-pending-tasks.component.css']
|
|
})
|
|
export class ClientPendingTasksComponent implements OnInit {
|
|
public translate = inject(TranslateService);
|
|
baseUrl: string;
|
|
mercureUrl: string;
|
|
traces: 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');
|
|
filters: { [key: string]: any } = {};
|
|
filteredCommands!: Observable<any[]>;
|
|
commandControl = new FormControl();
|
|
columns = [
|
|
{
|
|
columnDef: 'id',
|
|
header: 'labelId',
|
|
cell: (trace: any) => `${trace.id}`,
|
|
},
|
|
{
|
|
columnDef: 'command',
|
|
header: 'command',
|
|
cell: (trace: any) => trace.command
|
|
},
|
|
{
|
|
columnDef: 'status',
|
|
header: 'state',
|
|
cell: (trace: any) => trace.status
|
|
},
|
|
{
|
|
columnDef: 'executedAt',
|
|
header: 'execution',
|
|
cell: (trace: any) => this.datePipe.transform(trace.executedAt, 'dd/MM/yyyy HH:mm:ss'),
|
|
},
|
|
{
|
|
columnDef: 'finishedAt',
|
|
header: 'ending',
|
|
cell: (trace: any) => this.datePipe.transform(trace.finishedAt, 'dd/MM/yyyy HH:mm:ss'),
|
|
},
|
|
];
|
|
displayedColumns = [...this.columns.map(column => column.columnDef), 'information'];
|
|
|
|
filteredCommands2 = Object.keys(COMMAND_TYPES).map(key => ({
|
|
name: key,
|
|
value: key,
|
|
label: COMMAND_TYPES[key]
|
|
}));
|
|
|
|
today = new Date();
|
|
|
|
constructor(
|
|
private http: HttpClient,
|
|
@Inject(MAT_DIALOG_DATA) public data: { client: any, isOrganizationalUnit?: boolean },
|
|
private joyrideService: JoyrideService,
|
|
private dialog: MatDialog,
|
|
private cdr: ChangeDetectorRef,
|
|
private configService: ConfigService,
|
|
private toastService: ToastrService,
|
|
private translationService: TranslationService,
|
|
public dialogRef: MatDialogRef<ClientPendingTasksComponent>
|
|
) {
|
|
this.baseUrl = this.configService.apiUrl;
|
|
this.mercureUrl = this.configService.mercureUrl;
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
// Si es una unidad organizativa, agregar columna de cliente
|
|
if (this.data.isOrganizationalUnit) {
|
|
this.columns.splice(2, 0, {
|
|
columnDef: 'client',
|
|
header: 'customer',
|
|
cell: (trace: any) => trace.client?.name || 'N/A'
|
|
});
|
|
this.displayedColumns = [...this.columns.map(column => column.columnDef), 'information'];
|
|
}
|
|
|
|
this.loadTraces();
|
|
|
|
const eventSource = new EventSource(`${this.mercureUrl}?topic=`
|
|
+ encodeURIComponent(`traces`));
|
|
|
|
eventSource.onmessage = (event) => {
|
|
const data = JSON.parse(event.data);
|
|
if (data && data['@id']) {
|
|
this.updateTraceFromEvent(data['@id'], data);
|
|
}
|
|
}
|
|
}
|
|
|
|
private updateTraceFromEvent(traceId: string, data: any): void {
|
|
const traceIndex = this.traces.findIndex(trace => trace['@id'] === traceId);
|
|
if (traceIndex !== -1) {
|
|
const updatedTraces = [...this.traces];
|
|
|
|
updatedTraces[traceIndex] = {
|
|
...updatedTraces[traceIndex],
|
|
...data
|
|
};
|
|
|
|
this.traces = updatedTraces;
|
|
this.cdr.detectChanges();
|
|
}
|
|
}
|
|
|
|
loadTraces(): void {
|
|
this.loading = true;
|
|
|
|
let params = new HttpParams()
|
|
.set('status', 'pending')
|
|
.set('page', (this.page + 1).toString())
|
|
.set('itemsPerPage', this.itemsPerPage.toString());
|
|
|
|
// Si es una unidad organizativa, obtener las trazas de todos sus clientes
|
|
if (this.data.isOrganizationalUnit && this.data.client?.clients) {
|
|
const clientIds = this.data.client.clients.map((client: any) => client.id);
|
|
if (clientIds.length > 0) {
|
|
// Agregar cada ID de cliente como un parámetro separado
|
|
clientIds.forEach((id: number) => {
|
|
params = params.append('client.id[]', id.toString());
|
|
});
|
|
} else {
|
|
this.traces = [];
|
|
this.length = 0;
|
|
this.loading = false;
|
|
return;
|
|
}
|
|
} else {
|
|
// Cliente individual
|
|
const clientId = this.data.client?.id;
|
|
if (!clientId) {
|
|
this.loading = false;
|
|
return;
|
|
}
|
|
params = params.set('client.id', clientId.toString());
|
|
}
|
|
|
|
const url = `${this.baseUrl}/traces`;
|
|
|
|
console.log('URL con parámetros:', url, params.toString());
|
|
|
|
this.http.get<any>(url, { params }).subscribe(
|
|
(data) => {
|
|
this.traces = data['hydra:member'];
|
|
this.length = data['hydra:totalItems'];
|
|
this.loading = false;
|
|
},
|
|
(error) => {
|
|
console.error(this.translate.instant('errorFetchingTraces'), error);
|
|
this.loading = false;
|
|
}
|
|
);
|
|
}
|
|
|
|
onOptionCommandSelected(selectedCommand: any): void {
|
|
this.filters['command'] = selectedCommand.name;
|
|
this.loadTraces();
|
|
}
|
|
|
|
onOptionStatusSelected(selectedStatus: any): void {
|
|
this.filters['status'] = selectedStatus;
|
|
this.loadTraces();
|
|
}
|
|
|
|
openInputModal(inputData: any): void {
|
|
this.dialog.open(InputDialogComponent, {
|
|
width: '70vw',
|
|
height: '60vh',
|
|
data: { input: inputData }
|
|
});
|
|
}
|
|
|
|
cancelTrace(trace: any): void {
|
|
if (trace.status !== 'pending' && trace.status !== 'in-progress') {
|
|
this.toastService.warning(this.translate.instant('onlyTracesCanBeCanceled'), 'Advertencia');
|
|
return;
|
|
}
|
|
|
|
this.dialog.open(DeleteModalComponent, {
|
|
width: '300px',
|
|
data: { name: trace.jobId },
|
|
}).afterClosed().subscribe((result) => {
|
|
if (result) {
|
|
if (trace.status === 'in-progress') {
|
|
this.http.post(`${this.baseUrl}/traces/${trace['@id']}/kill-job`, {
|
|
jobId: trace.jobId
|
|
}).subscribe({
|
|
next: () => {
|
|
this.toastService.success(this.translate.instant('taskCanceledSuccessfully'));
|
|
this.loadTraces();
|
|
},
|
|
error: (error) => {
|
|
this.toastService.error(error.error['hydra:description'] || 'Error al cancelar la tarea');
|
|
console.error(this.translate.instant('errorCancelingInprogressTrace'), error);
|
|
}
|
|
});
|
|
} else {
|
|
this.http.post(`${this.baseUrl}/traces/server/${trace.uuid}/cancel`, {}).subscribe({
|
|
next: () => {
|
|
this.toastService.success(this.translate.instant('taskCanceledSuccessfully'));
|
|
this.loadTraces();
|
|
},
|
|
error: (error) => {
|
|
this.toastService.error(error.error['hydra:description'] || 'Error al cancelar la tarea');
|
|
console.error(this.translate.instant('errorCancelingPendingTrace'), error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
resetFilters(clientSearchCommandInput: any, clientSearchStatusInput: any) {
|
|
clientSearchCommandInput.value = '';
|
|
clientSearchStatusInput.value = '';
|
|
this.loadTraces();
|
|
}
|
|
|
|
openOutputModal(outputData: any): void {
|
|
this.dialog.open(OutputDialogComponent, {
|
|
width: '70vw',
|
|
data: { input: outputData }
|
|
});
|
|
}
|
|
|
|
onPageChange(event: any): void {
|
|
this.page = event.pageIndex;
|
|
this.itemsPerPage = event.pageSize;
|
|
this.length = event.length;
|
|
this.loadTraces();
|
|
}
|
|
|
|
translateCommand(command: string): string {
|
|
return this.translationService.getCommandTranslation(command);
|
|
}
|
|
|
|
clearCommandFilter(event: Event, clientSearchCommandInput: any): void {
|
|
clientSearchCommandInput.value = '';
|
|
this.loadTraces();
|
|
}
|
|
|
|
clearStatusFilter(event: Event, clientSearchStatusInput: any): void {
|
|
clientSearchStatusInput.value = '';
|
|
this.loadTraces();
|
|
}
|
|
|
|
onDateFilterChange(): void {
|
|
this.loadTraces();
|
|
}
|
|
|
|
iniciarTour(): void {
|
|
this.joyrideService.startTour({
|
|
steps: [
|
|
'tracesTitleStep',
|
|
'resetFiltersStep',
|
|
'filtersStep',
|
|
'tracesProgressStep',
|
|
'tracesInfoStep',
|
|
'paginationStep'
|
|
],
|
|
showPrevButton: true,
|
|
themeColor: '#3f51b5'
|
|
});
|
|
}
|
|
|
|
close(): void {
|
|
this.dialogRef.close();
|
|
}
|
|
|
|
clearAllActions(): void {
|
|
if (this.traces.length === 0) {
|
|
this.toastService.warning(this.translate.instant('noActionsToClean'));
|
|
return;
|
|
}
|
|
|
|
// Mostrar confirmación antes de proceder
|
|
this.dialog.open(DeleteModalComponent, {
|
|
width: '400px',
|
|
data: {
|
|
name: `Todas las acciones mostradas (${this.traces.length} acciones)`,
|
|
message : this.translate.instant('areYouSureYouWant')
|
|
},
|
|
}).afterClosed().subscribe((result) => {
|
|
if (result) {
|
|
this.loading = true;
|
|
|
|
// Enviar array de traces en el body
|
|
const tracesToCancel = this.traces.map((trace: any) => trace['@id']);
|
|
|
|
this.http.post(`${this.baseUrl}/traces/cancel-multiple`, {
|
|
traces: tracesToCancel
|
|
}).subscribe({
|
|
next: () => {
|
|
this.toastService.success(`Se han cancelado ${this.traces.length} acciones correctamente`);
|
|
this.loadTraces(); // Recargar las trazas
|
|
},
|
|
error: (error) => {
|
|
console.error(this.translate.instant('errorCancelingActions1'), error);
|
|
this.toastService.error(this.translate.instant('errorCancelingActions'));
|
|
this.loadTraces(); // Recargar las trazas para mostrar el estado actual
|
|
},
|
|
complete: () => {
|
|
this.loading = false;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|