From d234cdb74c15b2824c58233f5f118accffa0a74d Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 20 Aug 2025 09:25:10 +0200 Subject: [PATCH 1/5] refs #2613. Captured busy client status --- .../client-task-logs/client-task-logs.component.css | 7 +++++++ .../client-task-logs/client-task-logs.component.html | 7 +++++-- .../client-task-logs/client-task-logs.component.ts | 1 - .../app/components/task-logs/task-logs.component.css | 7 +++++++ .../app/components/task-logs/task-logs.component.html | 10 +++++++++- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/ogWebconsole/src/app/components/task-logs/client-task-logs/client-task-logs.component.css b/ogWebconsole/src/app/components/task-logs/client-task-logs/client-task-logs.component.css index 51bb4d8..7e21142 100644 --- a/ogWebconsole/src/app/components/task-logs/client-task-logs/client-task-logs.component.css +++ b/ogWebconsole/src/app/components/task-logs/client-task-logs/client-task-logs.component.css @@ -341,6 +341,12 @@ table { font-weight: 500; } +.chip-busy { + background-color: #ff8c00 !important; + color: white !important; + font-weight: 500; +} + .status-progress-flex { display: flex; align-items: center; @@ -366,6 +372,7 @@ table { .status-indicator.in-progress { background-color: #ffc107; } .status-indicator.cancelled { background-color: #6c757d; } .status-indicator.sent { background-color: #b19cd9; } +.status-indicator.busy { background-color: #ff8c00; } /* Opciones de cliente */ .client-option { diff --git a/ogWebconsole/src/app/components/task-logs/client-task-logs/client-task-logs.component.html b/ogWebconsole/src/app/components/task-logs/client-task-logs/client-task-logs.component.html index c4e57b0..2829b6f 100644 --- a/ogWebconsole/src/app/components/task-logs/client-task-logs/client-task-logs.component.html +++ b/ogWebconsole/src/app/components/task-logs/client-task-logs/client-task-logs.component.html @@ -34,13 +34,14 @@ Estado - Fallido Pendiente de ejecutar Ejecutando Completado con éxito Cancelado + Ocupado - + + @@ -42,12 +48,6 @@ {{ 'columnActions' | translate }} - - diff --git a/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.ts b/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.ts index 030387c..a4a5f2e 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/commands-task.component.ts @@ -3,16 +3,11 @@ import { HttpClient } from '@angular/common/http'; import { MatDialog } from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; import { CreateTaskComponent } from './create-task/create-task.component'; -import { DetailTaskComponent } from './detail-task/detail-task.component'; import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component'; import { JoyrideService } from 'ngx-joyride'; import { ConfigService } from '@services/config.service'; -import {CreateTaskScheduleComponent} from "./create-task-schedule/create-task-schedule.component"; -import {ShowClientsComponent} from "../../ogdhcp/show-clients/show-clients.component"; -import {Subnet} from "../../ogdhcp/og-dhcp-subnets.component"; import {ShowTaskScheduleComponent} from "./show-task-schedule/show-task-schedule.component"; import {ShowTaskScriptComponent} from "./show-task-script/show-task-script.component"; -import {CreateTaskScriptComponent} from "./create-task-script/create-task-script.component"; import {DatePipe} from "@angular/common"; @Component({ @@ -114,28 +109,6 @@ export class CommandsTaskComponent implements OnInit { }); } - manageScheduleAction(task: any): void { - this.dialog.open(CreateTaskScheduleComponent, { - width: '800px', - data: { task }, - }).afterClosed().subscribe( result => { - if (result) { - this.loadTasks(); - } - }) - } - - manageScriptAction(task: any): void { - this.dialog.open(CreateTaskScriptComponent, { - width: '900px', - data: { task }, - }).afterClosed().subscribe( result => { - if (result) { - this.loadTasks(); - } - }) - } - onPageChange(event: any): void { this.page = event.pageIndex + 1; this.itemsPerPage = event.pageSize; diff --git a/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.css b/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.css index 1a0cf6f..19d8d6d 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.css +++ b/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.css @@ -1,128 +1,243 @@ .dialog-title { - font-weight: bold; + color: #333; + font-weight: 600; + border-bottom: 2px solid #e0e0e0; + padding-bottom: 16px; + margin: 0; +} + +.dialog-content { + padding: 40px; + margin-top: 20px; + margin-bottom: 20px; } .task-form { - padding: 20px; + display: flex; + flex-direction: column; + gap: 20px; + padding: 0; } -.full-width { +.w-full { width: 100%; } +.w-half { + width: 48%; +} + .custom-time { display: flex; - gap: 15px; -} - -.w-half { - width: 50%; -} - -mat-form-field { - margin-bottom: 16px; -} - -form { - display: flex; - flex-direction: column; gap: 16px; - margin: auto; + flex-wrap: wrap; } -mat-form-field { - width: 100%; -} - -.action-container { - display: flex; - justify-content: flex-end; - gap: 1em; - padding: 1.5em; +.section-label { + display: block; + font-weight: 600; + color: #333; + margin-bottom: 12px; + font-size: 0.9rem; } .weekday-toggle-group { display: flex; - justify-content: space-between; gap: 8px; - margin-bottom: 16px; + flex-wrap: wrap; } .weekday-toggle { - flex: 1; - padding: 8px 0; - border: 1px solid #ccc; - border-radius: 4px; - background: #f5f5f5; + padding: 8px 12px; + border: 2px solid #e0e0e0; + background: white; + border-radius: 20px; cursor: pointer; + transition: all 0.3s ease; + font-size: 0.85rem; font-weight: 500; - transition: background 0.2s ease; + min-width: 60px; +} + +.weekday-toggle:hover { + border-color: #2196f3; + background: #f3f8ff; } .weekday-toggle.selected { - background: #1976d2; + background: #2196f3; color: white; - border-color: #1976d2; -} - -.month-toggle-group { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin-bottom: 16px; + border-color: #2196f3; } .month-toggle-row { display: flex; gap: 8px; margin-bottom: 8px; - justify-content: space-between; } .month-toggle { - flex: 1; - padding: 8px; - min-width: 48px; - border: 1px solid #ccc; - border-radius: 6px; - background-color: #f0f0f0; - text-align: center; + padding: 6px 10px; + border: 2px solid #e0e0e0; + background: white; + border-radius: 16px; cursor: pointer; - transition: background-color 0.2s ease; + transition: all 0.3s ease; + font-size: 0.8rem; + font-weight: 500; + min-width: 50px; +} + +.month-toggle:hover { + border-color: #4caf50; + background: #f1f8e9; } .month-toggle.selected { - background-color: #4caf50; + background: #4caf50; color: white; border-color: #4caf50; } -.summary-card { - background: #f9fafb; - border-left: 5px solid #3f51b5; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); - border-radius: 12px; - padding: 16px; - transition: box-shadow 0.3s ease; -} - -.summary-card:hover { - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); -} - -.summary-card { - background-color: #e3f2fd; - border-left: 4px solid #2196f3; - padding: 12px 16px; - margin-top: 16px; - border-radius: 6px; +.enabled-checkbox { display: flex; align-items: center; gap: 8px; - font-size: 14px; + padding: 16px; + background: #f8f9fa; + border-radius: 8px; + border: 1px solid #e0e0e0; +} + +.checkbox-icon { + color: #4caf50; +} + +/* Resumen mejorado */ +.summary-card { + background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%); + border: 1px solid #bbdefb; + border-radius: 12px; + margin-top: 20px; +} + +.summary-title { + display: flex; + align-items: center; + gap: 8px; + color: #1976d2; + font-size: 1.1rem; + margin: 0; +} + +.summary-title mat-icon { + font-size: 24px; +} + +.schedule-summary { + display: flex; + flex-direction: column; + gap: 12px; +} + +.summary-item { + display: flex; + align-items: center; + gap: 12px; + padding: 8px 0; + border-bottom: 1px solid rgba(25, 118, 210, 0.1); +} + +.summary-item:last-child { + border-bottom: none; +} + +.summary-icon { + color: #1976d2; + font-size: 20px; } .summary-text { - color: #0d47a1; + color: #333; + font-size: 0.95rem; line-height: 1.4; } + +/* Acciones */ +.action-container { + display: flex; + justify-content: flex-end; + gap: 12px; + padding: 20px; + border-top: 1px solid #e0e0e0; +} + +.ordinary-button { + background: #f5f5f5; + color: #333; + border: 1px solid #ddd; + padding: 10px 20px; + border-radius: 6px; + cursor: pointer; + transition: all 0.3s ease; +} + +.ordinary-button:hover { + background: #e0e0e0; +} + +.submit-button { + background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); + color: white; + border: none; + padding: 10px 24px; + border-radius: 6px; + cursor: pointer; + transition: all 0.3s ease; + display: flex; + align-items: center; + gap: 8px; + font-weight: 500; +} + +.submit-button:hover:not(:disabled) { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3); +} + +.submit-button:disabled { + background: #ccc; + cursor: not-allowed; +} + +.submit-button mat-icon { + font-size: 18px; +} + +/* Responsive */ +@media (max-width: 768px) { + .custom-time { + flex-direction: column; + } + + .w-half { + width: 100%; + } + + .weekday-toggle-group { + justify-content: center; + } + + .month-toggle-row { + justify-content: center; + } + + .action-container { + flex-direction: column; + } + + .submit-button, .ordinary-button { + width: 100%; + justify-content: center; + } +} diff --git a/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.html b/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.html index 0cb7fc9..186ff83 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.html +++ b/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.html @@ -1,4 +1,6 @@ -

Programar accion

+

+ {{ data.schedule ? 'Editar' : 'Crear' }} Programación de Tarea +

@@ -18,12 +20,13 @@ - Hora + Hora de ejecución + Hora en formato 24h (ej: 14:30)
- +
- Activar tarea + + power_settings_new + Activar programación + + - info - - {{ summaryText }} - + + + info + Resumen de la Programación + + + +
+
+ schedule + {{ summaryText }} +
+
+ next_plan + Próxima ejecución: {{ nextExecutionDate | date:'full' }} +
+
+ repeat + Se ejecutará {{ executionCount }} veces +
+
+
@@ -80,5 +105,8 @@ - + diff --git a/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.ts b/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.ts index b87c9c1..5822467 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/create-task-schedule/create-task-schedule.component.ts @@ -26,6 +26,8 @@ export class CreateTaskScheduleComponent implements OnInit{ editing: boolean = false; selectedMonths: { [key: string]: boolean } = {}; selectedDays: { [key: string]: boolean } = {}; + nextExecutionDate: Date | null = null; + executionCount: number = 0; constructor( private fb: FormBuilder, @@ -176,6 +178,9 @@ 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') { return `Esta acción se ejecutará una sola vez el ${ this.formatDate(start)} a las ${time}.`; } @@ -183,6 +188,45 @@ export class CreateTaskScheduleComponent implements OnInit{ return `Esta acción se ejecutará todos los ${days.join(', ')} de ${months.join(', ')}, desde el ${this.formatDate(start)} hasta el ${this.formatDate(end)} a las ${time}.`; } + private calculateNextExecutionAndCount(): void { + const recurrence = this.form.get('recurrenceType')?.value; + const time = this.form.get('executionTime')?.value; + + if (recurrence === 'none') { + const execDate = this.form.get('executionDate')?.value; + if (execDate && time) { + this.nextExecutionDate = new Date(execDate); + const [hours, minutes] = time.split(':'); + this.nextExecutionDate.setHours(parseInt(hours), parseInt(minutes), 0, 0); + this.executionCount = 1; + } + } else { + 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); + } + } + } + + private calculateExecutionCount(startDate: Date, endDate: Date, daysCount: number, monthsCount: number): number { + const start = new Date(startDate); + const end = new Date(endDate); + const daysDiff = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); + + // Aproximación: días seleccionados por semana * semanas * meses activos + return Math.ceil((daysCount / 7) * (daysDiff / 7) * (monthsCount / 12)); + } + formatDate(date: string | Date): string { const realDate = (date instanceof Date) ? date : new Date(date); return new Intl.DateTimeFormat('es-ES', { dateStyle: 'long' }).format(realDate); diff --git a/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.css b/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.css index 1cf8b3c..3fc5f7d 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.css +++ b/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.css @@ -275,10 +275,134 @@ table { padding: 16px; } -.toggle-options { +/* Estilos para las pestañas principales */ +.action-tabs { + margin-top: 20px; +} + +.action-tabs ::ng-deep .mat-tab-header { + border-bottom: 2px solid #e0e0e0; + margin-bottom: 20px; +} + +.action-tabs ::ng-deep .mat-tab-label { + min-width: 160px; + padding: 12px 24px; + font-weight: 500; + color: #666; + transition: all 0.3s ease; +} + +.action-tabs ::ng-deep .mat-tab-label:hover { + color: #1976d2; + background-color: rgba(25, 118, 210, 0.04); +} + +.action-tabs ::ng-deep .mat-tab-label.mat-tab-label-active { + color: #1976d2; + font-weight: 600; +} + +.action-tabs ::ng-deep .mat-ink-bar { + background-color: #1976d2; + height: 3px; +} + +/* Contenido de las pestañas */ +.tab-content { + padding: 20px 0; +} + +.action-description { display: flex; - justify-content: start; - margin: 16px 0; + align-items: center; + gap: 12px; + margin-bottom: 24px; + padding: 16px; + background-color: #f8f9fa; + border-radius: 8px; + border-left: 4px solid #1976d2; +} + +.action-description mat-icon { + color: #1976d2; + font-size: 24px; + width: 24px; + height: 24px; +} + +.action-description span { + color: #495057; + font-size: 14px; + font-weight: 500; +} + +/* Selector de tipo de script */ +.script-type-selector { + margin-bottom: 24px; + padding: 20px; + background-color: #f8f9fa; + border-radius: 8px; + border: 1px solid #e9ecef; +} + +.radio-group { + display: flex; + gap: 24px; +} + +.radio-button { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 16px; + border-radius: 6px; + transition: all 0.2s ease; +} + +.radio-button:hover { + background-color: rgba(25, 118, 210, 0.04); +} + +.radio-button mat-icon { + color: #1976d2; + font-size: 20px; + width: 20px; + height: 20px; +} + +/* Contenedor de acciones básicas */ +.basic-action-container { + display: flex; + flex-direction: column; + gap: 16px; +} + +/* Contenedor de scripts */ +.script-options { + display: flex; + flex-direction: column; + gap: 16px; +} + +/* Responsive design */ +@media (max-width: 768px) { + .action-tabs ::ng-deep .mat-tab-label { + min-width: 120px; + padding: 8px 16px; + font-size: 14px; + } + + .radio-group { + flex-direction: column; + gap: 16px; + } + + .action-description { + flex-direction: column; + text-align: center; + gap: 8px; + } } diff --git a/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.html b/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.html index 7a7ec4d..1715658 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.html +++ b/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.html @@ -2,60 +2,113 @@
- -
- - - edit Nuevo Script - - - storage Script Guardado - - -
- -
- - Orden de ejecucion - - - - Ingrese el script - - - -
- -
- - Seleccione script a ejecutar - - {{ script.name }} - - -
- -
- - Orden de ejecucion - - - -
-

Script:

-
-
- -
-

Ingrese los parámetros:

-
+ + + + + + +
+
+ power_settings_new + Selecciona una acción básica del sistema +
+ - {{ paramName }} - + Tipo de Acción + + + power Encender Equipo + + + power_off Apagar Equipo + + + restart_alt Reiniciar Equipo + + + login Iniciar Sesión + + + + + + Orden de ejecución +
-
-
+ + + + +
+
+ code + Ejecuta un script personalizado o crea uno nuevo +
+ + +
+
+ + + edit Nuevo Script + + + storage Script Guardado + + +
+ + +
+ + Orden de ejecución + + + + Ingrese el script + + + +
+ + +
+ + Seleccione script a ejecutar + + {{ script.name }} + + +
+ + +
+ + Orden de ejecución + + + +
+

Script:

+
+
+ +
+

Ingrese los parámetros:

+
+ + {{ paramName }} + + +
+
+
+
+
+
+
diff --git a/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.ts b/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.ts index f46cc88..d96ec4c 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/create-task-script/create-task-script.component.ts @@ -24,13 +24,15 @@ export class CreateTaskScriptComponent implements OnInit { scripts: any[] = []; scriptContent: string = ""; parameters: any = {}; + selectedTabIndex: number = 0; + selectedBasicAction: string = ''; commandType: string = 'existing'; selectedScript: any = null; newScript: string = ''; executionOrder: Number = 0; selection = new SelectionModel(true, []); parameterNames: string[] = Object.keys(this.parameters); - + commandTask: any; constructor( private fb: FormBuilder, private http: HttpClient, @@ -43,6 +45,8 @@ export class CreateTaskScriptComponent implements OnInit { @Inject(MAT_DIALOG_DATA) public data: any ) { this.baseUrl = this.configService.apiUrl; + this.commandTask = this.data.commandTask; + this.loadScripts() this.form = this.fb.group({ content: [''], @@ -66,6 +70,46 @@ export class CreateTaskScriptComponent implements OnInit { }); } + onTabChange(index: number): void { + this.selectedTabIndex = index; + if (index === 0) { + this.selectedBasicAction = ''; + } else { + this.selectedScript = null; + this.newScript = ''; + } + } + + onBasicActionChange(): void { + this.parameters = {}; + this.parameterNames = Object.keys(this.parameters); + } + + getBasicActionType(): string { + const actionMap: { [key: string]: string } = { + 'poweron': 'POWER-ON', + 'shutdown': 'SHUTDOWN', + 'reboot': 'REBOOT', + 'login': 'LOGIN', + }; + + return actionMap[this.selectedBasicAction] || ''; + } + + validateBasicAction(): boolean { + if (!this.selectedBasicAction) { + this.toastService.error('Debe seleccionar un tipo de acción'); + return false; + } + + if (this.executionOrder === null || this.executionOrder === undefined) { + this.toastService.error('Debe especificar el orden de ejecución'); + return false; + } + + return true; + } + saveNewScript() { if (!this.newScript.trim()) { this.toastService.error('Debe ingresar un script antes de guardar.'); @@ -115,12 +159,60 @@ export class CreateTaskScriptComponent implements OnInit { this.scriptContent = updatedScript; } + validateScript(): boolean { + if (this.commandType === 'new') { + if (!this.newScript.trim()) { + this.toastService.error('Debe ingresar un script'); + return false; + } + } else if (this.commandType === 'existing') { + if (!this.selectedScript) { + this.toastService.error('Debe seleccionar un script'); + return false; + } + } + + if (this.executionOrder === null || this.executionOrder === undefined) { + this.toastService.error('Debe especificar el orden de ejecución'); + return false; + } + + return true; + } + onSubmit() { + let isValid = false; + let content = ''; + let type = ''; + + if (this.selectedTabIndex === 0) { + isValid = this.validateBasicAction(); + if (isValid) { + content = this.getBasicActionType(); + type = 'basic-action'; + } + } else { // Pestaña de scripts + isValid = this.validateScript(); + if (isValid) { + content = this.commandType === 'existing' ? this.scriptContent : this.newScript; + type = 'run-script'; + } + } + + if (!isValid) { + return; + } + + if (!this.data) { + this.toastService.error('Error: No se recibieron datos del diálogo'); + return; + } + this.http.post(`${this.baseUrl}/command-task-scripts`, { - commandTask: this.data.task['@id'], - content: this.commandType === 'existing' ? this.scriptContent : this.newScript, + commandTask: this.commandTask['@id'], + content: content, order: this.executionOrder, - type: 'run-script', + type: this.selectedBasicAction, }).subscribe({ next: () => { this.toastService.success('Tarea creada con éxito'); diff --git a/ogWebconsole/src/app/components/commands/commands-task/create-task/create-task.component.ts b/ogWebconsole/src/app/components/commands/commands-task/create-task/create-task.component.ts index e550e5d..9a12fb0 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/create-task/create-task.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/create-task/create-task.component.ts @@ -269,7 +269,7 @@ export class CreateTaskComponent implements OnInit { }; if (scope === 'clients') { - payload.clients = this.selectedClients.map(client => client.uuid); + payload.clients = this.selectedClients.map(client => client['@id'] ? client['@id'] : client.uuid); payload.organizationalUnit = null; } else { payload.organizationalUnit = formData.organizationalUnit; diff --git a/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.css b/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.css index d092e27..4d1d26b 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.css +++ b/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.css @@ -87,3 +87,122 @@ mat-spinner { display: flex; gap: 15px; } + +.header-actions { + display: flex; + justify-content: flex-end; + margin-bottom: 20px; + padding: 16px; + background: #f8f9fa; + border-radius: 8px; +} + +.action-button { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 24px; + color: white; + border: none; + font-weight: 500; + cursor: pointer; + transition: background-color 0.2s ease; +} + +.action-button:hover { + background: #1565c0; +} + +.action-button mat-icon { + font-size: 20px; + width: 20px; + height: 20px; +} + +.create-button { + color: white; + border: none; + padding: 12px 24px; + font-weight: 500; +} + +.create-button mat-icon { + margin-right: 8px; +} + +.search-container { + display: flex; + gap: 16px; + margin: 20px 0; + flex-wrap: wrap; +} + +.search-string { + min-width: 250px; +} + +/* Estilos para los chips de días y meses */ +.days-display, .months-display { + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.day-chip, .month-chip { + background: #e3f2fd; + color: #1976d2; + padding: 4px 8px; + border-radius: 12px; + font-size: 0.75rem; + font-weight: 500; + border: 1px solid #bbdefb; +} + +/* Estilos para la próxima ejecución */ +.next-execution { + display: flex; + align-items: center; + gap: 8px; + color: #555; +} + +.execution-icon { + font-size: 18px; + color: #2196f3; +} + +/* Mejoras en la tabla */ +.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) { + .search-container { + flex-direction: column; + } + + .search-string { + min-width: auto; + width: 100%; + } + + .header-actions { + justify-content: center; + } + + .create-button { + width: 100%; + } +} diff --git a/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.html b/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.html index 0df04e2..c9f5526 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.html +++ b/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.html @@ -3,26 +3,33 @@

Gestionar programaciones de tareas en {{ data.commandTask?.name }}

+
+ +
+
- Buscar nombre del cliente + text="Busca programaciones por nombre para localizar una programación específica rápidamente."> + Buscar programación search - Pulsar 'enter' para buscar - + Buscar por tipo search - Pulsar 'enter' para buscar @@ -31,7 +38,7 @@ + text="Visualiza y administra las programaciones de tareas según los filtros aplicados."> @@ -88,7 +121,7 @@
{{ column.header }} @@ -47,19 +54,45 @@ Programado
- {{ schedule.recurrenceDetails.initDate | date }} → {{ schedule.recurrenceDetails.endDate | date}} + {{ schedule.recurrenceDetails?.initDate | date }} → {{ schedule.recurrenceDetails?.endDate | date}}
+ {{ schedule.executionTime | date: 'HH:mm' }} - + + +
+ schedule + {{ column.cell(schedule) || 'No calculada' }} +
+
+ + +
+ {{ day | slice:0:3 }} +
+
+ + +
+ {{ month | slice:0:3 }} +
+
+ + {{ column.cell(schedule) }} + - + Activo @@ -74,10 +107,10 @@
Acciones - -
+ text="Navega entre las páginas de programaciones usando el paginador."> diff --git a/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.ts b/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.ts index 108197a..aa84bd8 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/show-task-schedule/show-task-schedule.component.ts @@ -28,13 +28,14 @@ export class ShowTaskScheduleComponent implements OnInit{ columns = [ { columnDef: 'id', header: 'ID', cell: (schedule: any) => schedule.id }, { columnDef: 'recurrenceType', header: 'Recurrencia', cell: (schedule: any) => schedule.recurrenceType }, - { columnDef: 'time', header: 'Hora de ejecución', cell: (schedule: any) => this.datePipe.transform(schedule.executionTime, 'HH:mm') }, - { columnDef: 'daysOfWeek', header: 'Dias de la semana', cell: (schedule: any) => schedule.recurrenceDetails.daysOfWeek }, - { columnDef: 'months', header: 'Meses', cell: (schedule: any) => schedule.recurrenceDetails.months }, - { columnDef: 'enabled', header: 'Activo', cell: (schedule: any) => schedule.enabled } + { 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) => schedule.recurrenceDetails?.daysOfWeek || [] }, + { columnDef: 'months', header: 'Meses', cell: (schedule: any) => schedule.recurrenceDetails?.months || [] }, + { columnDef: 'enabled', header: 'Estado', cell: (schedule: any) => schedule.enabled } ]; - displayedColumns: string[] = ['id', 'recurrenceType', 'time', 'daysOfWeek', 'months', 'enabled', 'actions']; + displayedColumns: string[] = ['id', 'recurrenceType', 'executionTime', 'nextExecution', 'daysOfWeek', 'months', 'enabled', 'actions']; constructor( private toastService: ToastrService, @@ -63,21 +64,39 @@ export class ShowTaskScheduleComponent implements OnInit{ }, (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(() => this.loadData()); + }).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: 'tarea programada' } + data: { name: 'programación de tarea' } }); dialogRef.afterClosed().subscribe(result => { @@ -88,20 +107,52 @@ export class ShowTaskScheduleComponent implements OnInit{ this.loadData(); }, (error) => { - this.toastService.error(error.error['hydra:description']); + 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) { + const execDate = new Date(schedule.executionDate); + const now = new Date(); + + if (execDate < now) { + return 'Ya ejecutada'; + } + + return this.datePipe.transform(execDate, 'dd/MM/yyyy HH:mm') || 'Fecha inválida'; + } + return 'Sin fecha'; + } + + if (schedule.recurrenceDetails) { + const days = schedule.recurrenceDetails.daysOfWeek?.join(', ') || 'Todos'; + const months = schedule.recurrenceDetails.months?.join(', ') || 'Todos'; + return `${days} - ${months}`; + } + + return 'Configuración incompleta'; + } catch (error) { + return 'Error en cálculo'; + } + } + + onPageChange(event: any): void { + this.page = event.pageIndex; + this.itemsPerPage = event.pageSize; + this.loadData(); + } + onNoClick(): void { this.dialogRef.close(false); } - - onPageChange(event: any) { - this.page = event.pageIndex; - this.itemsPerPage = event.pageSize; - this.loadData() - } } diff --git a/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.css b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.css index d092e27..bd8e860 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.css +++ b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.css @@ -87,3 +87,222 @@ mat-spinner { display: flex; gap: 15px; } + +/* Estilos para el header de acciones */ +.header-actions { + display: flex; + justify-content: flex-end; + margin-bottom: 20px; + padding: 16px; + background: #f8f9fa; + border-radius: 8px; +} + +.action-button { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 24px; + background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); + color: white; + border: none; + border-radius: 6px; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 2px 8px rgba(76, 175, 80, 0.3); +} + +.action-button:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(76, 175, 80, 0.4); + background: linear-gradient(135deg, #45a049 0%, #3d8b40 100%); +} + +.action-button mat-icon { + font-size: 20px; + width: 20px; + height: 20px; +} + +/* Estilos para el contenido del script */ +.script-content { + background: #f8f9fa; + border: 1px solid #e9ecef; + border-radius: 8px; + overflow: hidden; + transition: all 0.3s ease; +} + +.script-content:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + border-color: #4caf50; +} + +.script-header { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 16px; + background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); + color: white; + font-weight: 500; +} + +.script-icon { + font-size: 18px; + width: 18px; + height: 18px; +} + +.script-title { + font-size: 14px; +} + +.script-body { + padding: 16px; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + line-height: 1.5; + white-space: pre-wrap; + background: #ffffff; + color: #333; + max-height: 200px; + overflow-y: auto; +} + +/* Estilos para el botón de parámetros */ +.parameters-button { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + background: linear-gradient(135deg, #2196f3 0%, #1976d2 100%); + color: white; + border: none; + border-radius: 6px; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 2px 6px rgba(33, 150, 243, 0.3); +} + +.parameters-button:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(33, 150, 243, 0.4); + background: linear-gradient(135deg, #1976d2 0%, #1565c0 100%); +} + +.parameters-button mat-icon { + font-size: 16px; + width: 16px; + height: 16px; +} + +.parameters-button span { + font-size: 13px; +} + +/* Mejoras en la tabla */ +.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) { + .search-container { + flex-direction: column; + } + + .search-string { + min-width: auto; + width: 100%; + } + + .header-actions { + justify-content: center; + } + + .action-button { + width: 100%; + } + + .script-body { + max-height: 150px; + } +} + +/* Estilos para los chips de tipo */ +.type-chip { + font-weight: 500; + padding: 8px 12px; + border-radius: 16px; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.type-script { + background: #e8f5e8; + color: #2e7d32; + border: 1px solid #c8e6c9; +} + +.type-command { + background: #fff3e0; + color: #f57c00; + border: 1px solid #ffcc80; +} + +.type-python { + background: #e3f2fd; + color: #1976d2; + border: 1px solid #bbdefb; +} + +.type-bash { + background: #f3e5f5; + color: #7b1fa2; + border: 1px solid #e1bee7; +} + +.type-default { + background: #f5f5f5; + color: #616161; + border: 1px solid #e0e0e0; +} + +/* Estilos para el indicador de orden */ +.order-indicator { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + background: #f8f9fa; + border-radius: 20px; + border: 1px solid #e9ecef; +} + +.order-icon { + font-size: 16px; + width: 16px; + height: 16px; + color: #6c757d; +} + +.order-number { + font-weight: 600; + color: #495057; + font-size: 14px; +} diff --git a/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.html b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.html index e8090db..4f767f7 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.html +++ b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.html @@ -3,6 +3,13 @@

Gestionar scripts de tareas en {{ data.commandTask?.name }}

+
+ +
+
@@ -32,8 +39,9 @@ - diff --git a/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.ts b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.ts index 45f4899..8cf5458 100644 --- a/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.ts +++ b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/show-task-script.component.ts @@ -5,6 +5,7 @@ import {HttpClient} from "@angular/common/http"; import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog"; import {ConfigService} from "@services/config.service"; import {CreateTaskScheduleComponent} from "../create-task-schedule/create-task-schedule.component"; +import {CreateTaskScriptComponent} from "../create-task-script/create-task-script.component"; import {DeleteModalComponent} from "../../../../shared/delete_modal/delete-modal/delete-modal.component"; import {ViewParametersModalComponent} from "./view-parameters-modal/view-parameters-modal.component"; @@ -96,6 +97,19 @@ export class ShowTaskScriptComponent implements OnInit{ }); } + createNewScript(): void { + const dialogRef = this.dialog.open(CreateTaskScriptComponent, { + width: '800px', + data: { commandTask: this.data.commandTask } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.loadData(); + } + }); + } + onPageChange(event: any) { this.page = event.pageIndex; this.itemsPerPage = event.pageSize; From 7a110d9d58ecaaa6718fa016f2fe6d2f043fa97d Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 25 Aug 2025 13:09:04 +0200 Subject: [PATCH 3/5] refs #2619. Task mark as success --- .../task-logs/task-logs.component.css | 28 ++++++++++++++- .../task-logs/task-logs.component.html | 8 +++++ .../task-logs/task-logs.component.ts | 35 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/ogWebconsole/src/app/components/task-logs/task-logs.component.css b/ogWebconsole/src/app/components/task-logs/task-logs.component.css index 88ca540..f2077a2 100644 --- a/ogWebconsole/src/app/components/task-logs/task-logs.component.css +++ b/ogWebconsole/src/app/components/task-logs/task-logs.component.css @@ -497,6 +497,12 @@ table { flex-shrink: 0; } +/* Ajuste para el botón de éxito en la barra de progreso */ +.progress-container .success-button { + margin-left: 5px; + flex-shrink: 0; +} + .paginator-container { display: flex; justify-content: end; @@ -549,7 +555,7 @@ table { .status-progress-flex { display: flex; align-items: center; - gap: 8px; + gap: 4px; } .status-option { @@ -617,6 +623,26 @@ button.cancel-button { color: #dc3545; } +.success-button { + display: flex; + align-items: center; + justify-content: center; + color: #28a745; + background-color: transparent; + border: none; + padding: 0px; + transition: all 0.3s ease; +} + +.success-button:hover { + background-color: rgba(40, 167, 69, 0.1); + border-radius: 50%; +} + +.success-button mat-icon { + color: #28a745; +} + .selected-row { background-color: rgba(102, 126, 234, 0.1) !important; } diff --git a/ogWebconsole/src/app/components/task-logs/task-logs.component.html b/ogWebconsole/src/app/components/task-logs/task-logs.component.html index b570279..b2df6d8 100644 --- a/ogWebconsole/src/app/components/task-logs/task-logs.component.html +++ b/ogWebconsole/src/app/components/task-logs/task-logs.component.html @@ -241,6 +241,10 @@ (click)="cancelTrace(trace)" class="cancel-button" matTooltip="{{ 'cancelTask' | translate }}"> cancel +
@@ -270,6 +274,10 @@ (click)="cancelTrace(trace)" class="cancel-button" matTooltip="{{ 'cancelTask' | translate }}"> cancel +
diff --git a/ogWebconsole/src/app/components/task-logs/task-logs.component.ts b/ogWebconsole/src/app/components/task-logs/task-logs.component.ts index 972701f..1326dec 100644 --- a/ogWebconsole/src/app/components/task-logs/task-logs.component.ts +++ b/ogWebconsole/src/app/components/task-logs/task-logs.component.ts @@ -321,6 +321,41 @@ export class TaskLogsComponent implements OnInit, OnDestroy { }); } + markTraceAsSuccess(trace: any): void { + if (trace.status !== 'in-progress') { + this.toastService.warning('Solo se pueden marcar como exitosas las trazas en progreso', 'Advertencia'); + return; + } + + const dialogRef = this.dialog.open(DeleteModalComponent, { + width: '400px', + data: { + title: 'Marcar Traza como Exitosa', + message: `¿Estás seguro de que quieres marcar la traza #${trace.id} como exitosa?`, + confirmText: 'Marcar como Exitosa', + cancelText: 'Cancelar' + } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.loading = true; + this.http.post(`${this.baseUrl}${trace['@id']}/mark-as-success`, {}).subscribe( + () => { + this.toastService.success('Traza marcada como exitosa correctamente', 'Éxito'); + this.loadTraces(); + this.loadTotalStats(); + }, + (error) => { + console.error('Error marking trace as success:', error); + this.toastService.error('Error al marcar la traza como exitosa', 'Error'); + this.loading = false; + } + ); + } + }); + } + private updateTracesStatus(clientUuid: string, newStatus: string, progress: Number): void { const traceIndex = this.traces.findIndex(trace => trace['@id'] === clientUuid); if (traceIndex !== -1) { From ee950fd685585f8a3b84280ae5fab5db3527ec33 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 25 Aug 2025 13:09:43 +0200 Subject: [PATCH 4/5] Updated delete moda --- .../delete-modal/delete-modal.component.html | 11 +++++----- .../delete-modal/delete-modal.component.ts | 21 +++++++++++++++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/ogWebconsole/src/app/shared/delete_modal/delete-modal/delete-modal.component.html b/ogWebconsole/src/app/shared/delete_modal/delete-modal/delete-modal.component.html index f3592de..546b5b9 100644 --- a/ogWebconsole/src/app/shared/delete_modal/delete-modal/delete-modal.component.html +++ b/ogWebconsole/src/app/shared/delete_modal/delete-modal/delete-modal.component.html @@ -1,10 +1,11 @@ -

Eliminar

+

{{ title }}

-

- ¿Estás seguro que deseas eliminar {{ data.name }}? +

+ {{ message }} + {{ name }}

- - + +
diff --git a/ogWebconsole/src/app/shared/delete_modal/delete-modal/delete-modal.component.ts b/ogWebconsole/src/app/shared/delete_modal/delete-modal/delete-modal.component.ts index d3cf678..9bfebc3 100644 --- a/ogWebconsole/src/app/shared/delete_modal/delete-modal/delete-modal.component.ts +++ b/ogWebconsole/src/app/shared/delete_modal/delete-modal/delete-modal.component.ts @@ -7,10 +7,27 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; styleUrl: './delete-modal.component.css' }) export class DeleteModalComponent { + title: string = 'Eliminar'; + message: string = '¿Estás seguro que deseas eliminar este elemento?'; + confirmText: string = 'Eliminar'; + cancelText: string = 'Cancelar'; + showName: boolean = false; + name: string = ''; + constructor( public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: { name: string } - ) {} + @Inject(MAT_DIALOG_DATA) public data: any + ) { + // Configurar valores por defecto o usar los proporcionados + if (data) { + this.title = data.title || this.title; + this.message = data.message || this.message; + this.confirmText = data.confirmText || this.confirmText; + this.cancelText = data.cancelText || this.cancelText; + this.name = data.name || ''; + this.showName = !!data.name; + } + } onNoClick(): void { this.dialogRef.close(false); From 3eae96f2c872fc3e8f899b60bb33ff6483cc6421 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 25 Aug 2025 13:12:41 +0200 Subject: [PATCH 5/5] refs #2596. Create task options from createImage --- CHANGELOG.md | 9 ++ .../create-image/create-image.component.css | 3 +- .../create-image/create-image.component.html | 7 ++ .../create-image/create-image.component.ts | 98 ++++++++++++++++++- 4 files changed, 114 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4e7635..f29af71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ # Changelog +## [0.20.0] - 2025-08-25 +### Added +- Se ha añadido un nuevo boton en "Trazas" para marcar la misma como completada cuando se requiera. +- Nuevo estado "ocupado" en las trazas para indicar que el cliente envia un "409" y que ya esta ejecutando una accion + +### Improved +- Mejorada la interfaz para gestionar las tareas. + +--- ## [0.19.0] - 2025-08-06 ### Added - Se ha añadido un nuevo estado "enviado" para cuando se ejecuten acciones a equipos en estado Windows o Linux diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css index a1ed15d..cf41b39 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.css @@ -129,8 +129,9 @@ .button-row { display: flex; + padding-right: 1em; gap: 12px; - padding-right: 0; + align-items: center; } /* Tabla de particiones modernizada */ diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html index 70eedfe..c19f0d8 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.html @@ -23,6 +23,13 @@
+
+ +
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts index 88a8f57..70e6b27 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/create-image/create-image.component.ts @@ -9,6 +9,7 @@ import {MatDialog} from "@angular/material/dialog"; import {QueueConfirmationModalComponent} from "../../../../../shared/queue-confirmation-modal/queue-confirmation-modal.component"; import {CreateRepositoryModalComponent} from "./create-repository-modal/create-repository-modal.component"; import {CreateBranchModalComponent} from "../../../../repositories/show-git-images/create-branch-modal/create-branch-modal.component"; +import {CreateTaskComponent} from "../../../../commands/commands-task/create-task/create-task.component"; @Component({ selector: 'app-create-image', @@ -108,7 +109,6 @@ export class CreateClientImageComponent implements OnInit{ } ngOnInit() { - console.log('CreateImageComponent ngOnInit ejecutado'); this.clientId = this.route.snapshot.paramMap.get('id'); this.loadPartitions(); this.loadImages(); @@ -612,8 +612,102 @@ export class CreateClientImageComponent implements OnInit{ this.isDestinationBranchEditable = !this.isDestinationBranchEditable; if (!this.isDestinationBranchEditable) { - // Opcional: Aquí se pueden agregar validaciones adicionales console.log('Rama destino guardada:', this.destinationBranch); } } + + isFormValid(): boolean { + if (!this.selectedPartition) { + return false; + } + + if (this.imageType === 'monolithic') { + if (this.monolithicAction === 'create') { + return this.name !== null && this.name.trim().length > 0; + } else if (this.monolithicAction === 'update') { + return this.selectedImage !== null; + } + } else if (this.imageType === 'git') { + if (this.hasValidGitData) { + return this.destinationBranch !== null && this.destinationBranch.trim().length > 0; + } else { + return this.selectedGitRepository !== null; + } + } + + return false; + } + + openScheduleModal(): void { + let scope = 'clients'; + + // Verificar que tenemos la información del cliente + if (!this.client) { + this.toastService.error('No se ha cargado la información del cliente'); + return; + } + + // Crear un array con el objeto cliente completo + let selectedClients = [this.client]; + console.log(selectedClients); + + const dialogRef = this.dialog.open(CreateTaskComponent, { + width: '800px', + data: { + scope: scope, + selectedClients: selectedClients, + organizationalUnit: this.client['@id'], + source: 'create-image', + runScriptContext: null + } + }); + + dialogRef.afterClosed().subscribe((result: any) => { + if (result) { + // Verificar que tenemos la partición seleccionada + if (!this.selectedPartition) { + this.toastService.error('Debe seleccionar una partición'); + return; + } + + let payload: any = {}; + + if (this.imageType === 'monolithic') { + payload = { + type: 'monolithic', + action: this.monolithicAction, + diskNumber: this.selectedPartition.diskNumber, + partitionNumber: this.selectedPartition.partitionNumber, + imageName: this.monolithicAction === 'create' ? this.name : this.selectedImage?.name + }; + } else if (this.imageType === 'git') { + payload = { + type: 'git', + diskNumber: this.selectedPartition.diskNumber, + partitionNumber: this.selectedPartition.partitionNumber, + repository: this.selectedGitRepository?.name || this.gitData?.repo, + sourceBranch: this.gitData?.branch, + destinationBranch: this.destinationBranch + }; + } + + const taskId = result['taskId'] ? result['taskId']['@id'] : result['@id']; + const executionOrder = result['executionOrder'] || 1; + + this.http.post(`${this.baseUrl}/command-task-scripts`, { + commandTask: taskId, + parameters: payload, + order: executionOrder, + type: 'create-image', + }).subscribe({ + next: () => { + this.toastService.success('Tarea de creación de imagen programada con éxito'); + }, + error: (error) => { + this.toastService.error(error.error['hydra:description'] || 'Error al programar la tarea'); + } + }); + } + }); + } }