develop #39
|
@ -1,5 +1,13 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
## [0.22.0] - 2025-09-03
|
||||||
|
### Added
|
||||||
|
- Nueva ux a la hora de gestionar tareas programadas.
|
||||||
|
- En modificar imagen git, nueva opcion para "forzar push".
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
- Nuevo manejo de errores en el modulo de ogRepository.
|
||||||
|
|
||||||
|
---
|
||||||
## [0.21.0] - 2025-08-28
|
## [0.21.0] - 2025-08-28
|
||||||
### Added
|
### Added
|
||||||
- Se ha incluido una integracion con el agente, para generar los scripts que se llaman al particionar.
|
- Se ha incluido una integracion con el agente, para generar los scripts que se llaman al particionar.
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<mat-form-field appearance="fill" class="w-full">
|
<mat-form-field appearance="fill" class="w-full">
|
||||||
<mat-label>Repetición</mat-label>
|
<mat-label>Repetición</mat-label>
|
||||||
<mat-select formControlName="recurrenceType">
|
<mat-select formControlName="recurrenceType">
|
||||||
<mat-option *ngFor="let type of recurrenceTypes" [value]="type">{{ type | titlecase }}</mat-option>
|
<mat-option *ngFor="let opt of recurrenceTypes" [value]="opt.value">{{ opt.label }}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,10 @@ export class CreateTaskScheduleComponent implements OnInit{
|
||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
apiUrl: string;
|
apiUrl: string;
|
||||||
recurrenceTypes = ['none', 'custom'];
|
recurrenceTypes = [
|
||||||
|
{ value: 'none', label: 'Sin repetición' },
|
||||||
|
{ value: 'custom', label: 'Repetición personalizada' }
|
||||||
|
];
|
||||||
weekDays: string[] = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
|
weekDays: string[] = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
|
||||||
isSingleDateSelected: boolean = true;
|
isSingleDateSelected: boolean = true;
|
||||||
monthsList: string[] = [
|
monthsList: string[] = [
|
||||||
|
@ -108,12 +111,27 @@ export class CreateTaskScheduleComponent implements OnInit{
|
||||||
}
|
}
|
||||||
|
|
||||||
formatExecutionTime(time: string | Date): string {
|
formatExecutionTime(time: string | Date): string {
|
||||||
const date = (time instanceof Date) ? time : new Date(time);
|
if (typeof time === 'string') {
|
||||||
if (isNaN(date.getTime())) {
|
const hhmmMatch = time.match(/^\d{2}:\d{2}/);
|
||||||
console.error('Invalid execution time:', time);
|
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');
|
||||||
|
return `${hours}:${minutes}`;
|
||||||
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return date.toISOString().substring(11, 16);
|
|
||||||
|
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 '';
|
||||||
}
|
}
|
||||||
|
|
||||||
convertDateToLocalISO(date: Date): string {
|
convertDateToLocalISO(date: Date): string {
|
||||||
|
@ -191,7 +209,7 @@ export class CreateTaskScheduleComponent implements OnInit{
|
||||||
private calculateNextExecutionAndCount(): void {
|
private calculateNextExecutionAndCount(): void {
|
||||||
const recurrence = this.form.get('recurrenceType')?.value;
|
const recurrence = this.form.get('recurrenceType')?.value;
|
||||||
const time = this.form.get('executionTime')?.value;
|
const time = this.form.get('executionTime')?.value;
|
||||||
|
|
||||||
if (recurrence === 'none') {
|
if (recurrence === 'none') {
|
||||||
const execDate = this.form.get('executionDate')?.value;
|
const execDate = this.form.get('executionDate')?.value;
|
||||||
if (execDate && time) {
|
if (execDate && time) {
|
||||||
|
@ -200,31 +218,102 @@ export class CreateTaskScheduleComponent implements OnInit{
|
||||||
this.nextExecutionDate.setHours(parseInt(hours), parseInt(minutes), 0, 0);
|
this.nextExecutionDate.setHours(parseInt(hours), parseInt(minutes), 0, 0);
|
||||||
this.executionCount = 1;
|
this.executionCount = 1;
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
const startDate = this.form.get('recurrenceDetails.initDate')?.value;
|
|
||||||
const endDate = this.form.get('recurrenceDetails.endDate')?.value;
|
|
||||||
const days = Object.keys(this.selectedDays).filter(day => this.selectedDays[day]);
|
|
||||||
const months = Object.keys(this.selectedMonths).filter(month => this.selectedMonths[month]);
|
|
||||||
|
|
||||||
if (startDate && endDate && days.length > 0 && months.length > 0) {
|
|
||||||
// Calcular próxima ejecución (simplificado)
|
|
||||||
this.nextExecutionDate = new Date(startDate);
|
|
||||||
const [hours, minutes] = time.split(':');
|
|
||||||
this.nextExecutionDate.setHours(parseInt(hours), parseInt(minutes), 0, 0);
|
|
||||||
|
|
||||||
// Calcular número aproximado de ejecuciones
|
|
||||||
this.executionCount = this.calculateExecutionCount(startDate, endDate, days.length, months.length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const startDateValue = this.form.get('recurrenceDetails.initDate')?.value;
|
||||||
|
const endDateValue = this.form.get('recurrenceDetails.endDate')?.value;
|
||||||
|
|
||||||
|
const selectedDayNames = Object.keys(this.selectedDays).filter(day => this.selectedDays[day]);
|
||||||
|
const selectedMonthNames = Object.keys(this.selectedMonths).filter(month => this.selectedMonths[month]);
|
||||||
|
|
||||||
|
if (!startDateValue || !endDateValue || selectedDayNames.length === 0 || selectedMonthNames.length === 0) {
|
||||||
|
this.nextExecutionDate = null;
|
||||||
|
this.executionCount = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startDate = new Date(startDateValue);
|
||||||
|
const endDate = new Date(endDateValue);
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateExecutionCount(startDate: Date, endDate: Date, daysCount: number, monthsCount: number): number {
|
private calculateExecutionCountExact(startDate: Date, endDate: Date, selectedDays: Set<number>, selectedMonths: Set<number>): number {
|
||||||
const start = new Date(startDate);
|
const cursor = new Date(startDate);
|
||||||
|
cursor.setHours(0, 0, 0, 0);
|
||||||
const end = new Date(endDate);
|
const end = new Date(endDate);
|
||||||
const daysDiff = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
|
end.setHours(23, 59, 59, 999);
|
||||||
|
|
||||||
// Aproximación: días seleccionados por semana * semanas * meses activos
|
let count = 0;
|
||||||
return Math.ceil((daysCount / 7) * (daysDiff / 7) * (monthsCount / 12));
|
while (cursor <= end) {
|
||||||
|
if (selectedMonths.has(cursor.getMonth()) && selectedDays.has(cursor.getDay())) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
cursor.setDate(cursor.getDate() + 1);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private findNextExecutionDate(startDate: Date, endDate: Date, time: string, selectedDays: Set<number>, selectedMonths: Set<number>): 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 [hours, minutes] = (time || '00:00').split(':');
|
||||||
|
|
||||||
|
const candidate = new Date(startCandidate);
|
||||||
|
while (candidate <= endLimit) {
|
||||||
|
if (selectedMonths.has(candidate.getMonth()) && selectedDays.has(candidate.getDay())) {
|
||||||
|
const candidateDateTime = new Date(candidate);
|
||||||
|
candidateDateTime.setHours(parseInt(hours), parseInt(minutes), 0, 0);
|
||||||
|
if (candidateDateTime >= now) {
|
||||||
|
return candidateDateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
candidate.setDate(candidate.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
formatDate(date: string | Date): string {
|
formatDate(date: string | Date): string {
|
||||||
|
|
|
@ -69,48 +69,287 @@ table {
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-elevation-z8 {
|
.mat-elevation-z8 {
|
||||||
box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.2);
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.paginator-container {
|
.mat-header-cell {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #495057;
|
||||||
|
padding: 16px 8px;
|
||||||
|
border-bottom: 2px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-cell {
|
||||||
|
padding: 16px 8px;
|
||||||
|
border-bottom: 1px solid #f1f3f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-type-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
justify-content: center;
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-spinner {
|
.recurrence-chip {
|
||||||
margin: 0 auto;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subnets-button-row {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px;
|
align-items: center;
|
||||||
|
padding: 7px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
min-width: 200px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recurrence-icon {
|
||||||
|
margin-right: 12px;
|
||||||
|
font-size: 20px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-label {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-details {
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.8;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colores para diferentes tipos de recurrencia */
|
||||||
|
.recurrence-none {
|
||||||
|
background-color: #667eea;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-daily {
|
||||||
|
background-color: #f093fb;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-weekly {
|
||||||
|
background-color: #4facfe;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-monthly {
|
||||||
|
background-color: #43e97b;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-yearly {
|
||||||
|
background-color: #fa709a;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-custom {
|
||||||
|
background-color: #ff9a9e;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos para el campo executionTime */
|
||||||
|
.time-display {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: #667eea;
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-value {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos para el campo nextExecution */
|
||||||
|
.next-execution {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: #4facfe;
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.execution-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos para el campo daysOfWeek */
|
||||||
|
.days-display {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.days-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
.days-icon {
|
||||||
|
margin-right: 6px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
color: #4facfe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.days-count {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.days-chips {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-chip {
|
||||||
|
background-color: #4facfe;
|
||||||
|
color: white;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
min-width: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos para el campo months */
|
||||||
|
.months-display {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.months-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
.months-icon {
|
||||||
|
margin-right: 6px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
color: #43e97b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.months-count {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.months-chips {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.month-chip {
|
||||||
|
background-color: #43e97b;
|
||||||
|
color: white;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
min-width: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos para el campo enabled */
|
||||||
|
.status-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
min-width: 100px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-active {
|
||||||
|
background-color: #43e97b;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-inactive {
|
||||||
|
background-color: #ff9a9e;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos para datos no definidos */
|
||||||
|
.no-data {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-data-text {
|
||||||
|
color: #6c757d;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos para el header de acciones */
|
||||||
.header-actions {
|
.header-actions {
|
||||||
|
margin-bottom: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-bottom: 20px;
|
|
||||||
padding: 16px;
|
|
||||||
background: #f8f9fa;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button {
|
.action-button {
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 12px 24px;
|
transition: all 0.3s ease;
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-button:hover {
|
|
||||||
background: #1565c0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button mat-icon {
|
.action-button mat-icon {
|
||||||
|
@ -119,90 +358,60 @@ mat-spinner {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-button {
|
/* Estilos para el contenedor de búsqueda */
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 12px 24px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-button mat-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
margin: 20px 0;
|
margin-bottom: 20px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-string {
|
.search-string {
|
||||||
|
flex: 1;
|
||||||
min-width: 250px;
|
min-width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilos para los chips de días y meses */
|
/* Estilos para el paginador */
|
||||||
.days-display, .months-display {
|
.paginator-container {
|
||||||
|
margin-top: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
justify-content: center;
|
||||||
gap: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.day-chip, .month-chip {
|
/* Estilos para los botones de acción en la tabla */
|
||||||
background: #e3f2fd;
|
.mat-column-actions .mat-cell {
|
||||||
color: #1976d2;
|
text-align: center;
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 500;
|
|
||||||
border: 1px solid #bbdefb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilos para la próxima ejecución */
|
.mat-column-actions button {
|
||||||
.next-execution {
|
margin: 0 4px;
|
||||||
display: flex;
|
transition: all 0.3s ease;
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
color: #555;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.execution-icon {
|
.mat-column-actions button:hover {
|
||||||
font-size: 18px;
|
transform: scale(1.1);
|
||||||
color: #2196f3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mejoras en la tabla */
|
/* Responsive design */
|
||||||
.mat-table {
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-header-cell {
|
|
||||||
background: #f5f5f5;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-row:hover {
|
|
||||||
background: #f8f9fa;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
.recurrence-chip {
|
||||||
|
min-width: 160px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-label {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recurrence-details {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-string {
|
.search-string {
|
||||||
min-width: auto;
|
min-width: 100%;
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-actions {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-button {
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,24 +44,22 @@
|
||||||
<td mat-cell *matCellDef="let schedule">
|
<td mat-cell *matCellDef="let schedule">
|
||||||
|
|
||||||
<ng-container *ngIf="column.columnDef === 'recurrenceType'">
|
<ng-container *ngIf="column.columnDef === 'recurrenceType'">
|
||||||
<mat-chip style="padding: 10px; margin: 5px;">
|
<div class="recurrence-type-container">
|
||||||
<ng-container *ngIf="column.cell(schedule) === 'none'; else scheduledTemplate">
|
<div class="recurrence-chip" [ngClass]="'recurrence-' + column.cell(schedule).type">
|
||||||
No programado
|
<mat-icon class="recurrence-icon">{{ column.cell(schedule).icon }}</mat-icon>
|
||||||
<div style="font-size: 12px;">
|
<div class="recurrence-content">
|
||||||
{{ schedule.executionDate | date }}
|
<div class="recurrence-label">{{ column.cell(schedule).label }}</div>
|
||||||
|
<div class="recurrence-details">{{ column.cell(schedule).details }}</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</div>
|
||||||
<ng-template #scheduledTemplate>
|
</div>
|
||||||
Programado
|
|
||||||
<div style="font-size: 12px;">
|
|
||||||
{{ schedule.recurrenceDetails?.initDate | date }} → {{ schedule.recurrenceDetails?.endDate | date}}
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</mat-chip>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="column.columnDef === 'executionTime'">
|
<ng-container *ngIf="column.columnDef === 'executionTime'">
|
||||||
{{ schedule.executionTime | date: 'HH:mm' }}
|
<div class="time-display">
|
||||||
|
<mat-icon class="time-icon">schedule</mat-icon>
|
||||||
|
<span class="time-value">{{ schedule.executionTime | date: 'HH:mm' }}</span>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="column.columnDef === 'nextExecution'">
|
<ng-container *ngIf="column.columnDef === 'nextExecution'">
|
||||||
|
@ -72,16 +70,26 @@
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="column.columnDef === 'daysOfWeek'">
|
<ng-container *ngIf="column.columnDef === 'daysOfWeek'">
|
||||||
<div class="days-display">
|
<div class="days-display" *ngIf="column.cell(schedule)">
|
||||||
<span *ngFor="let day of schedule.recurrenceDetails?.daysOfWeek"
|
<div class="days-chips">
|
||||||
class="day-chip">{{ day | slice:0:3 }}</span>
|
<span *ngFor="let day of column.cell(schedule).days"
|
||||||
|
class="day-chip">{{ day }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="no-data" *ngIf="!column.cell(schedule)">
|
||||||
|
<span class="no-data-text">No definido</span>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="column.columnDef === 'months'">
|
<ng-container *ngIf="column.columnDef === 'months'">
|
||||||
<div class="months-display">
|
<div class="months-display" *ngIf="column.cell(schedule)">
|
||||||
<span *ngFor="let month of schedule.recurrenceDetails?.months"
|
<div class="months-chips">
|
||||||
class="month-chip">{{ month | slice:0:3 }}</span>
|
<span *ngFor="let month of column.cell(schedule).months"
|
||||||
|
class="month-chip">{{ month }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="no-data" *ngIf="!column.cell(schedule)">
|
||||||
|
<span class="no-data-text">No definido</span>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -92,14 +100,12 @@
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="column.columnDef === 'enabled'">
|
<ng-container *ngIf="column.columnDef === 'enabled'">
|
||||||
<mat-chip [color]="schedule.enabled ? 'accent' : 'warn'" selected>
|
<div class="status-container">
|
||||||
<ng-container *ngIf="schedule.enabled">
|
<div class="status-indicator" [ngClass]="schedule.enabled ? 'status-active' : 'status-inactive'">
|
||||||
Activo
|
<mat-icon class="status-icon">{{ schedule.enabled ? 'check_circle' : 'cancel' }}</mat-icon>
|
||||||
</ng-container>
|
<span class="status-text">{{ schedule.enabled ? 'Activo' : 'Inactivo' }}</span>
|
||||||
<ng-container *ngIf="!schedule.enabled">
|
</div>
|
||||||
Inactivo
|
</div>
|
||||||
</ng-container>
|
|
||||||
</mat-chip>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
@ -27,11 +27,11 @@ export class ShowTaskScheduleComponent implements OnInit{
|
||||||
|
|
||||||
columns = [
|
columns = [
|
||||||
{ columnDef: 'id', header: 'ID', cell: (schedule: any) => schedule.id },
|
{ columnDef: 'id', header: 'ID', cell: (schedule: any) => schedule.id },
|
||||||
{ columnDef: 'recurrenceType', header: 'Recurrencia', cell: (schedule: any) => schedule.recurrenceType },
|
{ 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.datePipe.transform(schedule.executionTime, 'HH:mm') },
|
||||||
{ columnDef: 'nextExecution', header: 'Próxima ejecución', cell: (schedule: any) => this.calculateNextExecution(schedule) },
|
{ columnDef: 'nextExecution', header: 'Próxima ejecución', cell: (schedule: any) => this.calculateNextExecution(schedule) },
|
||||||
{ columnDef: 'daysOfWeek', header: 'Días de la semana', cell: (schedule: any) => schedule.recurrenceDetails?.daysOfWeek || [] },
|
{ columnDef: 'daysOfWeek', header: 'Días de la semana', cell: (schedule: any) => this.formatDaysOfWeek(schedule.recurrenceDetails?.daysOfWeek) },
|
||||||
{ columnDef: 'months', header: 'Meses', cell: (schedule: any) => schedule.recurrenceDetails?.months || [] },
|
{ columnDef: 'months', header: 'Meses', cell: (schedule: any) => this.formatMonths(schedule.recurrenceDetails?.months) },
|
||||||
{ columnDef: 'enabled', header: 'Estado', cell: (schedule: any) => schedule.enabled }
|
{ columnDef: 'enabled', header: 'Estado', cell: (schedule: any) => schedule.enabled }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -121,23 +121,49 @@ export class ShowTaskScheduleComponent implements OnInit{
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (schedule.recurrenceType === 'none') {
|
if (schedule.recurrenceType === 'none') {
|
||||||
if (schedule.executionDate) {
|
if (schedule.executionDate && schedule.executionTime) {
|
||||||
const execDate = new Date(schedule.executionDate);
|
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();
|
const now = new Date();
|
||||||
|
if (fullExecutionDateTime < now) {
|
||||||
if (execDate < now) {
|
|
||||||
return 'Ya ejecutada';
|
return 'Ya ejecutada';
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.datePipe.transform(execDate, 'dd/MM/yyyy HH:mm') || 'Fecha inválida';
|
return this.datePipe.transform(fullExecutionDateTime, 'dd/MM/yyyy HH:mm') || 'Fecha inválida';
|
||||||
}
|
}
|
||||||
return 'Sin fecha';
|
return 'Sin fecha/hora definida';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Personalizada (u otros tipos con detalles): calcular siguiente ejecución real
|
||||||
if (schedule.recurrenceDetails) {
|
if (schedule.recurrenceDetails) {
|
||||||
const days = schedule.recurrenceDetails.daysOfWeek?.join(', ') || 'Todos';
|
const startDate = schedule.recurrenceDetails.initDate ? new Date(schedule.recurrenceDetails.initDate) : null;
|
||||||
const months = schedule.recurrenceDetails.months?.join(', ') || 'Todos';
|
const endDate = schedule.recurrenceDetails.endDate ? new Date(schedule.recurrenceDetails.endDate) : null;
|
||||||
return `${days} - ${months}`;
|
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<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 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';
|
return 'Configuración incompleta';
|
||||||
|
@ -146,6 +172,76 @@ export class ShowTaskScheduleComponent implements OnInit{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<number>, selectedMonths: Set<number>): 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 {
|
onPageChange(event: any): void {
|
||||||
this.page = event.pageIndex;
|
this.page = event.pageIndex;
|
||||||
this.itemsPerPage = event.pageSize;
|
this.itemsPerPage = event.pageSize;
|
||||||
|
@ -155,4 +251,105 @@ export class ShowTaskScheduleComponent implements OnInit{
|
||||||
onNoClick(): void {
|
onNoClick(): void {
|
||||||
this.dialogRef.close(false);
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,8 @@ export class ShowTaskScriptComponent implements OnInit{
|
||||||
{ columnDef: 'id', header: 'ID', cell: (client: any) => client.id },
|
{ columnDef: 'id', header: 'ID', cell: (client: any) => client.id },
|
||||||
{ columnDef: 'order', header: 'Orden', cell: (client: any) => client.order },
|
{ columnDef: 'order', header: 'Orden', cell: (client: any) => client.order },
|
||||||
{ columnDef: 'content', header: 'Script', cell: (client: any) => client.content },
|
{ columnDef: 'content', header: 'Script', cell: (client: any) => client.content },
|
||||||
{ columnDef: 'type', header: 'Type', cell: (client: any) => client.type },
|
{ columnDef: 'type', header: 'Tipo', cell: (client: any) => client.type },
|
||||||
{ columnDef: 'parameters', header: 'Parameters', cell: (client: any) => client.parameters },
|
{ columnDef: 'parameters', header: 'Parámetros', cell: (client: any) => client.parameters },
|
||||||
];
|
];
|
||||||
|
|
||||||
displayedColumns: string[] = ['id', 'order', 'type', 'parameters', 'content', 'actions'];
|
displayedColumns: string[] = ['id', 'order', 'type', 'parameters', 'content', 'actions'];
|
||||||
|
|
|
@ -124,6 +124,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Checkbox para forzar push cuando estamos en modo modificar imagen git -->
|
||||||
|
<div class="force-push-container" *ngIf="imageType === 'git' && hasValidGitData">
|
||||||
|
<mat-checkbox [(ngModel)]="forcePush" class="force-push-checkbox">
|
||||||
|
Opción 'Forzar push'
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="repository-header" *ngIf="!hasValidGitData">
|
<div class="repository-header" *ngIf="!hasValidGitData">
|
||||||
<button *ngIf="imageType === 'git'"
|
<button *ngIf="imageType === 'git'"
|
||||||
class="create-repository-button"
|
class="create-repository-button"
|
||||||
|
|
|
@ -56,6 +56,7 @@ export class CreateClientImageComponent implements OnInit{
|
||||||
isDestinationBranchEditable: boolean = false;
|
isDestinationBranchEditable: boolean = false;
|
||||||
repositoryNotFound: boolean = false;
|
repositoryNotFound: boolean = false;
|
||||||
newlyCreatedRepository: any = null;
|
newlyCreatedRepository: any = null;
|
||||||
|
forcePush: boolean = false; // Nueva propiedad para el checkbox de forzar push
|
||||||
|
|
||||||
get hasValidGitData(): boolean {
|
get hasValidGitData(): boolean {
|
||||||
return this.gitData && this.gitData.repo && this.gitData.branch;
|
return this.gitData && this.gitData.repo && this.gitData.branch;
|
||||||
|
@ -406,6 +407,7 @@ export class CreateClientImageComponent implements OnInit{
|
||||||
gitRepository: gitRepoName,
|
gitRepository: gitRepoName,
|
||||||
originalBranch: originBranch,
|
originalBranch: originBranch,
|
||||||
destinationBranch: this.destinationBranch,
|
destinationBranch: this.destinationBranch,
|
||||||
|
options: this.forcePush,
|
||||||
queue: result
|
queue: result
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -641,15 +643,12 @@ export class CreateClientImageComponent implements OnInit{
|
||||||
openScheduleModal(): void {
|
openScheduleModal(): void {
|
||||||
let scope = 'clients';
|
let scope = 'clients';
|
||||||
|
|
||||||
// Verificar que tenemos la información del cliente
|
|
||||||
if (!this.client) {
|
if (!this.client) {
|
||||||
this.toastService.error('No se ha cargado la información del cliente');
|
this.toastService.error('No se ha cargado la información del cliente');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crear un array con el objeto cliente completo
|
|
||||||
let selectedClients = [this.client];
|
let selectedClients = [this.client];
|
||||||
console.log(selectedClients);
|
|
||||||
|
|
||||||
const dialogRef = this.dialog.open(CreateTaskComponent, {
|
const dialogRef = this.dialog.open(CreateTaskComponent, {
|
||||||
width: '800px',
|
width: '800px',
|
||||||
|
@ -657,14 +656,13 @@ export class CreateClientImageComponent implements OnInit{
|
||||||
scope: scope,
|
scope: scope,
|
||||||
selectedClients: selectedClients,
|
selectedClients: selectedClients,
|
||||||
organizationalUnit: this.client['@id'],
|
organizationalUnit: this.client['@id'],
|
||||||
source: 'create-image',
|
source: 'assistant',
|
||||||
runScriptContext: null
|
runScriptContext: null
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((result: any) => {
|
dialogRef.afterClosed().subscribe((result: any) => {
|
||||||
if (result) {
|
if (result) {
|
||||||
// Verificar que tenemos la partición seleccionada
|
|
||||||
if (!this.selectedPartition) {
|
if (!this.selectedPartition) {
|
||||||
this.toastService.error('Debe seleccionar una partición');
|
this.toastService.error('Debe seleccionar una partición');
|
||||||
return;
|
return;
|
||||||
|
@ -678,7 +676,8 @@ export class CreateClientImageComponent implements OnInit{
|
||||||
action: this.monolithicAction,
|
action: this.monolithicAction,
|
||||||
diskNumber: this.selectedPartition.diskNumber,
|
diskNumber: this.selectedPartition.diskNumber,
|
||||||
partitionNumber: this.selectedPartition.partitionNumber,
|
partitionNumber: this.selectedPartition.partitionNumber,
|
||||||
imageName: this.monolithicAction === 'create' ? this.name : this.selectedImage?.name
|
imageName: this.monolithicAction === 'create' ? this.name : this.selectedImage?.name,
|
||||||
|
imageUuid: this.monolithicAction === 'update' ? this.selectedImage?.uuid : null
|
||||||
};
|
};
|
||||||
} else if (this.imageType === 'git') {
|
} else if (this.imageType === 'git') {
|
||||||
payload = {
|
payload = {
|
||||||
|
|
|
@ -48,6 +48,6 @@ export class ConvertImageToVirtualComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.dialogRef.close(true);
|
this.dialogRef.close(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,6 @@ export class ConvertImageComponent implements OnInit{
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.dialogRef.close(true);
|
this.dialogRef.close(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,6 @@ export class EditImageComponent implements OnInit{
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,6 @@ export class ImportImageComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.dialogRef.close(true);
|
this.dialogRef.close(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,8 +84,10 @@ export class RepositoriesComponent implements OnInit {
|
||||||
width: '600px'
|
width: '600px'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(() => {
|
dialogRef.afterClosed().subscribe((result) => {
|
||||||
this.search();
|
if (result) {
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +110,11 @@ export class RepositoriesComponent implements OnInit {
|
||||||
this.dialog.open(ManageRepositoryComponent, {
|
this.dialog.open(ManageRepositoryComponent, {
|
||||||
width: '600px',
|
width: '600px',
|
||||||
data: repository['@id']
|
data: repository['@id']
|
||||||
}).afterClosed().subscribe(() => this.search());
|
}).afterClosed().subscribe((result) => {
|
||||||
|
if (result) {
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteRepository(event: MouseEvent,command: any): void {
|
deleteRepository(event: MouseEvent,command: any): void {
|
||||||
|
@ -141,7 +147,9 @@ export class RepositoriesComponent implements OnInit {
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
this.search();
|
if (result) {
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +163,9 @@ export class RepositoriesComponent implements OnInit {
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
this.search();
|
if (result) {
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-dialog-content {
|
||||||
|
height: 100% !important;
|
||||||
|
max-height: 100vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
.header-container {
|
.header-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -144,7 +144,6 @@ export class ShowMonoliticImagesComponent implements OnInit {
|
||||||
|
|
||||||
showImageInfo(event: MouseEvent, image:any) {
|
showImageInfo(event: MouseEvent, image:any) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.loading = true;
|
|
||||||
this.loadImageAlert(image).subscribe(
|
this.loadImageAlert(image).subscribe(
|
||||||
response => {
|
response => {
|
||||||
this.alertMessage = response;
|
this.alertMessage = response;
|
||||||
|
@ -193,7 +192,6 @@ export class ShowMonoliticImagesComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleAction(image: any, action:string): void {
|
toggleAction(image: any, action:string): void {
|
||||||
this.loading = true;
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'get-aux':
|
case 'get-aux':
|
||||||
this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/create-aux-files`, {}).subscribe({
|
this.http.post(`${this.baseUrl}/image-image-repositories/server/${image.uuid}/create-aux-files`, {}).subscribe({
|
||||||
|
|
Loading…
Reference in New Issue