develop #23

Merged
maranda merged 12 commits from develop into main 2025-05-19 16:58:24 +02:00
18 changed files with 208 additions and 188 deletions

View File

@ -1,4 +1,13 @@
# Changelog # Changelog
## [0.13.0] - 2025-05-20
### Added
- Se ha añadido nuevo campo "SSL_ENABLED" en el apartado configuracion.
### Improved
- Mejoras en el asistente de particionado.
- Se han añadido mejoras en los filtros de las trazas.
---
## [0.12.0] - 2025-5-13 ## [0.12.0] - 2025-5-13
### Added ### Added
- Se ha añadido un nuevo modal del detalle de las acciones ejecutadas por cada cliente. - Se ha añadido un nuevo modal del detalle de las acciones ejecutadas por cada cliente.

View File

@ -16,12 +16,20 @@
<ng-container matColumnDef="value"> <ng-container matColumnDef="value">
<mat-header-cell *matHeaderCellDef> Valor </mat-header-cell> <mat-header-cell *matHeaderCellDef> Valor </mat-header-cell>
<mat-cell *matCellDef="let variable"> <mat-cell *matCellDef="let variable">
<mat-form-field class="value-input"> <!-- Si es booleano, usamos checkbox -->
<mat-checkbox *ngIf="isBoolean(variable.value)"
[checked]="variable.value === 'true'"
(change)="variable.value = $event.checked ? 'true' : 'false'">
</mat-checkbox>
<!-- Si no es booleano, usamos input -->
<mat-form-field *ngIf="!isBoolean(variable.value)" class="value-input">
<input matInput [(ngModel)]="variable.value" /> <input matInput [(ngModel)]="variable.value" />
</mat-form-field> </mat-form-field>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table> </mat-table>

View File

@ -31,12 +31,15 @@ export class EnvVarsComponent {
this.envVars = Object.entries(response.vars).map(([name, value]) => ({ name, value })); this.envVars = Object.entries(response.vars).map(([name, value]) => ({ name, value }));
}, },
error: (err) => { error: (err) => {
console.error('Error al cargar las variables de entorno:', err);
this.toastService.error('No se pudieron cargar las variables de entorno.'); this.toastService.error('No se pudieron cargar las variables de entorno.');
} }
}); });
} }
isBoolean(value: string): boolean {
return value === 'true' || value === 'false';
}
saveEnvVars(): void { saveEnvVars(): void {
const vars = this.envVars.reduce((acc, variable) => { const vars = this.envVars.reduce((acc, variable) => {
acc[variable.name] = variable.value; acc[variable.name] = variable.value;

View File

@ -115,6 +115,7 @@ export class CreateTaskScheduleComponent implements OnInit{
} }
convertDateToLocalISO(date: Date): string { convertDateToLocalISO(date: Date): string {
date = new Date(date);
const adjustedDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); const adjustedDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
return adjustedDate.toISOString(); return adjustedDate.toISOString();
} }

View File

@ -75,6 +75,7 @@ export class CreateCommandComponent implements OnInit{
readOnly: this.createCommandForm.value.readOnly, readOnly: this.createCommandForm.value.readOnly,
enabled: this.createCommandForm.value.enabled, enabled: this.createCommandForm.value.enabled,
comments: this.createCommandForm.value.comments, comments: this.createCommandForm.value.comments,
parameters: this.createCommandForm.value.parameters,
}; };
if (this.commandId) { if (this.commandId) {

View File

@ -10,11 +10,11 @@
</h4> </h4>
</div> </div>
<div class="button-row"> <div class="button-row">
<button class="action-button" [disabled]="data.status === 'busy' || !selectedModelClient || !allSelected || !selectedDisk" (click)="save()">Ejecutar</button> <button class="action-button" [disabled]="data.status === 'busy' || !selectedModelClient || !allSelected || !selectedDisk || (selectedDisk.totalDiskSize - selectedDisk.used) <= 0" (click)="save()">Ejecutar</button>
</div> </div>
<div> <div>
<button mat-stroked-button color="accent" [disabled]="data.status === 'busy' || !selectedModelClient || !allSelected || !selectedDisk" (click)="openScheduleModal()"> <button mat-stroked-button color="accent" [disabled]="data.status === 'busy' || !selectedModelClient || !allSelected || !selectedDisk || (selectedDisk.totalDiskSize - selectedDisk.used) <= 0" (click)="openScheduleModal()">
<mat-icon>schedule</mat-icon> Opciones de programación <mat-icon>schedule</mat-icon> Opciones de programación
</button> </button>
</div> </div>
@ -101,15 +101,15 @@
<div class="disk-space-info-container"> <div class="disk-space-info-container">
<div class="disk-space-info" [ngClass]="selectedDisk.used < selectedDisk.totalDiskSize ? 'chip-free' : 'chip-full'"> <div class="disk-space-info" [ngClass]="selectedDisk.used < selectedDisk.totalDiskSize ? 'chip-free' : 'chip-full'">
Espacio usado: {{ selectedDisk.used }} MB Espacio usado: {{ selectedDisk.used | number:'1.2-2' }} MB
</div> </div>
<div class="disk-space-info" [ngClass]="selectedDisk.used < selectedDisk.totalDiskSize ? 'chip-free' : 'chip-full'"> <div class="disk-space-info" [ngClass]="selectedDisk.used < selectedDisk.totalDiskSize ? 'chip-free' : 'chip-full'">
Espacio libre: {{ selectedDisk.totalDiskSize - selectedDisk.used}} MB Espacio libre: {{ (selectedDisk.totalDiskSize - selectedDisk.used) | number:'1.2-2' }} MB
</div> </div>
<div class="disk-space-info"> <div class="disk-space-info">
Espacio total: {{ selectedDisk.totalDiskSize }} MB Espacio total: {{ selectedDisk.totalDiskSize | number:'1.2-2' }} MB
</div> </div>
</div> </div>

View File

@ -109,8 +109,8 @@ export class PartitionAssistantComponent implements OnInit{
this.http.get(url).subscribe( this.http.get(url).subscribe(
(response) => { (response) => {
this.data = response; this.data = response;
this.initializeDisks();
this.partitionCode = this.data.partitions[0].partitionCode; this.partitionCode = this.data.partitions[0].partitionCode;
this.initializeDisks();
}, },
(error) => { (error) => {
console.error('Error al cargar los datos del cliente:', error); console.error('Error al cargar los datos del cliente:', error);
@ -160,11 +160,11 @@ export class PartitionAssistantComponent implements OnInit{
disk!.partitions.push({ disk!.partitions.push({
uuid: partition.uuid, uuid: partition.uuid,
partitionNumber: partition.partitionNumber, partitionNumber: partition.partitionNumber,
size: this.convertBytesToGB(partition.size), size: this.convertBytesToGB(partition.partitionNumber === 1 && this.partitionCode === 'GPT' ? 512 : partition.size),
memoryUsage: partition.memoryUsage, memoryUsage: partition.memoryUsage,
partitionCode: partition.partitionCode, partitionCode: partition.partitionNumber === 1 && this.partitionCode === 'GPT' ? 'EFI' : partition.partitionCode,
filesystem: partition.filesystem, filesystem: partition.filesystem,
sizeBytes: partition.size, sizeBytes: partition.partitionNumber === 1 && this.partitionCode === 'GPT' ? 512 : partition.size,
format: false, format: false,
color: '#1f1b91', color: '#1f1b91',
percentage: 0, percentage: 0,
@ -254,8 +254,6 @@ export class PartitionAssistantComponent implements OnInit{
if (disk) { if (disk) {
const remainingGB = this.getRemainingGB(disk.partitions, disk.totalDiskSize); const remainingGB = this.getRemainingGB(disk.partitions, disk.totalDiskSize);
console.log('Remaining GB:', remainingGB);
console.log('Total Disk Size:', disk);
if (remainingGB > 0) { if (remainingGB > 0) {
const removedPartitions = disk.partitions.filter((p) => !p.removed); const removedPartitions = disk.partitions.filter((p) => !p.removed);

View File

@ -101,7 +101,7 @@
<div class="script-preview" [innerHTML]="scriptContent"></div> <div class="script-preview" [innerHTML]="scriptContent"></div>
</div> </div>
<div class="script-params" *ngIf="parameterNames.length > 0"> <div class="script-params" *ngIf="parameterNames.length > 0 && selectedScript.parameters">
<h3>Ingrese los parámetros:</h3> <h3>Ingrese los parámetros:</h3>
<div *ngFor="let paramName of parameterNames"> <div *ngFor="let paramName of parameterNames">
<mat-form-field appearance="fill" class="full-width"> <mat-form-field appearance="fill" class="full-width">

View File

@ -77,15 +77,12 @@ export class RunScriptAssistantComponent implements OnInit{
if (!ctx) { if (!ctx) {
return ''; return '';
} }
// Si es un array de clientes
if (Array.isArray(ctx)) { if (Array.isArray(ctx)) {
return ctx.map(c => c.name).join(', '); return ctx.map(c => c.name).join(', ');
} }
// Si es un objeto con propiedad name
if (typeof ctx === 'object' && 'name' in ctx) { if (typeof ctx === 'object' && 'name' in ctx) {
return ctx.name; return ctx.name;
} }
// Si es un string plano
return String(ctx); return String(ctx);
} }

View File

@ -177,12 +177,15 @@ export class ManageOrganizationalUnitComponent implements OnInit {
} }
onParentChange(event: any): void { onParentChange(event: any): void {
if (!this.isEditMode) {
this.setOrganizationalUnitDefaults(event.value); this.setOrganizationalUnitDefaults(event.value);
} }
}
setOrganizationalUnitDefaults(unitId: string): void { setOrganizationalUnitDefaults(unitId: string): void {
const selectedUnit: any = this.parentUnitsWithPaths.find(unit => unit.id === unitId); const selectedUnit: any = this.parentUnitsWithPaths.find(unit => unit.id === unitId);
if (selectedUnit) { const exclude = this.generalFormGroup.get('excludeParentChanges')?.value;
if (selectedUnit && !exclude) {
this.networkSettingsFormGroup.patchValue({ this.networkSettingsFormGroup.patchValue({
repository: selectedUnit.repository || null, repository: selectedUnit.repository || null,
hardwareProfile: selectedUnit.hardwareProfile || null, hardwareProfile: selectedUnit.hardwareProfile || null,
@ -204,6 +207,127 @@ export class ManageOrganizationalUnitComponent implements OnInit {
} }
} }
loadData(uuid: string): Promise<void> {
return new Promise((resolve, reject) => {
const url = `${this.baseUrl}/organizational-units/${uuid}`;
this.http.get<any>(url).subscribe(
data => {
const exclude = data.excludeParentChanges;
this.generalFormGroup.patchValue({
name: data.name,
parent: data.parent ? data.parent['@id'] : '',
description: data.description,
type: data.type,
excludeParentChanges: exclude
});
this.additionalInfoFormGroup.patchValue({
comments: data.comments
});
this.networkSettingsFormGroup.patchValue({
proxy: data.networkSettings?.proxy,
dns: data.networkSettings?.dns,
netmask: data.networkSettings?.netmask,
router: data.networkSettings?.router,
ntp: data.networkSettings?.ntp,
netiface: data.networkSettings?.netiface,
p2pMode: data.networkSettings?.p2pMode,
p2pTime: data.networkSettings?.p2pTime,
mcastIp: data.networkSettings?.mcastIp,
mcastSpeed: data.networkSettings?.mcastSpeed,
mcastPort: data.networkSettings?.mcastPort,
mcastMode: data.networkSettings?.mcastMode,
menu: data.networkSettings?.menu ? data.networkSettings.menu['@id'] : null,
hardwareProfile: data.networkSettings?.hardwareProfile ? data.networkSettings.hardwareProfile['@id'] : null,
ogLive: data.networkSettings?.ogLive ? data.networkSettings.ogLive['@id'] : null,
repository: data.networkSettings?.repository ? data.networkSettings.repository['@id'] : null,
pxeTemplate: data.networkSettings?.pxeTemplate ? data.networkSettings.pxeTemplate['@id'] : null
});
this.classroomInfoFormGroup.patchValue({
location: data.location,
projector: data.projector,
board: data.board,
capacity: data.capacity,
remoteCalendar: data.remoteCalendar ? data.remoteCalendar['@id'] : null
});
resolve();
},
error => {
console.error('Error fetching data for edit:', error);
this.toastService.error('Error fetching data');
reject(error);
this.onNoClick();
}
);
});
}
onSubmit() {
if (this.generalFormGroup.valid && this.additionalInfoFormGroup.valid && this.networkSettingsFormGroup.valid) {
const parentValue = this.generalFormGroup.value.parent;
const formData = {
name: this.generalFormGroup.value.name,
excludeParentChanges: this.generalFormGroup.value.excludeParentChanges,
parent: parentValue || null,
description: this.generalFormGroup.value.description,
comments: this.additionalInfoFormGroup.value.comments,
remoteCalendar: this.generalFormGroup.value.remoteCalendar,
type: this.generalFormGroup.value.type,
networkSettings: this.networkSettingsFormGroup.value,
location: this.classroomInfoFormGroup.value.location,
projector: this.classroomInfoFormGroup.value.projector,
board: this.classroomInfoFormGroup.value.board,
capacity: this.classroomInfoFormGroup.value.capacity,
};
if (this.isEditMode) {
const putUrl = `${this.baseUrl}/organizational-units/${this.data.uuid}`;
const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
this.http.put<any>(putUrl, formData, { headers }).subscribe(
response => {
this.unitAdded.emit();
this.dialogRef.close({ success: true });
this.toastService.success('Editado exitosamente', 'Éxito');
},
error => {
console.error('Error al realizar PUT:', error);
const errorMessages = error.error['hydra:description'].split('\n');
errorMessages.forEach((message: string) => {
const cleanedMessage = message.replace(/networkSettings\.(\w+):/, 'Error $1:');
this.toastService.error(cleanedMessage);
});
}
);
} else {
const postUrl = `${this.baseUrl}/organizational-units`;
const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
this.http.post<any>(postUrl, formData, { headers }).subscribe(
response => {
this.unitAdded.emit(response);
this.dialogRef.close({ success: true });
this.toastService.success('Creado exitosamente', 'Éxito');
},
error => {
console.error('Error al realizar POST:', error);
const errorMessages = error.error['hydra:description'].split('\n');
errorMessages.forEach((message: string) => {
const cleanedMessage = message.replace(/networkSettings\.(\w+):/, 'Error $1:');
this.toastService.error(cleanedMessage);
});
}
);
}
}
}
getSelectedParentName(): string | undefined { getSelectedParentName(): string | undefined {
const parentId = this.generalFormGroup.get('parent')?.value; const parentId = this.generalFormGroup.get('parent')?.value;
return this.parentUnitsWithPaths.find(unit => unit.id === parentId)?.name; return this.parentUnitsWithPaths.find(unit => unit.id === parentId)?.name;
@ -325,121 +449,6 @@ export class ManageOrganizationalUnitComponent implements OnInit {
this.networkSettingsFormGroup.value.pxeTemplate = event.value; this.networkSettingsFormGroup.value.pxeTemplate = event.value;
} }
loadData(uuid: string): Promise<void> {
return new Promise((resolve, reject) => {
const url = `${this.baseUrl}/organizational-units/${uuid}`;
this.http.get<any>(url).subscribe(
data => {
this.generalFormGroup.patchValue({
name: data.name,
parent: data.parent ? data.parent['@id'] : '',
description: data.description,
type: data.type,
excludeParentChanges: data.excludeParentChanges
});
this.additionalInfoFormGroup.patchValue({
comments: data.comments
});
this.networkSettingsFormGroup.patchValue({
proxy: data.networkSettings?.proxy,
dns: data.networkSettings?.dns,
netmask: data.networkSettings?.netmask,
router: data.networkSettings?.router,
ntp: data.networkSettings?.ntp,
netiface: data.networkSettings?.netiface,
p2pMode: data.networkSettings?.p2pMode,
p2pTime: data.networkSettings?.p2pTime,
mcastIp: data.networkSettings?.mcastIp,
mcastSpeed: data.networkSettings?.mcastSpeed,
mcastPort: data.networkSettings?.mcastPort,
mcastMode: data.networkSettings?.mcastMode,
menu: data.networkSettings?.menu ? data.networkSettings.menu['@id'] : null,
hardwareProfile: data.networkSettings?.hardwareProfile ? data.networkSettings.hardwareProfile['@id'] : null,
ogLive: data.networkSettings?.ogLive ? data.networkSettings.ogLive['@id'] : null,
repository: data.networkSettings?.repository ? data.networkSettings.repository['@id'] : null,
pxeTemplate: data.networkSettings?.pxeTemplate ? data.networkSettings.pxeTemplate['@id'] : null
});
this.classroomInfoFormGroup.patchValue({
location: data.location,
projector: data.projector,
board: data.board,
capacity: data.capacity,
remoteCalendar: data.remoteCalendar ? data.remoteCalendar['@id'] : null
});
resolve();
},
error => {
console.error('Error fetching data for edit:', error);
this.toastService.error('Error fetching data');
reject(error);
this.onNoClick();
}
);
});
}
onSubmit() {
if (this.generalFormGroup.valid && this.additionalInfoFormGroup.valid && this.networkSettingsFormGroup.valid) {
const parentValue = this.generalFormGroup.value.parent;
const formData = {
name: this.generalFormGroup.value.name,
excludeParentChanges: this.generalFormGroup.value.excludeParentChanges,
parent: parentValue || null,
description: this.generalFormGroup.value.description,
comments: this.additionalInfoFormGroup.value.comments,
remoteCalendar: this.generalFormGroup.value.remoteCalendar,
type: this.generalFormGroup.value.type,
networkSettings: this.networkSettingsFormGroup.value,
location: this.classroomInfoFormGroup.value.location,
projector: this.classroomInfoFormGroup.value.projector,
board: this.classroomInfoFormGroup.value.board,
capacity: this.classroomInfoFormGroup.value.capacity,
};
if (this.isEditMode) {
const putUrl = `${this.baseUrl}/organizational-units/${this.data.uuid}`;
const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
this.http.put<any>(putUrl, formData, { headers }).subscribe(
response => {
this.unitAdded.emit();
this.dialogRef.close({ success: true });
this.toastService.success('Editado exitosamente', 'Éxito');
},
error => {
console.error('Error al realizar PUT:', error);
const errorMessages = error.error['hydra:description'].split('\n');
errorMessages.forEach((message: string) => {
const cleanedMessage = message.replace(/networkSettings\.(\w+):/, 'Error $1:');
this.toastService.error(cleanedMessage);
});
}
);
} else {
const postUrl = `${this.baseUrl}/organizational-units`;
const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
this.http.post<any>(postUrl, formData, { headers }).subscribe(
response => {
this.unitAdded.emit(response);
this.dialogRef.close({ success: true });
this.toastService.success('Creado exitosamente', 'Éxito');
},
error => {
console.error('Error al realizar POST:', error);
const errorMessages = error.error['hydra:description'].split('\n');
errorMessages.forEach((message: string) => {
const cleanedMessage = message.replace(/networkSettings\.(\w+):/, 'Error $1:');
this.toastService.error(cleanedMessage);
});
}
);
}
}
}
onNoClick(): void { onNoClick(): void {
this.dialogRef.close({ success: false }); this.dialogRef.close({ success: false });
} }

View File

@ -129,8 +129,7 @@ export class CreatePxeTemplateComponent implements OnInit {
this.dialogRef.close(true); this.dialogRef.close(true);
}, },
error: error => { error: error => {
this.toastService.error('Error al actualizar la plantilla PXE'); this.toastService.error(error.error['hydra:description']);
this.dialogRef.close(false);
} }
}); });
} }

View File

@ -5,7 +5,7 @@
</button> </button>
<div class="header-container-title"> <div class="header-container-title">
<h2 joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'adminCommandsTitle' | <h2 joyrideStep="tracesTitleStep" text="{{ 'tracesTitleStepText' | translate }}">{{ 'adminCommandsTitle' |
translate }}</h2> translate }}</h2>
</div> </div>
@ -17,7 +17,7 @@
</div> </div>
</div> </div>
<div class="search-container"> <div class="search-container" joyrideStep="filtersStep" text="{{ 'filtersStepText' | translate }}">
<mat-form-field appearance="fill" class="search-select"> <mat-form-field appearance="fill" class="search-select">
<mat-label>{{ 'commandSelectStepText' | translate }}</mat-label> <mat-label>{{ 'commandSelectStepText' | translate }}</mat-label>
<mat-select (selectionChange)="onOptionCommandSelected($event.value)" #commandSearchInput> <mat-select (selectionChange)="onOptionCommandSelected($event.value)" #commandSearchInput>
@ -52,8 +52,8 @@
<app-loading [isLoading]="loading"></app-loading> <app-loading [isLoading]="loading"></app-loading>
<div *ngIf="!loading"> <div *ngIf="!loading">
<table mat-table [dataSource]="traces" class="mat-elevation-z8" joyrideStep="tableStep" <table mat-table [dataSource]="traces" class="mat-elevation-z8" joyrideStep="tracesTableStep"
text="{{ 'tableStepText' | translate }}"> text="{{ 'tracesTableStepText' | translate }}">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef"> <ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th> <th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let trace"> <td mat-cell *matCellDef="let trace">
@ -128,8 +128,8 @@
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="actions" joyrideStep="actionsStep" text="{{ 'actionsStepText' | translate }}"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef style="text-align: center;">{{ 'columnActions' | translate }}</th> <th mat-header-cell *matHeaderCellDef style="text-align: center;">{{ 'informationLabel' | translate }}</th>
<td mat-cell *matCellDef="let trace" style="text-align: center;"> <td mat-cell *matCellDef="let trace" style="text-align: center;">
<button mat-icon-button color="primary" [disabled]="!trace.input" (click)="openInputModal(trace.input)"> <button mat-icon-button color="primary" [disabled]="!trace.input" (click)="openInputModal(trace.input)">
<mat-icon> <mat-icon>

View File

@ -35,7 +35,6 @@ export class ClientTaskLogsComponent implements OnInit {
mode: ProgressBarMode = 'buffer'; mode: ProgressBarMode = 'buffer';
progress = 0; progress = 0;
bufferValue = 0; bufferValue = 0;
dateRange = new FormControl();
filteredCommands2 = Object.keys(COMMAND_TYPES).map(key => ({ filteredCommands2 = Object.keys(COMMAND_TYPES).map(key => ({
name: key, name: key,
@ -135,7 +134,9 @@ export class ClientTaskLogsComponent implements OnInit {
} }
onOptionCommandSelected(selectedCommand: any): void { onOptionCommandSelected(selectedCommand: any): void {
this.filters['command'] = selectedCommand.id; this.filters['command'] = selectedCommand.id || selectedCommand.uuid;
console.log('Comando seleccionado:', selectedCommand);
console.log('Valor que se usará para el filtro:', selectedCommand.name);
this.loadTraces(); this.loadTraces();
} }
@ -191,22 +192,13 @@ export class ClientTaskLogsComponent implements OnInit {
.set('itemsPerPage', this.itemsPerPage.toString()); .set('itemsPerPage', this.itemsPerPage.toString());
if (this.filters['command']) { if (this.filters['command']) {
params = params.set('command.id', this.filters['command']); params = params.set('command.uuid', this.filters['command']);
} }
if (this.filters['status']) { if (this.filters['status']) {
params = params.set('status', this.filters['status']); params = params.set('status', this.filters['status']);
} }
const range = this.dateRange?.value;
if (range?.start && range?.end) {
const fromDate = this.datePipe.transform(range.start, 'yyyy-MM-dd');
const toDate = this.datePipe.transform(range.end, 'yyyy-MM-dd');
params = params.set('executedAt[after]', fromDate!);
params = params.set('executedAt[before]', toDate!);
}
const url = `${this.baseUrl}/traces`; const url = `${this.baseUrl}/traces`;
this.http.get<any>(url, { params }).subscribe( this.http.get<any>(url, { params }).subscribe(
@ -240,7 +232,6 @@ export class ClientTaskLogsComponent implements OnInit {
this.loading = true; this.loading = true;
clientSearchCommandInput.value = null; clientSearchCommandInput.value = null;
clientSearchStatusInput.value = null; clientSearchStatusInput.value = null;
this.dateRange.reset();
this.filters = {}; this.filters = {};
this.loadTraces(); this.loadTraces();
} }
@ -290,11 +281,10 @@ export class ClientTaskLogsComponent implements OnInit {
iniciarTour(): void { iniciarTour(): void {
this.joyrideService.startTour({ this.joyrideService.startTour({
steps: [ steps: [
'titleStep', 'tracesTitleStep',
'resetFiltersStep', 'resetFiltersStep',
'clientSelectStep', 'filtersStep',
'commandSelectStep', 'tracesTableStep',
'tableStep',
'paginationStep' 'paginationStep'
], ],
showPrevButton: true, showPrevButton: true,

View File

@ -4,7 +4,7 @@
</button> </button>
<div class="header-container-title"> <div class="header-container-title">
<h2 joyrideStep="titleStep" text="{{ 'titleStepText' | translate }}">{{ 'adminCommandsTitle' | <h2 joyrideStep="tracesTitleStep" text="{{ 'tracesTitleStepText' | translate }}">{{ 'adminCommandsTitle' |
translate }}</h2> translate }}</h2>
</div> </div>
@ -16,9 +16,8 @@
</div> </div>
</div> </div>
<div class="search-container"> <div class="search-container" joyrideStep="filtersStep" text="{{ 'filtersStepText' | translate }}">
<mat-form-field appearance="fill" class="search-select" joyrideStep="clientSelectStep" <mat-form-field appearance="fill" class="search-select">
text="{{ 'clientSelectStepText' | translate }}">
<input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto" #commandClientInput <input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto" #commandClientInput
placeholder="{{ 'filterClientPlaceholder' | translate }}"> placeholder="{{ 'filterClientPlaceholder' | translate }}">
<mat-autocomplete #clientAuto="matAutocomplete" [displayWith]="displayFnClient" <mat-autocomplete #clientAuto="matAutocomplete" [displayWith]="displayFnClient"
@ -74,8 +73,8 @@
<app-loading [isLoading]="loading"></app-loading> <app-loading [isLoading]="loading"></app-loading>
<div *ngIf="!loading"> <div *ngIf="!loading">
<table mat-table [dataSource]="traces" class="mat-elevation-z8" joyrideStep="tableStep" <table mat-table [dataSource]="traces" class="mat-elevation-z8" joyrideStep="tracesTableStep"
text="{{ 'tableStepText' | translate }}"> text="{{ 'tracesTableStepText' | translate }}">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef"> <ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th> <th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let trace"> <td mat-cell *matCellDef="let trace">
@ -150,10 +149,11 @@
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="actions" joyrideStep="actionsStep" text="{{ 'actionsStepText' | translate }}"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef style="text-align: center;">{{ 'columnActions' | translate }}</th> <th mat-header-cell *matHeaderCellDef style="text-align: center;">{{ 'informationLabel' | translate }}</th>
<td mat-cell *matCellDef="let trace" style="text-align: center;"> <td mat-cell *matCellDef="let trace" style="text-align: center;">
<button mat-icon-button color="primary" [disabled]="!trace.input" (click)="openInputModal(trace.input)"> <button mat-icon-button color="primary" [disabled]="!trace.input || trace.input.length === 0"
(click)="openInputModal(trace.input)">
<mat-icon> <mat-icon>
<span class="material-symbols-outlined"> <span class="material-symbols-outlined">
mode_comment mode_comment

View File

@ -323,11 +323,10 @@ export class TaskLogsComponent implements OnInit {
iniciarTour(): void { iniciarTour(): void {
this.joyrideService.startTour({ this.joyrideService.startTour({
steps: [ steps: [
'titleStep', 'tracesTitleStep',
'resetFiltersStep', 'resetFiltersStep',
'clientSelectStep', 'filtersStep',
'commandSelectStep', 'tracesTableStep',
'tableStep',
'paginationStep' 'paginationStep'
], ],
showPrevButton: true, showPrevButton: true,

View File

@ -34,6 +34,7 @@
<span>{{ 'commands' | translate }}</span> <span>{{ 'commands' | translate }}</span>
</span> </span>
</mat-list-item> </mat-list-item>
<!--
<mat-list-item routerLink="/commands-groups" (click)="onItemClick()" <mat-list-item routerLink="/commands-groups" (click)="onItemClick()"
matTooltip="{{ 'TOOLTIP_COMMAND_GROUPS' | translate }}" matTooltipShowDelay="1000"> matTooltip="{{ 'TOOLTIP_COMMAND_GROUPS' | translate }}" matTooltipShowDelay="1000">
<span class="entry"> <span class="entry">
@ -41,6 +42,7 @@
<span>{{ 'commandGroups' | translate }}</span> <span>{{ 'commandGroups' | translate }}</span>
</span> </span>
</mat-list-item> </mat-list-item>
-->
<mat-list-item routerLink="/commands-task" (click)="onItemClick()" matTooltip="{{ 'TOOLTIP_TASKS' | translate }}" <mat-list-item routerLink="/commands-task" (click)="onItemClick()" matTooltip="{{ 'TOOLTIP_TASKS' | translate }}"
matTooltipShowDelay="1000"> matTooltipShowDelay="1000">
<span class="entry"> <span class="entry">

View File

@ -80,7 +80,7 @@
"searchGroupNameLabel": "Search group name", "searchGroupNameLabel": "Search group name",
"loadingStepText": "Wait while the command groups are loading.", "loadingStepText": "Wait while the command groups are loading.",
"viewCommands": "View commands", "viewCommands": "View commands",
"paginationStepText": "Navigate between command group pages using the paginator.", "paginationStepText": "Navigate through pages using the paginator.",
"commandGroupDetailsTitle": "Command Group Details", "commandGroupDetailsTitle": "Command Group Details",
"createdBy": "Created by", "createdBy": "Created by",
"groupId": "Group ID", "groupId": "Group ID",
@ -527,6 +527,8 @@
"gitImage": "Git images", "gitImage": "Git images",
"gitImageStep": "Git images", "gitImageStep": "Git images",
"gitImageStepText": "On this screen you can manage git images.", "gitImageStepText": "On this screen you can manage git images.",
"partitions": "Particiones", "isDefaultLabel": "Default",
"isDefaultLabel": "Default" "tracesTitleStepText": "In this screen, you can see the execution traces of each client, with its id, command, real-time status, date and actions to be performed.",
"filtersStepText": "Here you can see the different filters to apply to the table information.",
"tracesTableStepText": "This is the table with the execution traces updated in real time."
} }

View File

@ -25,7 +25,7 @@
"back": "Atrás", "back": "Atrás",
"addClientDialogTitle": "Añadir Cliente", "addClientDialogTitle": "Añadir Cliente",
"dialogTitleEditUser": "Editar usuario", "dialogTitleEditUser": "Editar usuario",
"dialogTitleChangePassword": "Change password", "dialogTitleChangePassword": "Cambiar contraseña",
"labelCurrentPassword": "Contraseña actual", "labelCurrentPassword": "Contraseña actual",
"labelNewPassword": "Nueva contraseña", "labelNewPassword": "Nueva contraseña",
"labelRepeatPassword": "Repite la contraseña", "labelRepeatPassword": "Repite la contraseña",
@ -79,7 +79,7 @@
"searchGroupNameLabel": "Buscar nombre de grupo", "searchGroupNameLabel": "Buscar nombre de grupo",
"loadingStepText": "Espera mientras se cargan los grupos de comandos.", "loadingStepText": "Espera mientras se cargan los grupos de comandos.",
"viewCommands": "Ver comandos", "viewCommands": "Ver comandos",
"paginationStepText": "Navega entre las páginas de grupos de comandos usando el paginador.", "paginationStepText": "Navega entre las páginas usando el paginador.",
"commandGroupDetailsTitle": "Detalles del Grupo de Comandos", "commandGroupDetailsTitle": "Detalles del Grupo de Comandos",
"createdBy": "Creado por", "createdBy": "Creado por",
"groupId": "ID del Grupo", "groupId": "ID del Grupo",
@ -521,7 +521,7 @@
"remoteAccess": "Disponible acceso remoto", "remoteAccess": "Disponible acceso remoto",
"noRemoteAccess": "No disponible acceso remoto", "noRemoteAccess": "No disponible acceso remoto",
"capacityWarning": "El aforo no puede ser", "capacityWarning": "El aforo no puede ser",
"procedimientosCliente": "Procedimientos", "procedimientosCliente": "Histórico acciones",
"tableDateRepositoryText": "Esta tabla muestra los datos asociados a los repositorios existentes.", "tableDateRepositoryText": "Esta tabla muestra los datos asociados a los repositorios existentes.",
"repositoryTitleStepText": "En esta pantalla se pueden gestionar los repositorios de imágenes.", "repositoryTitleStepText": "En esta pantalla se pueden gestionar los repositorios de imágenes.",
"monolithicImageStep": "Imagen monolítica", "monolithicImageStep": "Imagen monolítica",
@ -530,6 +530,8 @@
"monolithicImage": "Imagenes monolíticas", "monolithicImage": "Imagenes monolíticas",
"gitImage": "Imágenes Git", "gitImage": "Imágenes Git",
"gitImageStepText": "Esta opción permite visualizar las imágenes Git disponibles en el servidor.", "gitImageStepText": "Esta opción permite visualizar las imágenes Git disponibles en el servidor.",
"partitions": "Particiones", "isDefaultLabel": "Por defecto",
"isDefaultLabel": "Por defecto" "tracesTitleStepText": "En esta pantalla, puedes ver las trazas de ejecución de cada cliente, con su id, comando, estado en tiempo real, fecha y acciones a realizar.",
"filtersStepText": "Aquí puedes ver los diferentes filtros que aplicar a la información de la tabla.",
"tracesTableStepText": "Esta es la tabla con las trazas de ejecución actualizadas en tiempo real."
} }