From 0adae7dbae9b0e668e3306499b319235b3e907b4 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 15 Jul 2026 11:17:34 +0200 Subject: [PATCH] ref 3009. Added excecute task right now --- ogWebconsole/src/app/app.module.ts | 4 + .../show-task-script.component.css | 23 ++-- .../show-task-script.component.html | 7 +- .../show-task-script.component.ts | 8 ++ .../view-script-modal.component.css | 12 ++ .../view-script-modal.component.html | 7 ++ .../view-script-modal.component.ts | 18 +++ .../execute-command.component.ts | 21 +++- .../execute-task-now.component.css | 41 +++++++ .../execute-task-now.component.html | 34 ++++++ .../execute-task-now.component.ts | 105 ++++++++++++++++++ ogWebconsole/src/locale/cat.json | 8 +- ogWebconsole/src/locale/en.json | 8 +- ogWebconsole/src/locale/es.json | 8 +- 14 files changed, 288 insertions(+), 16 deletions(-) create mode 100644 ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.css create mode 100644 ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.html create mode 100644 ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.ts create mode 100644 ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.css create mode 100644 ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.html create mode 100644 ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.ts diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts index 0af94eb..d6b298c 100644 --- a/ogWebconsole/src/app/app.module.ts +++ b/ogWebconsole/src/app/app.module.ts @@ -172,12 +172,14 @@ import { ShowTaskScheduleComponent } from './components/commands/commands-task/s import { ShowTaskScriptComponent } from './components/commands/commands-task/show-task-script/show-task-script.component'; import { CreateTaskScriptComponent } from './components/commands/commands-task/create-task-script/create-task-script.component'; import { ViewParametersModalComponent } from './components/commands/commands-task/show-task-script/view-parameters-modal/view-parameters-modal.component'; +import { ViewScriptModalComponent } from './components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component'; import { OutputDialogComponent } from './components/task-logs/output-dialog/output-dialog.component'; import { ClientTaskLogsComponent } from './components/task-logs/client-task-logs/client-task-logs.component'; import { BootSoPartitionComponent } from './components/commands/main-commands/execute-command/boot-so-partition/boot-so-partition.component'; import { RemoveCacheImageComponent } from './components/commands/main-commands/execute-command/remove-cache-image/remove-cache-image.component'; import { ChangeParentComponent } from './components/groups/shared/change-parent/change-parent.component'; import { SoftwareProfilePartitionComponent } from './components/commands/main-commands/execute-command/software-profile-partition/software-profile-partition.component'; +import { ExecuteTaskNowComponent } from './components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component'; import { ClientPendingTasksComponent } from './components/task-logs/client-pending-tasks/client-pending-tasks.component'; import { QueueConfirmationModalComponent } from './shared/queue-confirmation-modal/queue-confirmation-modal.component'; import { ModalOverlayComponent } from './shared/modal-overlay/modal-overlay.component'; @@ -316,12 +318,14 @@ registerLocaleData(localeEs, 'es-ES'); ShowTaskScriptComponent, CreateTaskScriptComponent, ViewParametersModalComponent, + ViewScriptModalComponent, OutputDialogComponent, ClientTaskLogsComponent, BootSoPartitionComponent, RemoveCacheImageComponent, ChangeParentComponent, SoftwareProfilePartitionComponent, + ExecuteTaskNowComponent, ClientPendingTasksComponent, ModalOverlayComponent, ScrollToTopComponent, 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 abebd3a..c83c2c2 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 @@ -321,18 +321,21 @@ mat-spinner { color: #666; } -/* Contenido del script en celda */ -.script-content-cell { - background-color: #f5f5f5; - padding: 8px 12px; - border: 1px solid #e0e0e0; - border-radius: 4px; - font-family: 'Monaco', 'Menlo', monospace; - white-space: pre-wrap; - font-size: 12px; - max-width: 300px; +/* Vista previa del script en celda (una sola línea, altura de fila fija) */ +.script-preview-cell { + display: flex; + align-items: center; + gap: 4px; + max-width: 260px; +} + +.script-preview-text { + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + font-family: 'Monaco', 'Menlo', monospace; + font-size: 12px; + color: #555; } .no-content-hint { 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 d6b8dcc..73af703 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 @@ -50,8 +50,11 @@ {{ 'labelScript' | translate }} -
- {{ item.content }} +
+ {{ item.content }} +
({{ 'viewParameters' | translate }}) 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 65eac9c..8b2e32c 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 @@ -10,6 +10,7 @@ import { CreateTaskScheduleComponent } from "../create-task-schedule/create-task 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"; +import { ViewScriptModalComponent } from "./view-script-modal/view-script-modal.component"; @Component({ selector: 'app-show-task-script', @@ -119,6 +120,13 @@ export class ShowTaskScriptComponent implements OnInit { }); } + openScriptModal(content: string): void { + this.dialog.open(ViewScriptModalComponent, { + width: '700px', + data: content + }); + } + createNewScript(): void { const dialogRef = this.dialog.open(CreateTaskScriptComponent, { width: '800px', diff --git a/ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.css b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.css new file mode 100644 index 0000000..d5fcf86 --- /dev/null +++ b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.css @@ -0,0 +1,12 @@ +.script-full-content { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + white-space: pre-wrap; + word-break: break-word; + background: #f5f5f5; + border: 1px solid #e0e0e0; + border-radius: 4px; + padding: 12px; + max-height: 60vh; + overflow-y: auto; +} diff --git a/ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.html b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.html new file mode 100644 index 0000000..54c9fea --- /dev/null +++ b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.html @@ -0,0 +1,7 @@ +

{{ 'labelScript' | translate }}

+ +
{{ data }}
+
+ + + diff --git a/ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.ts b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.ts new file mode 100644 index 0000000..392d17c --- /dev/null +++ b/ogWebconsole/src/app/components/commands/commands-task/show-task-script/view-script-modal/view-script-modal.component.ts @@ -0,0 +1,18 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; + +@Component({ + selector: 'app-view-script-modal', + templateUrl: './view-script-modal.component.html', + styleUrl: './view-script-modal.component.css' +}) +export class ViewScriptModalComponent { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any + ) {} + + close(): void { + this.dialogRef.close(); + } +} diff --git a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts index 876b557..a7c89ba 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts @@ -11,6 +11,7 @@ import { RemoveCacheImageComponent } from "./remove-cache-image/remove-cache-ima import { AuthService } from '@services/auth.service'; import {SoftwareProfilePartitionComponent} from "./software-profile-partition/software-profile-partition.component"; import { QueueConfirmationModalComponent } from '../../../../shared/queue-confirmation-modal/queue-confirmation-modal.component'; +import { ExecuteTaskNowComponent } from './execute-task-now/execute-task-now.component'; @Component({ selector: 'app-execute-command', @@ -43,6 +44,7 @@ export class ExecuteCommandComponent implements OnInit { { translationKey: 'executeCommands.runScript', slug: 'run-script', disabled: false }, { translationKey: 'executeCommands.refresh', slug: 'refresh', disabled: false }, { translationKey: 'executeCommands.deleteLease', slug: 'delete-lease', disabled: false }, + { translationKey: 'executeCommands.executeTask', slug: 'execute-task', disabled: false }, ]; client: any = {}; @@ -87,7 +89,8 @@ export class ExecuteCommandComponent implements OnInit { 'remove-cache-image', 'partition', 'refresh', - 'delete-lease' + 'delete-lease', + 'execute-task' ], 'ou-minimal': [ 'power-on', @@ -138,6 +141,10 @@ export class ExecuteCommandComponent implements OnInit { command.disabled = !states.every(state => state === 'og-live'); } + if (command.slug === 'execute-task') { + command.disabled = this.clientData.length === 0; + } + return command; }); } @@ -194,6 +201,18 @@ export class ExecuteCommandComponent implements OnInit { if (action === 'delete-lease') { this.deleteClientLease(); } + + if (action === 'execute-task') { + this.openExecuteTaskModal(); + } + } + + openExecuteTaskModal(): void { + this.dialog.open(ExecuteTaskNowComponent, { + width: '500px', + height: 'auto', + data: { clients: this.clientData } + }); } rebootClient(): void { diff --git a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.css b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.css new file mode 100644 index 0000000..a50deed --- /dev/null +++ b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.css @@ -0,0 +1,41 @@ +.form-container { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px; +} + +.full-width { + width: 100%; +} + +.hint-text { + margin: 0; + font-size: 12px; + color: rgba(0, 0, 0, 0.6); +} + +.mat-dialog-title { + font-size: 20px; + font-weight: 600; + margin-bottom: 12px; +} + +.mat-dialog-content { + max-height: 60vh; + overflow-y: auto; +} + +.mat-dialog-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + padding: 16px; +} + +.action-container { + display: flex; + justify-content: flex-end; + gap: 1em; + padding: 1.5em; +} diff --git a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.html b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.html new file mode 100644 index 0000000..cc8a12f --- /dev/null +++ b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.html @@ -0,0 +1,34 @@ +

{{ 'executeTaskNowModalTitle' | translate }}

+ + + + {{ 'selectTask' | translate }} + + + + + + + {{ task.name }} + + + + +

{{ 'loadingDots' | translate }}

+

{{ 'noTasksAvailableMessage' | translate }}

+
+ + + + + diff --git a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.ts b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.ts new file mode 100644 index 0000000..fcf3d75 --- /dev/null +++ b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-task-now/execute-task-now.component.ts @@ -0,0 +1,105 @@ +import { inject } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { HttpClient } from '@angular/common/http'; +import { FormControl } from '@angular/forms'; +import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; +import { map, startWith } from 'rxjs/operators'; +import { ToastrService } from 'ngx-toastr'; +import { ConfigService } from '@services/config.service'; + +@Component({ + selector: 'app-execute-task-now', + templateUrl: './execute-task-now.component.html', + styleUrls: ['./execute-task-now.component.css'] +}) +export class ExecuteTaskNowComponent implements OnInit { + public translate = inject(TranslateService); + baseUrl: string; + tasks: any[] = []; + tasksLoading = true; + taskControl = new FormControl(); + filteredTasks$!: Observable; + selectedTask: any = null; + private tasks$ = new BehaviorSubject([]); + + constructor( + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private http: HttpClient, + private configService: ConfigService, + private toastService: ToastrService, + ) { + this.baseUrl = this.configService.apiUrl; + } + + ngOnInit(): void { + this.loadTasks(); + + this.filteredTasks$ = combineLatest([ + this.taskControl.valueChanges.pipe(startWith('')), + this.tasks$ + ]).pipe( + map(([value]) => this._filterTasks(typeof value === 'string' ? value : value?.name)) + ); + } + + loadTasks(): void { + this.http.get(`${this.baseUrl}/command-tasks?page=1&itemsPerPage=100`).subscribe( + response => { + this.tasks = response['hydra:member'] || []; + this.tasksLoading = false; + this.tasks$.next(this.tasks); + }, + error => { + this.tasksLoading = false; + this.toastService.error(this.translate.instant('errorLoadingTasks'), error); + } + ); + } + + private _filterTasks(value: string): any[] { + const filterValue = (value || '').toLowerCase(); + return this.tasks.filter(task => task.name.toLowerCase().includes(filterValue)); + } + + displayFn(task: any): string { + return task && task.name ? task.name : ''; + } + + onTaskSelected(task: any): void { + this.selectedTask = task; + } + + executeNow(): void { + if (!this.selectedTask) { + return; + } + + const clients = this.data?.clients || []; + + if (clients.length === 0) { + this.toastService.error(this.translate.instant('noClientsSelected')); + return; + } + + const payload = { + clients: clients.map((client: any) => client['@id'] || `/clients/${client.uuid}`) + }; + + this.http.post(`${this.baseUrl}/command-tasks/${this.selectedTask.uuid}/execute`, payload).subscribe({ + next: () => { + this.toastService.success(this.translate.instant('taskExecutedSuccessfully')); + this.dialogRef.close(true); + }, + error: () => { + this.toastService.error(this.translate.instant('errorExecutingTaskNow')); + } + }); + } + + closeModal(): void { + this.dialogRef.close(false); + } +} diff --git a/ogWebconsole/src/locale/cat.json b/ogWebconsole/src/locale/cat.json index 98da817..945a6e4 100644 --- a/ogWebconsole/src/locale/cat.json +++ b/ogWebconsole/src/locale/cat.json @@ -459,6 +459,7 @@ "errorEditingRole1": "Error en editar el rol:", "errorEditingSystem": "Error en editar el sistema operatiu", "errorExecutingGroupOfCommands": "Error executant el grup de comandaments:", + "errorExecutingTaskNow": "Error en executar la tasca", "errorFetchingAvailableCommands": "Error en obtenir els comandaments disponibles", "errorFetchingBranchesForGit": "Error en obtenir les branques per a les dades de Git", "errorFetchingCalendars": "Error en obtenir els calendaris", @@ -589,11 +590,13 @@ "hardwareInventory": "Inventari de maquinari", "runScript": "Executar script", "refresh": "Refrescar", - "deleteLease": "Eliminar concessió DHCP" + "deleteLease": "Eliminar concessió DHCP", + "executeTask": "Executar tasca" }, "executeCommandStepText": "Conjunt d'accions a executar sobre una selecció de clients o unitat organitzativa", "executeCommandTitle": "Executar Comandament", "executeCommandTooltip": "Executar comandament en aquest element", + "executeTaskNowModalTitle": "Executar tasca ara", "execution": "Execució", "executionDate": "Data d'execució", "executionDateTimeSectionTitle": "Data i hora d'execució", @@ -1036,6 +1039,7 @@ "noSoftwareProfileAssigned": "Sense perfil de programari assignat", "notAccessible": "No accessible", "noTags": "Sense etiquetes", + "noTasksAvailableMessage": "No hi ha tasques disponibles", "notAvailableLowercase": "no disponible", "notCalculated": "No calculada", "notDefined": "No definit", @@ -1404,6 +1408,7 @@ "taskCreatedSuccessfully": "Tasca creada correctament", "taskDeletedSuccessfully": "Tasca eliminada correctament", "taskDetailsTitle": "Detalls de la Tasca", + "taskExecutedSuccessfully": "Tasca executada correctament", "taskId": "ID de la Tasca", "taskName": "Nom de la tasca", "tasks": "Tasques", @@ -1512,6 +1517,7 @@ "viewInput": "Veure entrada", "viewOutput": "Veure sortida", "viewParameters": "Veure paràmetres", + "viewScript": "Veure script", "viewPendingTraces": "Veure traces pendents", "viewSentTraces": "Veure traces enviades", "viewSubnets": "Veure Subxarxes", diff --git a/ogWebconsole/src/locale/en.json b/ogWebconsole/src/locale/en.json index a4bc94e..d1f11b2 100644 --- a/ogWebconsole/src/locale/en.json +++ b/ogWebconsole/src/locale/en.json @@ -459,6 +459,7 @@ "errorEditingRole1": "Error editing role:", "errorEditingSystem": "Error editing operating system", "errorExecutingGroupOfCommands": "Error executing command group:", + "errorExecutingTaskNow": "Error executing the task", "errorFetchingAvailableCommands": "Error fetching available commands", "errorFetchingBranchesForGit": "Error fetching branches for Git data", "errorFetchingCalendars": "Error fetching calendars", @@ -589,11 +590,13 @@ "hardwareInventory": "Hardware Inventory", "runScript": "Run Script", "refresh": "Refresh", - "deleteLease": "Delete DHCP Lease" + "deleteLease": "Delete DHCP Lease", + "executeTask": "Run Task" }, "executeCommandStepText": "Set of actions to be performed on a selection of clients or an organizational unit.", "executeCommandTitle": "Execute Command", "executeCommandTooltip": "Execute command on this element", + "executeTaskNowModalTitle": "Run Task Now", "execution": "Execution", "executionDate": "Execution date", "executionDateTimeSectionTitle": "Execution date and time", @@ -1036,6 +1039,7 @@ "noSoftwareProfileAssigned": "No software profile assigned", "notAccessible": "Not accessible", "noTags": "No tags", + "noTasksAvailableMessage": "No tasks available", "notAvailableLowercase": "not available", "notCalculated": "Not calculated", "notDefined": "Not defined", @@ -1404,6 +1408,7 @@ "taskCreatedSuccessfully": "Task created successfully", "taskDeletedSuccessfully": "Task deleted successfully", "taskDetailsTitle": "Task Details", + "taskExecutedSuccessfully": "Task executed successfully", "taskId": "Task ID", "taskName": "Task name", "tasks": "Tasks", @@ -1512,6 +1517,7 @@ "viewInput": "View input", "viewOutput": "View output", "viewParameters": "View parameters", + "viewScript": "View script", "viewPendingTraces": "View pending traces", "viewSentTraces": "View sent traces", "viewSubnets": "View Subnets", diff --git a/ogWebconsole/src/locale/es.json b/ogWebconsole/src/locale/es.json index 2b59169..5c8f219 100644 --- a/ogWebconsole/src/locale/es.json +++ b/ogWebconsole/src/locale/es.json @@ -459,6 +459,7 @@ "errorEditingRole1": "Error al editar el rol:", "errorEditingSystem": "Error al editar el sistema operativo", "errorExecutingGroupOfCommands": "Error ejecutando el grupo de comandos:", + "errorExecutingTaskNow": "Error al ejecutar la tarea", "errorFetchingAvailableCommands": "Error al obtener los comandos disponibles", "errorFetchingBranchesForGit": "Error al obtener las ramas para los datos de Git", "errorFetchingCalendars": "Error al obtener los calendarios", @@ -589,11 +590,13 @@ "hardwareInventory": "Inventario de hardware", "runScript": "Ejecutar script", "refresh": "Refrescar", - "deleteLease": "Eliminar concesión DHCP" + "deleteLease": "Eliminar concesión DHCP", + "executeTask": "Ejecutar tarea" }, "executeCommandStepText": "Conjunto de acciones a ejecutar sobre una selección de clientes o unidad organizativa", "executeCommandTitle": "Ejecutar Comando", "executeCommandTooltip": "Ejecutar comando en este elemento", + "executeTaskNowModalTitle": "Ejecutar tarea ahora", "execution": "Ejecución", "executionDate": "Fecha de ejecución", "executionDateTimeSectionTitle": "Fecha y hora de ejecución", @@ -1036,6 +1039,7 @@ "noSoftwareProfileAssigned": "Sin perfil de software asignado", "notAccessible": "No accesible", "noTags": "Sin tags", + "noTasksAvailableMessage": "No hay tareas disponibles", "notAvailableLowercase": "no disponible", "notCalculated": "No calculada", "notDefined": "No definido", @@ -1404,6 +1408,7 @@ "taskCreatedSuccessfully": "Tarea creada con éxito", "taskDeletedSuccessfully": "Tarea eliminada con éxito", "taskDetailsTitle": "Detalles de la Tarea", + "taskExecutedSuccessfully": "Tarea ejecutada correctamente", "taskId": "ID de la Tarea", "taskName": "Nombre de la tarea", "tasks": "Tareas", @@ -1512,6 +1517,7 @@ "viewInput": "Ver entrada", "viewOutput": "Ver salida", "viewParameters": "Ver parámetros", + "viewScript": "Ver script", "viewPendingTraces": "Ver trazas pendientes", "viewSentTraces": "Ver trazas enviadas", "viewSubnets": "Ver Subredes",