refs #2596. Create task options
testing/ogGui-multibranch/pipeline/head There was a failure building this commit
Details
testing/ogGui-multibranch/pipeline/head There was a failure building this commit
Details
parent
0a14bbd486
commit
3270f75f15
|
@ -33,9 +33,9 @@ export class CommandsTaskComponent implements OnInit {
|
|||
columns = [
|
||||
{ columnDef: 'id', header: 'ID', cell: (task: any) => task.id },
|
||||
{ columnDef: 'name', header: 'Nombre de tarea', cell: (task: any) => task.name },
|
||||
{ columnDef: 'organizationalUnit', header: 'Ámbito', cell: (task: any) => task.organizationalUnit.name },
|
||||
{ columnDef: 'organizationalUnit', header: 'Ámbito', cell: (task: any) => task.scope },
|
||||
{ columnDef: 'management', header: 'Gestiones', cell: (task: any) => task.schedules },
|
||||
{ columnDef: 'nextExecution', header: 'Próxima ejecución', cell: (task: any) => this.datePipe.transform(task.nextExecution, 'dd/MM/yyyy HH:mm:ss', 'UTC') },
|
||||
{ columnDef: 'nextExecution', header: 'Próxima ejecución', cell: (task: any) => this.datePipe.transform(task.nextExecution, 'dd/MM/yyyy HH:mm:ss') },
|
||||
{ columnDef: 'createdBy', header: 'Creado por', cell: (task: any) => task.createdBy },
|
||||
];
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
<input matInput formControlName="executionTime" placeholder="08:00" type="time">
|
||||
</mat-form-field>
|
||||
|
||||
<!-- Mostrar solo si no es 'none' -->
|
||||
<div *ngIf="form.get('recurrenceType')?.value !== 'none'" class="mb-4">
|
||||
<label>Días de la semana:</label>
|
||||
<div class="weekday-toggle-group">
|
||||
|
@ -37,7 +36,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Selección de meses -->
|
||||
<div *ngIf="form.get('recurrenceType')?.value !== 'none'" >
|
||||
<label>Meses:</label>
|
||||
<div class="month-toggle-row" *ngFor="let row of monthRows">
|
||||
|
@ -52,7 +50,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rango de fechas -->
|
||||
<div *ngIf="form.get('recurrenceType')?.value !== 'none'" class="custom-time" formGroupName="recurrenceDetails">
|
||||
<mat-form-field appearance="fill" class="w-half">
|
||||
<mat-label>Desde</mat-label>
|
||||
|
|
|
@ -19,6 +19,14 @@ mat-form-field {
|
|||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
mat-form-field.mat-form-field-disabled {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
mat-form-field.mat-form-field-disabled .mat-form-field-label {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
|
@ -63,3 +71,148 @@ mat-form-field {
|
|||
gap: 1em;
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
/* Estilos para la selección de clientes */
|
||||
.clients-selection {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.clients-selection h4 {
|
||||
margin-bottom: 16px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pre-selected-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
background-color: #e3f2fd;
|
||||
border: 1px solid #2196f3;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 16px;
|
||||
color: #1976d2;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pre-selected-info mat-icon {
|
||||
color: #2196f3;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.loading-clients {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.clients-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.selected-count {
|
||||
font-weight: 500;
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
.clients-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 12px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.client-card {
|
||||
transition: all 0.2s ease;
|
||||
border: 2px solid transparent;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.client-card.pre-selected {
|
||||
border-color: #4caf50;
|
||||
background-color: #e8f5e8;
|
||||
}
|
||||
|
||||
.client-card.pre-selected:hover {
|
||||
border-color: #45a049;
|
||||
background-color: #d4edda;
|
||||
}
|
||||
|
||||
.client-card mat-card-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.client-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.client-name {
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.client-details {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.client-ip {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.client-status {
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-og-live {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status-offline {
|
||||
background-color: #f44336;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status-unknown {
|
||||
background-color: #ff9800;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.selected-icon {
|
||||
color: #1976d2;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 768px) {
|
||||
.clients-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.clients-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,11 @@
|
|||
<mat-dialog-content class="dialog-content">
|
||||
<mat-spinner class="loading-spinner" *ngIf="loading"></mat-spinner>
|
||||
|
||||
<!-- Toggle entre crear o añadir -->
|
||||
<mat-radio-group *ngIf="data?.source === 'assistant'" [(ngModel)]="taskMode" class="task-mode-selection" name="taskMode">
|
||||
<mat-radio-button value="create">Crear tarea</mat-radio-button>
|
||||
<mat-radio-button value="add">Introducir en tarea existente</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
|
||||
<!-- Selección de tarea existente -->
|
||||
<div *ngIf="taskMode === 'add'" class="select-task">
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Seleccione una tarea</mat-label>
|
||||
|
@ -33,7 +31,6 @@
|
|||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Formulario de nueva tarea -->
|
||||
<form *ngIf="taskMode === 'create' && taskForm && !loading" [formGroup]="taskForm" class="task-form">
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>{{ 'nameLabel' | translate }}</mat-label>
|
||||
|
@ -48,15 +45,17 @@
|
|||
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-label>Ámbito</mat-label>
|
||||
<mat-select formControlName="scope" (selectionChange)="onScopeChange($event.value)">
|
||||
<mat-select formControlName="scope" (selectionChange)="onScopeChange($event.value)"
|
||||
[disabled]="data?.clients && data.clients.length >= 1">
|
||||
<mat-option value="organizational-unit">Unidad Organizativa</mat-option>
|
||||
<mat-option value="classrooms-group">Grupo de aulas</mat-option>
|
||||
<mat-option value="classroom">Aulas</mat-option>
|
||||
<mat-option value="clients-group">Grupos de clientes</mat-option>
|
||||
<mat-option value="clients">Clientes</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill" class="full-width">
|
||||
<mat-form-field *ngIf="taskForm.get('scope')?.value !== 'clients'" appearance="fill" class="full-width">
|
||||
<mat-label>{{ 'organizationalUnitLabel' | translate }}</mat-label>
|
||||
<mat-select formControlName="organizationalUnit">
|
||||
<mat-option *ngFor="let unit of availableOrganizationalUnits" [value]="unit['@id']">
|
||||
|
@ -66,6 +65,40 @@
|
|||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div *ngIf="taskForm.get('scope')?.value === 'clients'" class="clients-selection">
|
||||
<h4>Clientes seleccionados</h4>
|
||||
|
||||
<div *ngIf="data?.selectedClients && data.selectedClients.length > 0" class="pre-selected-info">
|
||||
<mat-icon>info</mat-icon>
|
||||
<span>Los clientes han sido pre-seleccionados desde el componente de despliegue de imágenes.</span>
|
||||
</div>
|
||||
|
||||
<div class="clients-list">
|
||||
<div class="clients-grid">
|
||||
<mat-card
|
||||
*ngFor="let client of clients"
|
||||
class="client-card"
|
||||
[class.pre-selected]="isClientPreSelected(client)"
|
||||
>
|
||||
<mat-card-content>
|
||||
<div class="client-info">
|
||||
<div class="client-name">{{ client.name || client.hostname }}</div>
|
||||
<div class="client-details">
|
||||
<span class="client-ip">{{ client.ip }}</span>
|
||||
<span class="client-status" [class]="'status-' + client.status">
|
||||
{{ client.status }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-icon class="selected-icon">
|
||||
check_circle
|
||||
</mat-icon>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-checkbox *ngIf="!editing" formControlName="scheduleAfterCreate">
|
||||
¿Quieres programar la tarea al finalizar su creación?
|
||||
</mat-checkbox>
|
||||
|
|
|
@ -29,6 +29,7 @@ export class CreateTaskComponent implements OnInit {
|
|||
existingTasks: any[] = [];
|
||||
selectedExistingTask: string | null = null;
|
||||
executionOrder: number | null = null;
|
||||
selectedClients: any[] = [];
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
|
@ -41,13 +42,32 @@ export class CreateTaskComponent implements OnInit {
|
|||
) {
|
||||
this.baseUrl = this.configService.apiUrl;
|
||||
this.apiUrl = `${this.baseUrl}/command-tasks`;
|
||||
|
||||
let initialScope = '';
|
||||
if (this.data?.selectedClients && this.data.selectedClients.length > 0) {
|
||||
initialScope = 'clients';
|
||||
} else if (this.data?.scope) {
|
||||
initialScope = this.data.scope;
|
||||
} else if (this.data?.runScriptContext) {
|
||||
initialScope = this.data.runScriptContext.type || '';
|
||||
}
|
||||
|
||||
this.taskForm = this.fb.group({
|
||||
scope: [ this.data?.scope ? this.data.scope : '', Validators.required],
|
||||
scope: [initialScope, Validators.required],
|
||||
name: ['', Validators.required],
|
||||
organizationalUnit: [ this.data?.organizationalUnit ? this.data.organizationalUnit : null, Validators.required],
|
||||
organizationalUnit: [ this.data?.organizationalUnit ? this.data.organizationalUnit : null],
|
||||
notes: [''],
|
||||
scheduleAfterCreate: [false]
|
||||
});
|
||||
|
||||
if (this.data?.selectedClients && Array.isArray(this.data.selectedClients)) {
|
||||
this.selectedClients = [...this.data.selectedClients];
|
||||
this.clients = [...this.data.selectedClients];
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.onScopeChange(initialScope);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -111,10 +131,31 @@ export class CreateTaskComponent implements OnInit {
|
|||
}
|
||||
|
||||
onScopeChange(scope: string): void {
|
||||
this.filterUnits(scope).subscribe(filteredUnits => {
|
||||
this.availableOrganizationalUnits = filteredUnits;
|
||||
if (scope === 'clients') {
|
||||
if (this.data?.selectedClients && this.data.selectedClients.length > 0) {
|
||||
this.selectedClients = [...this.data.selectedClients];
|
||||
this.clients = [...this.data.selectedClients];
|
||||
} else {
|
||||
this.toastr.error('No hay clientes pre-seleccionados para este ámbito');
|
||||
this.taskForm.get('scope')?.setValue('');
|
||||
return;
|
||||
}
|
||||
this.taskForm.get('organizationalUnit')?.setValue('');
|
||||
});
|
||||
this.taskForm.get('organizationalUnit')?.clearValidators();
|
||||
} else {
|
||||
this.filterUnits(scope).subscribe(filteredUnits => {
|
||||
this.availableOrganizationalUnits = filteredUnits;
|
||||
if (!this.data?.organizationalUnit) {
|
||||
this.taskForm.get('organizationalUnit')?.setValue('');
|
||||
}
|
||||
this.taskForm.get('organizationalUnit')?.setValidators(Validators.required);
|
||||
});
|
||||
}
|
||||
this.taskForm.get('organizationalUnit')?.updateValueAndValidity();
|
||||
}
|
||||
|
||||
isClientPreSelected(client: any): boolean {
|
||||
return this.data?.selectedClients && this.data.selectedClients.some((c: any) => c.uuid === client.uuid);
|
||||
}
|
||||
|
||||
startUnitsFilter(): Promise<void> {
|
||||
|
@ -209,14 +250,31 @@ export class CreateTaskComponent implements OnInit {
|
|||
}
|
||||
|
||||
const formData = this.taskForm.value;
|
||||
const scope = formData.scope;
|
||||
|
||||
if (scope === 'clients' && this.selectedClients.length === 0) {
|
||||
this.toastr.error('Debe seleccionar al menos un cliente');
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope !== 'clients' && !formData.organizationalUnit) {
|
||||
this.toastr.error('Debe seleccionar una unidad organizativa');
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: any = {
|
||||
name: formData.name,
|
||||
scope: formData.scope,
|
||||
organizationalUnit: formData.organizationalUnit,
|
||||
notes: formData.notes || '',
|
||||
};
|
||||
|
||||
if (scope === 'clients') {
|
||||
payload.clients = this.selectedClients.map(client => client.uuid);
|
||||
payload.organizationalUnit = null;
|
||||
} else {
|
||||
payload.organizationalUnit = formData.organizationalUnit;
|
||||
}
|
||||
|
||||
if (this.editing) {
|
||||
const taskId = this.data.task.uuid;
|
||||
this.http.patch<any>(`${this.apiUrl}/${taskId}`, payload).subscribe({
|
||||
|
|
|
@ -28,7 +28,7 @@ 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', 'UTC') },
|
||||
{ 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 }
|
||||
|
|
|
@ -107,18 +107,18 @@ export class ExecuteCommandComponent implements OnInit {
|
|||
|
||||
this.arrayCommands = this.arrayCommands.map(command => {
|
||||
if (allOffOrDisconnected) {
|
||||
command.disabled = command.slug !== 'power-on';
|
||||
command.disabled = command.slug !== 'power-on' && command.slug !== 'create-image';
|
||||
} else if (allSameState) {
|
||||
if (states[0] === 'off' || states[0] === 'disconnected') {
|
||||
command.disabled = command.slug !== 'power-on';
|
||||
command.disabled = !['power-on', 'create-image'].includes(command.slug);
|
||||
} else {
|
||||
command.disabled = !['power-off', 'reboot', 'login', 'create-image', 'deploy-image', 'remove-cache-image', 'partition', 'run-script', 'software-inventory'].includes(command.slug);
|
||||
}
|
||||
} else {
|
||||
if (command.slug === 'create-image'|| command.slug === 'software-inventory') {
|
||||
if (command.slug === 'software-inventory') {
|
||||
command.disabled = multipleClients;
|
||||
} else if (
|
||||
['power-on', 'power-off', 'reboot', 'login', 'deploy-image', 'partition', 'remove-cache-image', 'run-script'].includes(command.slug)
|
||||
['power-on', 'power-off', 'reboot', 'login', 'deploy-image', 'partition', 'remove-cache-image', 'run-script', 'create-image'].includes(command.slug)
|
||||
) {
|
||||
command.disabled = false;
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue