import {Component, Inject, OnInit} from '@angular/core'; import {MatTableDataSource} from "@angular/material/table"; import {Client} from "../../../groups/model/model"; import {ToastrService} from "ngx-toastr"; import {HttpClient} from "@angular/common/http"; import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog"; import {ConfigService} from "@services/config.service"; import {DeleteModalComponent} from "../../../../shared/delete_modal/delete-modal/delete-modal.component"; import {CreateTaskScheduleComponent} from "../create-task-schedule/create-task-schedule.component"; import {DatePipe} from "@angular/common"; @Component({ selector: 'app-show-task-schedule', templateUrl: './show-task-schedule.component.html', styleUrl: './show-task-schedule.component.css' }) export class ShowTaskScheduleComponent implements OnInit{ baseUrl: string; dataSource = new MatTableDataSource([]); length = 0; itemsPerPage: number = 10; pageSizeOptions: number[] = [5, 10, 20]; page = 0; loading: boolean = false; filters: { [key: string]: string } = {}; datePipe: DatePipe = new DatePipe('es-ES'); columns = [ { columnDef: 'id', header: 'ID', cell: (schedule: any) => schedule.id }, { columnDef: 'recurrenceType', header: 'Tipo de Programación', cell: (schedule: any) => this.getRecurrenceTypeDisplay(schedule) }, { columnDef: 'executionTime', header: 'Hora de ejecución', cell: (schedule: any) => this.datePipe.transform(schedule.executionTime, 'HH:mm') }, { columnDef: 'nextExecution', header: 'Próxima ejecución', cell: (schedule: any) => this.calculateNextExecution(schedule) }, { columnDef: 'daysOfWeek', header: 'Días de la semana', cell: (schedule: any) => this.formatDaysOfWeek(schedule.recurrenceDetails?.daysOfWeek) }, { columnDef: 'months', header: 'Meses', cell: (schedule: any) => this.formatMonths(schedule.recurrenceDetails?.months) }, { columnDef: 'enabled', header: 'Estado', cell: (schedule: any) => schedule.enabled } ]; displayedColumns: string[] = ['id', 'recurrenceType', 'executionTime', 'nextExecution', 'daysOfWeek', 'months', 'enabled', 'actions']; constructor( private toastService: ToastrService, private http: HttpClient, public dialogRef: MatDialogRef, public dialog: MatDialog, private configService: ConfigService, @Inject(MAT_DIALOG_DATA) public data: any ) { this.baseUrl = this.configService.apiUrl; } ngOnInit(): void { if (this.data) { this.loadData(); } } loadData() { this.loading = true; this.http.get(`${this.baseUrl}/command-task-schedules?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}&commandTask.id=${this.data.commandTask?.id}`, { params: this.filters }).subscribe( (data) => { this.dataSource.data = data['hydra:member']; this.length = data['hydra:totalItems']; this.loading = false; }, (error) => { this.loading = false; this.toastService.error('Error al cargar las programaciones'); } ); } createNewSchedule(): void { this.dialog.open(CreateTaskScheduleComponent, { width: '800px', data: { task: this.data.commandTask } }).afterClosed().subscribe(result => { if (result) { this.loadData(); this.toastService.success('Programación creada correctamente'); } }); } editSchedule(schedule: any): void { this.dialog.open(CreateTaskScheduleComponent, { width: '800px', data: { schedule: schedule, task: this.data.commandTask } }).afterClosed().subscribe(result => { if (result) { this.loadData(); this.toastService.success('Programación actualizada correctamente'); } }); } deleteSchedule(schedule: any): void { const dialogRef = this.dialog.open(DeleteModalComponent, { width: '300px', data: { name: 'programación de tarea' } }); dialogRef.afterClosed().subscribe(result => { if (result) { this.http.delete(`${this.baseUrl}${schedule['@id']}`).subscribe( () => { this.toastService.success('Programación eliminada correctamente'); this.loadData(); }, (error) => { this.toastService.error(error.error['hydra:description'] || 'Error al eliminar la programación'); } ); } }) } calculateNextExecution(schedule: any): string { if (!schedule.enabled) { return 'Deshabilitada'; } try { if (schedule.recurrenceType === 'none') { if (schedule.executionDate && schedule.executionTime) { const executionDate = new Date(schedule.executionDate); const [hours, minutes] = this.parseExecutionTime(schedule.executionTime); const fullExecutionDateTime = new Date( executionDate.getFullYear(), executionDate.getMonth(), executionDate.getDate(), hours, minutes, 0, 0 ); const now = new Date(); if (fullExecutionDateTime < now) { return 'Ya ejecutada'; } return this.datePipe.transform(fullExecutionDateTime, 'dd/MM/yyyy HH:mm') || 'Fecha inválida'; } return 'Sin fecha/hora definida'; } // Personalizada (u otros tipos con detalles): calcular siguiente ejecución real if (schedule.recurrenceDetails) { const startDate = schedule.recurrenceDetails.initDate ? new Date(schedule.recurrenceDetails.initDate) : null; const endDate = schedule.recurrenceDetails.endDate ? new Date(schedule.recurrenceDetails.endDate) : null; const days = Array.isArray(schedule.recurrenceDetails.daysOfWeek) ? schedule.recurrenceDetails.daysOfWeek : []; const months = Array.isArray(schedule.recurrenceDetails.months) ? schedule.recurrenceDetails.months : []; if (!startDate || !endDate || days.length === 0 || months.length === 0) { return 'Configuración incompleta'; } const selectedDayIndices = new Set(days.map((d: string) => this.getDayIndexFromName(d))); const selectedMonthIndices = new Set(months.map((m: string) => this.getMonthIndexFromName(m))); const [hours, minutes] = this.parseExecutionTime(schedule.executionTime); const next = this.findNextExecutionDate(startDate, endDate, hours, minutes, selectedDayIndices, selectedMonthIndices); if (next) { return this.datePipe.transform(next, 'dd/MM/yyyy HH:mm') || 'Fecha inválida'; } return 'No hay próximas ejecuciones'; } return 'Configuración incompleta'; } catch (error) { return 'Error en cálculo'; } } private parseExecutionTime(executionTime: any): [number, number] { if (typeof executionTime === 'string') { const match = executionTime.match(/^(\d{2}):(\d{2})/); if (match) { return [parseInt(match[1], 10), parseInt(match[2], 10)]; } const parsed = new Date(executionTime); if (!isNaN(parsed.getTime())) { return [parsed.getHours(), parsed.getMinutes()]; } return [0, 0]; } if (executionTime instanceof Date && !isNaN(executionTime.getTime())) { return [executionTime.getHours(), executionTime.getMinutes()]; } return [0, 0]; } private findNextExecutionDate(startDate: Date, endDate: Date, hours: number, minutes: number, selectedDays: Set, selectedMonths: Set): Date | null { const now = new Date(); const startCandidate = new Date(Math.max(new Date(startDate).setHours(0, 0, 0, 0), new Date(now).setHours(0, 0, 0, 0))); const endLimit = new Date(endDate); endLimit.setHours(23, 59, 59, 999); const cursor = new Date(startCandidate); while (cursor <= endLimit) { if (selectedMonths.has(cursor.getMonth()) && selectedDays.has(cursor.getDay())) { const candidateDateTime = new Date(cursor); candidateDateTime.setHours(hours, minutes, 0, 0); if (candidateDateTime >= now) { return candidateDateTime; } } cursor.setDate(cursor.getDate() + 1); } return null; } private getDayIndexFromName(dayName: string): number { const mapping: { [key: string]: number } = { sunday: 0, monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6 }; return mapping[dayName?.toLowerCase?.()] ?? -1; } private getMonthIndexFromName(monthName: string): number { const mapping: { [key: string]: number } = { january: 0, february: 1, march: 2, april: 3, may: 4, june: 5, july: 6, august: 7, september: 8, october: 9, november: 10, december: 11 }; return mapping[monthName?.toLowerCase?.()] ?? -1; } onPageChange(event: any): void { this.page = event.pageIndex; this.itemsPerPage = event.pageSize; this.loadData(); } onNoClick(): void { this.dialogRef.close(false); } getRecurrenceTypeDisplay(schedule: any): any { if (schedule.recurrenceType === 'none') { return { type: 'none', label: 'Ejecución única', icon: 'event', color: 'primary', details: schedule.executionDate ? `Fecha: ${this.datePipe.transform(schedule.executionDate, 'dd/MM/yyyy')}` : 'Sin fecha definida' }; } else if (schedule.recurrenceType === 'daily') { return { type: 'daily', label: 'Diaria', icon: 'repeat', color: 'accent', details: 'Todos los días' }; } else if (schedule.recurrenceType === 'weekly') { return { type: 'weekly', label: 'Semanal', icon: 'view_week', color: 'accent', details: schedule.recurrenceDetails?.daysOfWeek?.length > 0 ? `Días: ${schedule.recurrenceDetails.daysOfWeek.join(', ')}` : 'Todos los días' }; } else if (schedule.recurrenceType === 'monthly') { return { type: 'monthly', label: 'Mensual', icon: 'calendar_month', color: 'accent', details: schedule.recurrenceDetails?.months?.length > 0 ? `Meses: ${schedule.recurrenceDetails.months.join(', ')}` : 'Todos los meses' }; } else if (schedule.recurrenceType === 'yearly') { return { type: 'yearly', label: 'Anual', icon: 'event_note', color: 'accent', details: 'Una vez al año' }; } else { return { type: 'custom', label: 'Personalizada', icon: 'settings', color: 'warn', details: 'Configuración especial' }; } } formatDaysOfWeek(days: string[]): any { if (!days || days.length === 0) return null; const dayMap: { [key: string]: string } = { 'monday': 'Lun', 'tuesday': 'Mar', 'wednesday': 'Mié', 'thursday': 'Jue', 'friday': 'Vie', 'saturday': 'Sáb', 'sunday': 'Dom' }; return { days: days.map(day => dayMap[day.toLowerCase()] || day), count: days.length }; } formatMonths(months: string[]): any { if (!months || months.length === 0) return null; const monthMap: { [key: string]: string } = { 'january': 'Ene', 'february': 'Feb', 'march': 'Mar', 'april': 'Abr', 'may': 'May', 'june': 'Jun', 'july': 'Jul', 'august': 'Ago', 'september': 'Sep', 'october': 'Oct', 'november': 'Nov', 'december': 'Dic' }; return { months: months.map(month => monthMap[month.toLowerCase()] || month), count: months.length }; } }