develop #43
|
@ -1,4 +1,13 @@
|
|||
# Changelog
|
||||
## [0.23.1] - 2025-09-09
|
||||
### Fixed
|
||||
- Se ha corregido el formato de la fecha a la hora de cambiar las tareas programadas
|
||||
|
||||
---
|
||||
## [0.23.0] - 2025-09-08
|
||||
### Added
|
||||
- Se ha añadido la funcionalidad para crear backups de repositorios GIT.
|
||||
|
||||
## [0.22.2] - 2025-09-05
|
||||
### Improved
|
||||
- Se ha mejorado la UX en el asistente de ejecurcion de scripts.
|
||||
|
|
|
@ -28,10 +28,21 @@
|
|||
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let task">
|
||||
<ng-container *ngIf="column.columnDef !== 'management'">
|
||||
<ng-container *ngIf="column.columnDef !== 'management' && column.columnDef !== 'scope' && column.columnDef !== 'organizationalUnit'">
|
||||
{{ column.cell(task) }}
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="column.columnDef === 'scope'">
|
||||
<mat-chip [color]="getScopeColor(task.scope)" selected>
|
||||
{{ getScopeTranslation(task.scope) }}
|
||||
</mat-chip>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="column.columnDef === 'organizationalUnit'">
|
||||
<span *ngIf="task.scope !== 'clients'">{{ task.organizationalUnit?.name || '' }}</span>
|
||||
<span *ngIf="task.scope === 'clients'">-</span>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="column.columnDef === 'management'">
|
||||
<button class="action-button schedule-btn" (click)="openShowScheduleDialog(task)">
|
||||
<mat-icon>schedule</mat-icon>
|
||||
|
|
|
@ -28,13 +28,14 @@ export class CommandsTaskComponent implements OnInit {
|
|||
columns = [
|
||||
{ columnDef: 'id', header: 'ID', cell: (task: any) => task.id },
|
||||
{ columnDef: 'name', header: 'Nombre de tarea', cell: (task: any) => task.name },
|
||||
{ columnDef: 'organizationalUnit', header: 'Ámbito', cell: (task: any) => task.scope },
|
||||
{ columnDef: 'scope', header: 'Ámbito', cell: (task: any) => task.scope },
|
||||
{ columnDef: 'organizationalUnit', header: 'Unidad Organizativa', cell: (task: any) => task.organizationalUnit?.name || '' },
|
||||
{ columnDef: 'management', header: 'Gestiones', cell: (task: any) => task.schedules },
|
||||
{ columnDef: 'nextExecution', header: 'Próxima ejecución', cell: (task: any) => this.datePipe.transform(task.nextExecution, 'dd/MM/yyyy HH:mm:ss') },
|
||||
{ columnDef: 'createdBy', header: 'Creado por', cell: (task: any) => task.createdBy },
|
||||
];
|
||||
|
||||
displayedColumns: string[] = ['id', 'name', 'organizationalUnit', 'management', 'nextExecution', 'createdBy', 'actions'];
|
||||
displayedColumns: string[] = ['id', 'name', 'scope', 'management', 'nextExecution', 'createdBy', 'actions'];
|
||||
loading: boolean = false;
|
||||
private apiUrl: string;
|
||||
|
||||
|
@ -60,6 +61,7 @@ export class CommandsTaskComponent implements OnInit {
|
|||
(data) => {
|
||||
this.tasks = data['hydra:member'];
|
||||
this.length = data['hydra:totalItems'];
|
||||
this.updateDisplayedColumns();
|
||||
this.loading = false;
|
||||
},
|
||||
(error) => {
|
||||
|
@ -69,6 +71,38 @@ export class CommandsTaskComponent implements OnInit {
|
|||
);
|
||||
}
|
||||
|
||||
updateDisplayedColumns(): void {
|
||||
const hasNonClientTasks = this.tasks.some(task => task.scope !== 'clients');
|
||||
|
||||
if (hasNonClientTasks) {
|
||||
this.displayedColumns = ['id', 'name', 'scope', 'organizationalUnit', 'management', 'nextExecution', 'createdBy', 'actions'];
|
||||
} else {
|
||||
this.displayedColumns = ['id', 'name', 'scope', 'management', 'nextExecution', 'createdBy', 'actions'];
|
||||
}
|
||||
}
|
||||
|
||||
getScopeTranslation(scope: string): string {
|
||||
const translations: { [key: string]: string } = {
|
||||
'organizational-unit': 'Unidad Organizativa',
|
||||
'classrooms-group': 'Grupo de aulas',
|
||||
'classroom': 'Aula',
|
||||
'clients-group': 'Grupo de clientes',
|
||||
'clients': 'Clientes'
|
||||
};
|
||||
return translations[scope] || scope;
|
||||
}
|
||||
|
||||
getScopeColor(scope: string): string {
|
||||
const colors: { [key: string]: string } = {
|
||||
'organizational-unit': 'primary',
|
||||
'classrooms-group': 'accent',
|
||||
'classroom': 'warn',
|
||||
'clients-group': 'accent',
|
||||
'clients': 'primary'
|
||||
};
|
||||
return colors[scope] || 'primary';
|
||||
}
|
||||
|
||||
openCreateTaskModal(): void {
|
||||
this.dialog.open(CreateTaskComponent, {
|
||||
width: '800px',
|
||||
|
|
|
@ -116,10 +116,17 @@ export class CreateTaskScheduleComponent implements OnInit{
|
|||
if (hhmmMatch) {
|
||||
return hhmmMatch[0];
|
||||
}
|
||||
|
||||
const parsed = new Date(time);
|
||||
if (!isNaN(parsed.getTime())) {
|
||||
const hours = String(parsed.getHours()).padStart(2, '0');
|
||||
const minutes = String(parsed.getMinutes()).padStart(2, '0');
|
||||
const utcHours = parsed.getUTCHours();
|
||||
const utcMinutes = parsed.getUTCMinutes();
|
||||
|
||||
const currentDate = new Date();
|
||||
currentDate.setUTCHours(utcHours, utcMinutes, 0, 0);
|
||||
|
||||
const hours = String(currentDate.getHours()).padStart(2, '0');
|
||||
const minutes = String(currentDate.getMinutes()).padStart(2, '0');
|
||||
return `${hours}:${minutes}`;
|
||||
}
|
||||
return '';
|
||||
|
@ -140,6 +147,23 @@ export class CreateTaskScheduleComponent implements OnInit{
|
|||
return adjustedDate.toISOString();
|
||||
}
|
||||
|
||||
convertTimeToUTC(timeString: string): string {
|
||||
const today = new Date();
|
||||
const [hours, minutes] = timeString.split(':');
|
||||
|
||||
const localDateTime = new Date(
|
||||
today.getFullYear(),
|
||||
today.getMonth(),
|
||||
today.getDate(),
|
||||
parseInt(hours, 10),
|
||||
parseInt(minutes, 10),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
return localDateTime.toISOString();
|
||||
}
|
||||
|
||||
|
||||
onSubmit() {
|
||||
const formData = this.form.value;
|
||||
|
@ -147,7 +171,7 @@ export class CreateTaskScheduleComponent implements OnInit{
|
|||
const payload: any = {
|
||||
commandTask: this.data.task['@id'],
|
||||
executionDate: formData.recurrenceType === 'none' ? this.convertDateToLocalISO(formData.executionDate) : null,
|
||||
executionTime: formData.executionTime,
|
||||
executionTime: this.convertTimeToUTC(formData.executionTime),
|
||||
recurrenceType: formData.recurrenceType,
|
||||
recurrenceDetails: {
|
||||
...formData.recurrenceDetails,
|
||||
|
@ -196,7 +220,6 @@ export class CreateTaskScheduleComponent implements OnInit{
|
|||
const days = Object.keys(this.selectedDays).filter(day => this.selectedDays[day]);
|
||||
const months = Object.keys(this.selectedMonths).filter(month => this.selectedMonths[month]);
|
||||
|
||||
// Calcular próxima ejecución y conteo
|
||||
this.calculateNextExecutionAndCount();
|
||||
|
||||
if (recurrence === 'none') {
|
||||
|
@ -239,10 +262,8 @@ export class CreateTaskScheduleComponent implements OnInit{
|
|||
const selectedDayIndices = new Set<number>(selectedDayNames.map(name => this.getDayIndexFromName(name)));
|
||||
const selectedMonthIndices = new Set<number>(selectedMonthNames.map(name => this.getMonthIndexFromName(name)));
|
||||
|
||||
// Conteo exacto de ejecuciones en el rango [startDate, endDate]
|
||||
this.executionCount = this.calculateExecutionCountExact(startDate, endDate, selectedDayIndices, selectedMonthIndices);
|
||||
|
||||
// Próxima ejecución >= ahora dentro del rango y criterios
|
||||
this.nextExecutionDate = this.findNextExecutionDate(startDate, endDate, time, selectedDayIndices, selectedMonthIndices);
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
<ng-container *ngIf="column.columnDef === 'executionTime'">
|
||||
<div class="time-display">
|
||||
<mat-icon class="time-icon">schedule</mat-icon>
|
||||
<span class="time-value">{{ schedule.executionTime | date: 'HH:mm' }}</span>
|
||||
<span class="time-value">{{ column.cell(schedule) }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ export class ShowTaskScheduleComponent implements OnInit{
|
|||
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: 'executionTime', header: 'Hora de ejecución', cell: (schedule: any) => this.formatExecutionTimeForDisplay(schedule.executionTime) },
|
||||
{ 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) },
|
||||
|
@ -123,13 +123,22 @@ export class ShowTaskScheduleComponent implements OnInit{
|
|||
if (schedule.recurrenceType === 'none') {
|
||||
if (schedule.executionDate && schedule.executionTime) {
|
||||
const executionDate = new Date(schedule.executionDate);
|
||||
const [hours, minutes] = this.parseExecutionTime(schedule.executionTime);
|
||||
|
||||
const executionTimeDate = new Date(schedule.executionTime);
|
||||
const utcHours = executionTimeDate.getUTCHours();
|
||||
const utcMinutes = executionTimeDate.getUTCMinutes();
|
||||
|
||||
const tempDate = new Date();
|
||||
tempDate.setUTCHours(utcHours, utcMinutes, 0, 0);
|
||||
const localHours = tempDate.getHours();
|
||||
const localMinutes = tempDate.getMinutes();
|
||||
|
||||
const fullExecutionDateTime = new Date(
|
||||
executionDate.getFullYear(),
|
||||
executionDate.getMonth(),
|
||||
executionDate.getDate(),
|
||||
hours,
|
||||
minutes,
|
||||
localHours,
|
||||
localMinutes,
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
@ -144,7 +153,6 @@ export class ShowTaskScheduleComponent implements OnInit{
|
|||
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;
|
||||
|
@ -157,7 +165,15 @@ export class ShowTaskScheduleComponent implements OnInit{
|
|||
|
||||
const selectedDayIndices = new Set<number>(days.map((d: string) => this.getDayIndexFromName(d)));
|
||||
const selectedMonthIndices = new Set<number>(months.map((m: string) => this.getMonthIndexFromName(m)));
|
||||
const [hours, minutes] = this.parseExecutionTime(schedule.executionTime);
|
||||
|
||||
const executionTimeDate = new Date(schedule.executionTime);
|
||||
const utcHours = executionTimeDate.getUTCHours();
|
||||
const utcMinutes = executionTimeDate.getUTCMinutes();
|
||||
|
||||
const tempDate = new Date();
|
||||
tempDate.setUTCHours(utcHours, utcMinutes, 0, 0);
|
||||
const hours = tempDate.getHours();
|
||||
const minutes = tempDate.getMinutes();
|
||||
|
||||
const next = this.findNextExecutionDate(startDate, endDate, hours, minutes, selectedDayIndices, selectedMonthIndices);
|
||||
if (next) {
|
||||
|
@ -172,6 +188,37 @@ export class ShowTaskScheduleComponent implements OnInit{
|
|||
}
|
||||
}
|
||||
|
||||
formatExecutionTimeForDisplay(time: string | Date): string {
|
||||
if (typeof time === 'string') {
|
||||
const hhmmMatch = time.match(/^\d{2}:\d{2}/);
|
||||
if (hhmmMatch) {
|
||||
return hhmmMatch[0];
|
||||
}
|
||||
|
||||
const parsed = new Date(time);
|
||||
if (!isNaN(parsed.getTime())) {
|
||||
const utcHours = parsed.getUTCHours();
|
||||
const utcMinutes = parsed.getUTCMinutes();
|
||||
|
||||
const currentDate = new Date();
|
||||
currentDate.setUTCHours(utcHours, utcMinutes, 0, 0);
|
||||
|
||||
const hours = String(currentDate.getHours()).padStart(2, '0');
|
||||
const minutes = String(currentDate.getMinutes()).padStart(2, '0');
|
||||
return `${hours}:${minutes}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
if (time instanceof Date && !isNaN(time.getTime())) {
|
||||
const hours = String(time.getHours()).padStart(2, '0');
|
||||
const minutes = String(time.getMinutes()).padStart(2, '0');
|
||||
return `${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private parseExecutionTime(executionTime: any): [number, number] {
|
||||
if (typeof executionTime === 'string') {
|
||||
const match = executionTime.match(/^(\d{2}):(\d{2})/);
|
||||
|
@ -180,7 +227,13 @@ export class ShowTaskScheduleComponent implements OnInit{
|
|||
}
|
||||
const parsed = new Date(executionTime);
|
||||
if (!isNaN(parsed.getTime())) {
|
||||
return [parsed.getHours(), parsed.getMinutes()];
|
||||
const utcHours = parsed.getUTCHours();
|
||||
const utcMinutes = parsed.getUTCMinutes();
|
||||
|
||||
const currentDate = new Date();
|
||||
currentDate.setUTCHours(utcHours, utcMinutes, 0, 0);
|
||||
|
||||
return [currentDate.getHours(), currentDate.getMinutes()];
|
||||
}
|
||||
return [0, 0];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue