refs #985 Added loading in tables and fix some test

oggui/translations
Alvaro Puente Mella 2024-10-23 10:39:42 +02:00
parent a0deb37a35
commit 87680723bc
32 changed files with 695 additions and 265 deletions

View File

@ -13,6 +13,12 @@
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint> <mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<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>
@ -22,13 +28,19 @@
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th> <th mat-header-cell *matHeaderCellDef i18n="@@columnActions" style="text-align: center;">Acciones</th>
<td mat-cell *matCellDef="let role" style="text-align: center;"> <td mat-cell *matCellDef="let role" style="text-align: center;">
<button mat-icon-button color="primary" (click)="editRole(role)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button> <button mat-icon-button color="primary" (click)="editRole(role)" i18n="@@editImage">
<button mat-icon-button color="warn" (click)="deleteRole(role)" i18n="@@buttonDelete" [disabled]="role.permissions.includes('ROLE_SUPER_ADMIN')"><mat-icon>delete</mat-icon></button> <mat-icon>edit</mat-icon>
</button>
<button mat-icon-button color="warn" (click)="deleteRole(role)" i18n="@@buttonDelete" [disabled]="role.permissions.includes('ROLE_SUPER_ADMIN')">
<mat-icon>delete</mat-icon>
</button>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
</div>
<div class="paginator-container"> <div class="paginator-container">
<mat-paginator [length]="length" <mat-paginator [length]="length"
[pageSize]="itemsPerPage" [pageSize]="itemsPerPage"

View File

@ -1,13 +1,12 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { AddRoleModalComponent } from './add-role-modal/add-role-modal.component';
import { DeleteModalComponent } from '../../../../shared/delete_modal/delete-modal/delete-modal.component';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { DataService } from "./data.service"; import { DataService } from "./data.service";
import {CreateCalendarComponent} from "../../../calendar/create-calendar/create-calendar.component";
import { PageEvent } from "@angular/material/paginator"; import { PageEvent } from "@angular/material/paginator";
import { DeleteModalComponent } from '../../../../shared/delete_modal/delete-modal/delete-modal.component';
import { AddRoleModalComponent } from './add-role-modal/add-role-modal.component';
@Component({ @Component({
selector: 'app-roles', selector: 'app-roles',
@ -57,13 +56,16 @@ export class RolesComponent implements OnInit {
} }
search() { search() {
this.loading = true;
this.http.get<any>(`${this.apiUrl}?&page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( this.http.get<any>(`${this.apiUrl}?&page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe(
(data) => { (data) => {
this.dataSource.data = data['hydra:member']; this.dataSource.data = data['hydra:member'];
this.length = data['hydra:totalItems']; this.length = data['hydra:totalItems'];
this.loading = false;
}, },
(error) => { (error) => {
console.error('Error fetching commands', error); console.error('Error fetching roles', error);
this.loading = false;
} }
); );
} }
@ -114,7 +116,7 @@ export class RolesComponent implements OnInit {
}); });
} }
onPageChange(event: any): void { onPageChange(event: PageEvent): void {
this.page = event.pageIndex; this.page = event.pageIndex;
this.itemsPerPage = event.pageSize; this.itemsPerPage = event.pageSize;
this.length = event.length; this.length = event.length;

View File

@ -13,8 +13,13 @@
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint> <mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<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 user"> {{ column.cell(user) }} </td> <td mat-cell *matCellDef="let user"> {{ column.cell(user) }} </td>
@ -30,6 +35,8 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
</div>
<div class="paginator-container"> <div class="paginator-container">
<mat-paginator [length]="length" <mat-paginator [length]="length"
[pageSize]="itemsPerPage" [pageSize]="itemsPerPage"

View File

@ -6,14 +6,12 @@ import { DeleteModalComponent } from '../../../../shared/delete_modal/delete-mod
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { DataService } from "./data.service"; import { DataService } from "./data.service";
import {AddRoleModalComponent} from "../../roles/roles/add-role-modal/add-role-modal.component";
@Component({ @Component({
selector: 'app-users', selector: 'app-users',
templateUrl: './users.component.html', templateUrl: './users.component.html',
styleUrls: ['./users.component.css'] styleUrls: ['./users.component.css']
}) })
export class UsersComponent implements OnInit { export class UsersComponent implements OnInit {
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
dataSource = new MatTableDataSource<any>(); dataSource = new MatTableDataSource<any>();
@ -61,13 +59,16 @@ export class UsersComponent implements OnInit {
} }
search() { search() {
this.loading = true;
this.http.get<any>(`${this.apiUrl}?&page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( this.http.get<any>(`${this.apiUrl}?&page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe(
(data) => { (data) => {
this.dataSource.data = data['hydra:member']; this.dataSource.data = data['hydra:member'];
this.length = data['hydra:totalItems']; this.length = data['hydra:totalItems'];
this.loading = false;
}, },
(error) => { (error) => {
console.error('Error fetching commands', error); console.error('Error fetching users', error);
this.loading = false;
} }
); );
} }
@ -78,7 +79,6 @@ export class UsersComponent implements OnInit {
dialogRef.componentInstance.userAdded.subscribe(() => { dialogRef.componentInstance.userAdded.subscribe(() => {
this.search(); this.search();
}); });
} }
editUser(user: any): void { editUser(user: any): void {
@ -114,8 +114,6 @@ export class UsersComponent implements OnInit {
console.error('Error deleting user:', error); console.error('Error deleting user:', error);
} }
}); });
} else {
console.log('User deletion cancelled');
} }
}); });
} }
@ -126,5 +124,4 @@ export class UsersComponent implements OnInit {
this.length = event.length; this.length = event.length;
this.search(); this.search();
} }
} }

View File

@ -13,6 +13,12 @@
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint> <mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<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>
@ -42,6 +48,8 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
</div>
<div class="paginator-container"> <div class="paginator-container">
<mat-paginator [length]="length" <mat-paginator [length]="length"
[pageSize]="itemsPerPage" [pageSize]="itemsPerPage"

View File

@ -10,8 +10,9 @@ import { MatButtonModule } from '@angular/material/button';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator'; import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { FormsModule } from '@angular/forms'; // Importa FormsModule para ngModel import { FormsModule } from '@angular/forms';
import { CalendarComponent } from './calendar.component'; import { CalendarComponent } from './calendar.component';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
describe('CalendarComponent', () => { describe('CalendarComponent', () => {
let component: CalendarComponent; let component: CalendarComponent;
@ -32,7 +33,8 @@ describe('CalendarComponent', () => {
MatTableModule, MatTableModule,
MatPaginatorModule, MatPaginatorModule,
MatTooltipModule, MatTooltipModule,
FormsModule // Añade FormsModule aquí para que ngModel funcione FormsModule,
MatProgressSpinner
] ]
}) })
.compileComponents(); .compileComponents();

View File

@ -148,13 +148,16 @@ export class CalendarComponent implements OnInit {
} }
applyFilter() { applyFilter() {
this.loading = true;
this.http.get<any>(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`).subscribe({ this.http.get<any>(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`).subscribe({
next: (response) => { next: (response) => {
this.dataSource.data = response['hydra:member']; this.dataSource.data = response['hydra:member'];
this.length = response['hydra:totalItems']; this.length = response['hydra:totalItems'];
this.loading = false;
}, },
error: (error) => { error: (error) => {
console.error('Error al cargar las imágenes:', error); console.error('Error al cargar las imágenes:', error);
this.loading = false;
} }
}); });
} }

View File

@ -14,6 +14,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<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>
@ -46,6 +51,7 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
</div>
<div class="paginator-container"> <div class="paginator-container">
<mat-paginator [length]="length" <mat-paginator [length]="length"

View File

@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { CreateCommandGroupComponent } from './create-command-group/create-command-group.component' import { CreateCommandGroupComponent } from './create-command-group/create-command-group.component';
import { DetailCommandGroupComponent } from './detail-command-group/detail-command-group.component'; import { DetailCommandGroupComponent } from './detail-command-group/detail-command-group.component';
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component'; import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
import { MatTableDataSource } from "@angular/material/table"; import { MatTableDataSource } from "@angular/material/table";
@ -22,6 +22,7 @@ export class CommandsGroupsComponent implements OnInit {
page: number = 0; page: number = 0;
pageSizeOptions: number[] = [10, 20, 40, 100]; pageSizeOptions: number[] = [10, 20, 40, 100];
datePipe: DatePipe = new DatePipe('es-ES'); datePipe: DatePipe = new DatePipe('es-ES');
loading: boolean = false;
columns = [ columns = [
{ {
columnDef: 'id', columnDef: 'id',
@ -54,13 +55,16 @@ export class CommandsGroupsComponent implements OnInit {
} }
search(): void { search(): void {
this.loading = true;
this.http.get<any>(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( this.http.get<any>(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe(
(data) => { (data) => {
this.dataSource.data = data['hydra:member']; this.dataSource.data = data['hydra:member'];
this.length = data['hydra:totalItems']; this.length = data['hydra:totalItems'];
this.loading = false;
}, },
(error) => { (error) => {
console.error('Error fetching command groups', error); console.error('Error fetching command groups', error);
this.loading = false;
} }
); );
} }

View File

@ -1,6 +1,10 @@
<h2 mat-dialog-title>{{ editing ? 'Editar' : 'Crear' }} grupo de comando</h2> <h2 mat-dialog-title>{{ editing ? 'Editar' : 'Crear' }} grupo de comando</h2>
<mat-dialog-content class="form-container"> <mat-dialog-content class="form-container">
<form class="command-group-form" (ngSubmit)="onSubmit()"> <div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<form *ngIf="!loading" class="command-group-form" (ngSubmit)="onSubmit()">
<mat-form-field> <mat-form-field>
<mat-label>Nombre del Grupo</mat-label> <mat-label>Nombre del Grupo</mat-label>
<input matInput [(ngModel)]="groupName" name="groupName" required /> <input matInput [(ngModel)]="groupName" name="groupName" required />
@ -11,7 +15,7 @@
<div class="command-selection"> <div class="command-selection">
<div class="available-commands"> <div class="available-commands">
<h3>Comandos Disponibles</h3> <h3>Comandos Disponibles</h3>
<div class="table-wrapper"> <!-- Agregar este contenedor --> <div class="table-wrapper">
<table mat-table [dataSource]="availableCommands" class="mat-elevation-z8"> <table mat-table [dataSource]="availableCommands" class="mat-elevation-z8">
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Nombre </th> <th mat-header-cell *matHeaderCellDef> Nombre </th>
@ -57,4 +61,3 @@
<button mat-button (click)="close()">Cancelar</button> <button mat-button (click)="close()">Cancelar</button>
<button mat-button (click)="onSubmit()" cdkFocusInitial> Guardar </button> <button mat-button (click)="onSubmit()" cdkFocusInitial> Guardar </button>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -15,6 +15,7 @@ export class CreateCommandGroupComponent implements OnInit {
groupName: string = ''; groupName: string = '';
enabled: boolean = true; enabled: boolean = true;
editing: boolean = false; editing: boolean = false;
loading: boolean = false;
private apiUrl = `${this.baseUrl}/commands`; private apiUrl = `${this.baseUrl}/commands`;
constructor( constructor(
@ -34,12 +35,15 @@ export class CreateCommandGroupComponent implements OnInit {
} }
loadAvailableCommands(): void { loadAvailableCommands(): void {
this.loading = true;
this.http.get<any>(this.apiUrl).subscribe( this.http.get<any>(this.apiUrl).subscribe(
(data) => { (data) => {
this.availableCommands = data['hydra:member']; this.availableCommands = data['hydra:member'];
this.loading = false;
}, },
(error) => { (error) => {
console.error('Error fetching available commands', error); console.error('Error fetching available commands', error);
this.loading = false;
} }
); );
} }

View File

@ -1,7 +1,12 @@
<div class="detail-command-group-container"> <div class="detail-command-group-container">
<h2>Detalles del Grupo de Comandos</h2> <h2>Detalles del Grupo de Comandos</h2>
<mat-card> <!-- Indicador de carga -->
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<mat-card *ngIf="!loading">
<mat-card-header> <mat-card-header>
<mat-card-title>{{ data.name }}</mat-card-title> <mat-card-title>{{ data.name }}</mat-card-title>
<mat-card-subtitle>Creado por: {{ data.createdBy }}</mat-card-subtitle> <mat-card-subtitle>Creado por: {{ data.createdBy }}</mat-card-subtitle>
@ -27,11 +32,10 @@
<tr mat-row *matRowDef="let row; columns: ['name', 'uuid'];"></tr> <tr mat-row *matRowDef="let row; columns: ['name', 'uuid'];"></tr>
</table> </table>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<!-- Sección para seleccionar clientes --> <!-- Sección para seleccionar clientes -->
<div class="additional-section" *ngIf="showClientSelect"> <div class="additional-section" *ngIf="showClientSelect && !loading">
<form [formGroup]="form"> <form [formGroup]="form">
<h4>Selecciona los clientes:</h4> <h4>Selecciona los clientes:</h4>
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
@ -48,7 +52,7 @@
</form> </form>
</div> </div>
<div class="command-group-actions"> <div class="command-group-actions" *ngIf="!loading">
<button mat-flat-button color="primary" (click)="toggleClientSelect()"> <button mat-flat-button color="primary" (click)="toggleClientSelect()">
{{ showClientSelect ? 'Ejecutar' : 'Programar Ejecución' }} {{ showClientSelect ? 'Ejecutar' : 'Programar Ejecución' }}
</button> </button>

View File

@ -13,8 +13,9 @@ export class DetailCommandGroupComponent implements OnInit {
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
form!: FormGroup; form!: FormGroup;
clients: any[] = []; clients: any[] = [];
showClientSelect = false; // Ocultar selección de clientes inicialmente showClientSelect = false;
canExecute = false; canExecute = false;
loading: boolean = false;
constructor( constructor(
@Inject(MAT_DIALOG_DATA) public data: any, @Inject(MAT_DIALOG_DATA) public data: any,
@ -29,17 +30,28 @@ export class DetailCommandGroupComponent implements OnInit {
selectedClients: [[], Validators.required], selectedClients: [[], Validators.required],
}); });
// Obtener la lista de clientes this.loadClients();
this.http.get<any>(`${this.baseUrl}/clients?page=1&itemsPerPage=30`).subscribe(response => { }
loadClients(): void {
this.loading = true;
this.http.get<any>(`${this.baseUrl}/clients?page=1&itemsPerPage=30`).subscribe({
next: (response) => {
this.clients = response['hydra:member']; this.clients = response['hydra:member'];
this.loading = false;
},
error: (error) => {
console.error('Error fetching clients:', error);
this.loading = false;
}
}); });
} }
toggleClientSelect(): void { toggleClientSelect(): void {
if (!this.showClientSelect) { if (!this.showClientSelect) {
this.showClientSelect = true; // Mostrar selección de clientes this.showClientSelect = true;
} else { } else {
this.execute(); // Ejecutar si ya está visible this.execute();
} }
} }
@ -50,13 +62,16 @@ export class DetailCommandGroupComponent implements OnInit {
}; };
const apiUrl = `${this.baseUrl}/command-groups/${this.data.uuid}/execute`; const apiUrl = `${this.baseUrl}/command-groups/${this.data.uuid}/execute`;
this.loading = true;
this.http.post(apiUrl, payload).subscribe({ this.http.post(apiUrl, payload).subscribe({
next: () => { next: () => {
this.dialogRef.close(); this.dialogRef.close();
this.toastService.success('Grupo de comandos ejecutado exitosamente'); this.toastService.success('Grupo de comandos ejecutado exitosamente');
this.loading = false;
}, },
error: (error) => { error: (error) => {
console.error('Error ejecutando grupo de comandos:', error); console.error('Error ejecutando grupo de comandos:', error);
this.loading = false;
} }
}); });
} else { } else {

View File

@ -16,6 +16,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading">
<table mat-table [dataSource]="tasks" class="mat-elevation-z8"> <table mat-table [dataSource]="tasks" class="mat-elevation-z8">
<ng-container matColumnDef="taskid"> <ng-container matColumnDef="taskid">
<th mat-header-cell *matHeaderCellDef> Id</th> <th mat-header-cell *matHeaderCellDef> Id</th>
@ -56,6 +61,7 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
</div>
<mat-paginator [length]="length" <mat-paginator [length]="length"
[pageSize]="itemsPerPage" [pageSize]="itemsPerPage"

View File

@ -20,6 +20,7 @@ export class CommandsTaskComponent implements OnInit {
page: number = 1; page: number = 1;
pageSizeOptions: number[] = [5, 10, 20, 40, 100]; pageSizeOptions: number[] = [5, 10, 20, 40, 100];
displayedColumns: string[] = ['taskid', 'notes', 'name', 'scheduledDate', 'enabled', 'actions']; displayedColumns: string[] = ['taskid', 'notes', 'name', 'scheduledDate', 'enabled', 'actions'];
loading: boolean = false;
private apiUrl = `${this.baseUrl}/command-tasks`; private apiUrl = `${this.baseUrl}/command-tasks`;
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService) {} constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService) {}
@ -34,13 +35,16 @@ export class CommandsTaskComponent implements OnInit {
} }
loadTasks(): void { loadTasks(): void {
this.loading = true;
this.http.get<any>(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( this.http.get<any>(`${this.apiUrl}?page=${this.page}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe(
(data) => { (data) => {
this.tasks = data['hydra:member']; this.tasks = data['hydra:member'];
this.length = data['hydra:totalItems']; this.length = data['hydra:totalItems'];
this.loading = false;
}, },
(error) => { (error) => {
console.error('Error fetching tasks', error); console.error('Error fetching tasks', error);
this.loading = false;
} }
); );
} }

View File

@ -13,11 +13,9 @@ export class DetailTaskComponent {
public dialogRef: MatDialogRef<DetailTaskComponent>, public dialogRef: MatDialogRef<DetailTaskComponent>,
@Inject(MAT_DIALOG_DATA) public data: any @Inject(MAT_DIALOG_DATA) public data: any
) { ) {
this.task = data.task; // Asignamos la tarea que viene en el modal this.task = data.task;
console.log('tasaas',this.task);
} }
// Método opcional para cerrar el modal
closeDialog(): void { closeDialog(): void {
this.dialogRef.close(); this.dialogRef.close();
} }

View File

@ -5,6 +5,7 @@
</div> </div>
</div> </div>
<mat-divider class="divider"></mat-divider> <mat-divider class="divider"></mat-divider>
<div class="search-container"> <div class="search-container">
<mat-form-field appearance="fill" class="search-select"> <mat-form-field appearance="fill" class="search-select">
<input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto" placeholder="Seleccione un cliente"> <input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto" placeholder="Seleccione un cliente">
@ -15,7 +16,6 @@
</mat-autocomplete> </mat-autocomplete>
</mat-form-field> </mat-form-field>
<!-- Autocomplete para seleccionar un comando -->
<mat-form-field appearance="fill" class="search-select"> <mat-form-field appearance="fill" class="search-select">
<input type="text" matInput [formControl]="commandControl" [matAutocomplete]="commandAuto" placeholder="Seleccione un comando"> <input type="text" matInput [formControl]="commandControl" [matAutocomplete]="commandAuto" placeholder="Seleccione un comando">
<mat-autocomplete #commandAuto="matAutocomplete" [displayWith]="displayFnCommand" (optionSelected)="onOptionCommandSelected($event.option.value)"> <mat-autocomplete #commandAuto="matAutocomplete" [displayWith]="displayFnCommand" (optionSelected)="onOptionCommandSelected($event.option.value)">
@ -26,19 +26,79 @@
</mat-form-field> </mat-form-field>
</div> </div>
<!-- Indicador de carga -->
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<!-- Tabla de trazas -->
<div *ngIf="!loading">
<table mat-table [dataSource]="traces" class="mat-elevation-z8"> <table mat-table [dataSource]="traces" class="mat-elevation-z8">
<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">
<ng-container >
{{ column.cell(trace) }} {{ column.cell(trace) }}
</ng-container>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
</div>
<div class="paginator-container">
<mat-paginator [length]="length"
[pageSize]="itemsPerPage"
[pageIndex]="page"
[pageSizeOptions]="pageSizeOptions"
(page)="onPageChange($event)">
</mat-paginator>
</div>
<div class="header-container">
<h2 class="title" i18n="@@adminCommandsTitle">Trazas de comandos y procedimientos</h2>
<div class="images-button-row">
<button mat-flat-button color="primary" (click)="resetFilters()">Reiniciar filtros</button>
</div>
</div>
<mat-divider class="divider"></mat-divider>
<div class="search-container">
<mat-form-field appearance="fill" class="search-select">
<input type="text" matInput [formControl]="clientControl" [matAutocomplete]="clientAuto" placeholder="Seleccione un cliente">
<mat-autocomplete #clientAuto="matAutocomplete" [displayWith]="displayFnClient" (optionSelected)="onOptionClientSelected($event.option.value)">
<mat-option *ngFor="let client of filteredClients | async" [value]="client">
{{ client.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<mat-form-field appearance="fill" class="search-select">
<input type="text" matInput [formControl]="commandControl" [matAutocomplete]="commandAuto" placeholder="Seleccione un comando">
<mat-autocomplete #commandAuto="matAutocomplete" [displayWith]="displayFnCommand" (optionSelected)="onOptionCommandSelected($event.option.value)">
<mat-option *ngFor="let command of filteredCommands | async" [value]="command">
{{ command.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading">
<table mat-table [dataSource]="traces" class="mat-elevation-z8">
<ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
<td mat-cell *matCellDef="let trace">
{{ column.cell(trace) }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
<div class="paginator-container"> <div class="paginator-container">
<mat-paginator [length]="length" <mat-paginator [length]="length"

View File

@ -1,9 +1,9 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import {Observable, startWith} from 'rxjs'; import { Observable } from 'rxjs';
import {FormControl} from "@angular/forms"; import { FormControl } from '@angular/forms';
import {map} from "rxjs/operators"; import { map, startWith } from 'rxjs/operators';
import {DatePipe} from "@angular/common"; import { DatePipe } from '@angular/common';
@Component({ @Component({
selector: 'app-task-logs', selector: 'app-task-logs',
@ -46,7 +46,7 @@ export class TaskLogsComponent implements OnInit {
}, },
{ {
columnDef: 'executedAt', columnDef: 'executedAt',
header: 'Programacion de ejecución', header: 'Programación de ejecución',
cell: (trace: any) => `${this.datePipe.transform(trace.executedAt, 'dd/MM/yyyy hh:mm:ss')}`, cell: (trace: any) => `${this.datePipe.transform(trace.executedAt, 'dd/MM/yyyy hh:mm:ss')}`,
}, },
{ {
@ -110,40 +110,45 @@ export class TaskLogsComponent implements OnInit {
} }
loadTraces(): void { loadTraces(): void {
this.loading = true;
const url = `${this.baseUrl}/traces?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`; const url = `${this.baseUrl}/traces?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`;
this.http.get<any>(url, { params: this.filters }).subscribe( this.http.get<any>(url, { params: this.filters }).subscribe(
(data) => { (data) => {
this.traces = data['hydra:member']; this.traces = data['hydra:member'];
this.length = data['hydra:totalItems']; this.length = data['hydra:totalItems'];
this.groupedTraces = this.groupByCommandId(this.traces); this.groupedTraces = this.groupByCommandId(this.traces);
this.loading = false;
}, },
(error) => { (error) => {
console.error('Error fetching traces', error); console.error('Error fetching traces', error);
this.loading = false;
} }
); );
} }
loadCommands() { loadCommands() {
this.loading = true;
this.http.get<any>(`${this.baseUrl}/commands?&page=1&itemsPerPage=10000`).subscribe( this.http.get<any>(`${this.baseUrl}/commands?&page=1&itemsPerPage=10000`).subscribe(
response => { response => {
this.commands = response['hydra:member']; this.commands = response['hydra:member'];
this.loading = false; this.loading = false;
}, },
error => { error => {
console.error('Error fetching parent units:', error); console.error('Error fetching commands:', error);
this.loading = false; this.loading = false;
} }
); );
} }
loadClients() { loadClients() {
this.loading = true;
this.http.get<any>(`${this.baseUrl}/clients?&page=1&itemsPerPage=10000`).subscribe( this.http.get<any>(`${this.baseUrl}/clients?&page=1&itemsPerPage=10000`).subscribe(
response => { response => {
this.clients = response['hydra:member']; this.clients = response['hydra:member'];
this.loading = false; this.loading = false;
}, },
error => { error => {
console.error('Error fetching parent units:', error); console.error('Error fetching clients:', error);
this.loading = false; this.loading = false;
} }
); );

View File

@ -5,6 +5,7 @@
</div> </div>
</div> </div>
<mat-divider class="divider"></mat-divider> <mat-divider class="divider"></mat-divider>
<div class="search-container"> <div class="search-container">
<mat-form-field appearance="fill" class="search-string"> <mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar nombre de comando</mat-label> <mat-label i18n="@@searchLabel">Buscar nombre de comando</mat-label>
@ -14,6 +15,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<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>
@ -33,15 +39,14 @@
<td mat-cell *matCellDef="let client" style="text-align: center;"> <td mat-cell *matCellDef="let client" style="text-align: center;">
<button mat-icon-button color="info" (click)="viewDetails($event, client)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button> <button mat-icon-button color="info" (click)="viewDetails($event, client)"><mat-icon i18n="@@deleteElementTooltip">visibility</mat-icon></button>
<button mat-icon-button color="primary" (click)="editCommand($event, client)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button> <button mat-icon-button color="primary" (click)="editCommand($event, client)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="deleteCommand($event, client)"> <button mat-icon-button color="warn" (click)="deleteCommand($event, client)"><mat-icon i18n="@@deleteElementTooltip">delete</mat-icon></button>
<mat-icon i18n="@@deleteElementTooltip">delete</mat-icon>
</button>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
</div>
<div class="paginator-container"> <div class="paginator-container">
<mat-paginator [length]="length" <mat-paginator [length]="length"

View File

@ -5,8 +5,8 @@ import { ToastrService } from 'ngx-toastr';
import { CommandDetailComponent } from './detail-command/command-detail.component'; import { CommandDetailComponent } from './detail-command/command-detail.component';
import { CreateCommandComponent } from './create-command/create-command.component'; import { CreateCommandComponent } from './create-command/create-command.component';
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component'; import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
import {MatTableDataSource} from "@angular/material/table"; import { MatTableDataSource } from '@angular/material/table';
import {DatePipe} from "@angular/common"; import { DatePipe } from '@angular/common';
@Component({ @Component({
selector: 'app-commands', selector: 'app-commands',
@ -22,6 +22,7 @@ export class CommandsComponent implements OnInit {
page: number = 0; page: number = 0;
pageSizeOptions: number[] = [10, 20, 40, 100]; pageSizeOptions: number[] = [10, 20, 40, 100];
datePipe: DatePipe = new DatePipe('es-ES'); datePipe: DatePipe = new DatePipe('es-ES');
loading: boolean = false;
columns = [ columns = [
{ {
columnDef: 'id', columnDef: 'id',
@ -54,13 +55,16 @@ export class CommandsComponent implements OnInit {
} }
search(): void { search(): void {
this.loading = true;
this.http.get<any>(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe( this.http.get<any>(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe(
(data) => { (data) => {
this.dataSource.data = data['hydra:member']; this.dataSource.data = data['hydra:member'];
this.length = data['hydra:totalItems']; this.length = data['hydra:totalItems'];
this.loading = false;
}, },
(error) => { (error) => {
console.error('Error fetching commands', error); console.error('Error fetching commands', error);
this.loading = false;
} }
); );
} }

View File

@ -1,4 +1,14 @@
<div class="client-info"> <div class="header-container">
<h2 class="title">Administrar Cliente</h2>
</div>
<mat-divider class="divider"></mat-divider>
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!loading" class="client-info">
<div class="info-section"> <div class="info-section">
<mat-tab-group dynamicHeight> <mat-tab-group dynamicHeight>
<mat-tab label="Datos generales"> <mat-tab label="Datos generales">
@ -30,8 +40,8 @@
<div class="second-section"> <div class="second-section">
<div class="buttons-row"> <div class="buttons-row">
<button mat-flat-button color="primary" (click)="togglePartitionAssistant()">Asistente a particionado</button> <button mat-flat-button color="primary" (click)="togglePartitionAssistant()" [disabled]="isDiskUsageEmpty">Asistente particionado</button>
<button mat-flat-button color="primary" (click)="showBootImage()">Restaurar imagen</button> <button mat-flat-button color="primary" (click)="showBootImage()" [disabled]="isDiskUsageEmpty">Restaurar imagen</button>
</div> </div>
<div class="info-section"> <div class="info-section">
@ -61,10 +71,9 @@
</div> </div>
</div> </div>
<div class="assistants-container" *ngIf="isPartitionAssistantVisible" #assistantContainer> <div class="assistants-container" *ngIf="isPartitionAssistantVisible" #assistantContainer>
<app-partition-assistant [data]="clientData" [clientUuid]="clientUuid"></app-partition-assistant> <app-partition-assistant [data]="clientData" [clientUuid]="clientUuid"></app-partition-assistant>
</div> </div>
<div class="assistants-container" *ngIf="isBootImageVisible" #assistantContainer> <div *ngIf="isBootImageVisible" #assistantContainer>
<app-restore-image [data]="clientData"></app-restore-image> <app-restore-image [data]="clientData"></app-restore-image>
</div> </div>

View File

@ -1,5 +1,6 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
interface ClientInfo { interface ClientInfo {
property: string; property: string;
value: any; value: any;
@ -18,11 +19,12 @@ export class ClientMainViewComponent implements OnInit {
isPartitionAssistantVisible: boolean = false; isPartitionAssistantVisible: boolean = false;
isBootImageVisible: boolean = false; isBootImageVisible: boolean = false;
isDiskUsageVisible: boolean = true; isDiskUsageVisible: boolean = true;
generalData: ClientInfo[] = []; generalData: ClientInfo[] = [];
networkData: ClientInfo[] = []; networkData: ClientInfo[] = [];
classroomData: ClientInfo[] = []; classroomData: ClientInfo[] = [];
diskUsageData: any[] = []; diskUsageData: any[] = [];
isDiskUsageEmpty: boolean = true;
loading: boolean = true; // Variable para controlar el estado de carga
constructor(private http: HttpClient) { constructor(private http: HttpClient) {
const url = window.location.href; const url = window.location.href;
@ -36,6 +38,7 @@ export class ClientMainViewComponent implements OnInit {
this.updateNetworkData(); this.updateNetworkData();
this.updateClassroomData(); this.updateClassroomData();
this.calculateDiskUsage(); this.calculateDiskUsage();
this.loading = false; // Desactivar el estado de carga cuando los datos se hayan procesado
} }
updateGeneralData() { updateGeneralData() {
@ -52,6 +55,7 @@ export class ClientMainViewComponent implements OnInit {
{ property: 'Creado por', value: this.clientData?.createdBy || '' } { property: 'Creado por', value: this.clientData?.createdBy || '' }
]; ];
} }
updateNetworkData() { updateNetworkData() {
this.networkData = [ this.networkData = [
{ property: 'Perfil hardware', value: this.clientData?.hardwareProfile?.description || '' }, { property: 'Perfil hardware', value: this.clientData?.hardwareProfile?.description || '' },
@ -103,15 +107,16 @@ export class ClientMainViewComponent implements OnInit {
const percentage = total > 0 ? Math.round((used / total) * 100) : 0; const percentage = total > 0 ? Math.round((used / total) * 100) : 0;
return { diskNumber, total, used, percentage }; return { diskNumber, total, used, percentage };
}); });
this.isDiskUsageEmpty = this.diskUsageData.length === 0;
} }
togglePartitionAssistant() { togglePartitionAssistant() {
this.isPartitionAssistantVisible = !this.isPartitionAssistantVisible; this.isPartitionAssistantVisible = !this.isPartitionAssistantVisible;
this.isBootImageVisible = false this.isBootImageVisible = false;
if (this.isPartitionAssistantVisible) { if (this.isPartitionAssistantVisible) {
setTimeout(() => { setTimeout(() => {
this.assistantContainer.nativeElement.scrollIntoView({ behavior: 'smooth' }); this.assistantContainer.nativeElement.scrollIntoView({ behavior: 'smooth' });
}, 0); }, 200);
} }
} }

View File

@ -34,6 +34,12 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="!loading" class="loading-container">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="loading">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<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>
@ -78,3 +84,4 @@
(page)="onPageChange($event)"> (page)="onPageChange($event)">
</mat-paginator> </mat-paginator>
</div> </div>
</div>

View File

@ -1,6 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AddClientsToPxeComponent } from './add-clients-to-pxe.component'; import { AddClientsToPxeComponent } from './add-clients-to-pxe.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core';
import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
describe('AddClientsToPxeComponent', () => { describe('AddClientsToPxeComponent', () => {
let component: AddClientsToPxeComponent; let component: AddClientsToPxeComponent;
@ -8,7 +23,25 @@ describe('AddClientsToPxeComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [AddClientsToPxeComponent] declarations: [AddClientsToPxeComponent],
imports: [
HttpClientTestingModule,
ToastrModule.forRoot(),
BrowserAnimationsModule,
MatDividerModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatButtonModule,
MatTableModule,
MatPaginatorModule,
MatTooltipModule,
FormsModule,
MatProgressSpinner,
MatOptionModule,
ReactiveFormsModule,
MatSelectModule
]
}) })
.compileComponents(); .compileComponents();

View File

@ -1,6 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ClientsComponent } from './clients.component'; import { ClientsComponent } from './clients.component';
import { MatInputModule } from '@angular/material/input';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core';
import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
import { MatDialogRef } from '@angular/material/dialog';
describe('ClientsComponent', () => { describe('ClientsComponent', () => {
let component: ClientsComponent; let component: ClientsComponent;
@ -8,7 +24,26 @@ describe('ClientsComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ClientsComponent] declarations: [ClientsComponent],
imports: [
HttpClientTestingModule,
ToastrModule.forRoot(),
BrowserAnimationsModule,
MatDividerModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatButtonModule,
MatTableModule,
MatPaginatorModule,
MatTooltipModule,
FormsModule,
MatProgressSpinner,
MatOptionModule,
ReactiveFormsModule,
MatSelectModule,
MatDialogRef
]
}) })
.compileComponents(); .compileComponents();

View File

@ -1,6 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ServerInfoDialogComponent } from './server-info-dialog.component'; import { ServerInfoDialogComponent } from './server-info-dialog.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core';
import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
describe('ServerInfoDialogComponent', () => { describe('ServerInfoDialogComponent', () => {
let component: ServerInfoDialogComponent; let component: ServerInfoDialogComponent;
@ -8,7 +23,25 @@ describe('ServerInfoDialogComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ServerInfoDialogComponent] declarations: [ServerInfoDialogComponent],
imports: [
HttpClientTestingModule,
ToastrModule.forRoot(),
BrowserAnimationsModule,
MatDividerModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatButtonModule,
MatTableModule,
MatPaginatorModule,
MatTooltipModule,
FormsModule,
MatProgressSpinner,
MatOptionModule,
ReactiveFormsModule,
MatSelectModule
]
}) })
.compileComponents(); .compileComponents();

View File

@ -1,6 +1,19 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OperativeSystemComponent } from './operative-system.component'; import { OperativeSystemComponent } from './operative-system.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
describe('OperativeSystemComponent', () => { describe('OperativeSystemComponent', () => {
let component: OperativeSystemComponent; let component: OperativeSystemComponent;
@ -8,7 +21,22 @@ describe('OperativeSystemComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [OperativeSystemComponent] declarations: [OperativeSystemComponent],
imports: [
HttpClientTestingModule,
ToastrModule.forRoot(),
BrowserAnimationsModule,
MatDividerModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatButtonModule,
MatTableModule,
MatPaginatorModule,
MatTooltipModule,
FormsModule,
MatProgressSpinner
]
}) })
.compileComponents(); .compileComponents();

View File

@ -1,6 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SoftwareProfileComponent } from './software-profile.component'; import { SoftwareProfileComponent } from './software-profile.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core';
import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
describe('SoftwareProfileComponent', () => { describe('SoftwareProfileComponent', () => {
let component: SoftwareProfileComponent; let component: SoftwareProfileComponent;
@ -8,7 +23,25 @@ describe('SoftwareProfileComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [SoftwareProfileComponent] declarations: [SoftwareProfileComponent],
imports: [
HttpClientTestingModule,
ToastrModule.forRoot(),
BrowserAnimationsModule,
MatDividerModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatButtonModule,
MatTableModule,
MatPaginatorModule,
MatTooltipModule,
FormsModule,
MatProgressSpinner,
MatOptionModule,
ReactiveFormsModule,
MatSelectModule
]
}) })
.compileComponents(); .compileComponents();

View File

@ -1,6 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateSoftwareComponent } from './create-software.component'; import { CreateSoftwareComponent } from './create-software.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core';
import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
describe('CreateSoftwareComponent', () => { describe('CreateSoftwareComponent', () => {
let component: CreateSoftwareComponent; let component: CreateSoftwareComponent;
@ -8,7 +23,25 @@ describe('CreateSoftwareComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [CreateSoftwareComponent] declarations: [CreateSoftwareComponent],
imports: [
HttpClientTestingModule,
ToastrModule.forRoot(),
BrowserAnimationsModule,
MatDividerModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatButtonModule,
MatTableModule,
MatPaginatorModule,
MatTooltipModule,
FormsModule,
MatProgressSpinner,
MatOptionModule,
ReactiveFormsModule,
MatSelectModule
]
}) })
.compileComponents(); .compileComponents();

View File

@ -8,13 +8,13 @@
<div class="search-container"> <div class="search-container">
<mat-form-field appearance="fill" class="search-string"> <mat-form-field appearance="fill" class="search-string">
<mat-label i18n="@@searchLabel">Buscar nombre de software</mat-label> <mat-label i18n="@@searchLabel">Buscar nombre de software</mat-label>
<input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder"> <input matInput name="searchBar" placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
<mat-icon matSuffix>search</mat-icon> <mat-icon matSuffix>search</mat-icon>
<mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint> <mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="fill" class="search-boolean"> <mat-form-field appearance="fill" class="search-boolean">
<mat-label i18n="@@searchLabel">Buscar por tipo</mat-label> <mat-label i18n="@@searchLabel">Buscar por tipo</mat-label>
<mat-select [(ngModel)]="filters['type']" (selectionChange)="search()" placeholder="Seleccionar opción"> <mat-select name="searchType" [(ngModel)]="filters['type']" (selectionChange)="search()" placeholder="Seleccionar opción">
<mat-option [value]="'application'">Aplicación</mat-option> <mat-option [value]="'application'">Aplicación</mat-option>
<mat-option [value]="'operative-system'">Sistema operativo</mat-option> <mat-option [value]="'operative-system'">Sistema operativo</mat-option>
<mat-option [value]="'file'">Sistema de ficheros</mat-option> <mat-option [value]="'file'">Sistema de ficheros</mat-option>

View File

@ -1,6 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SoftwareComponent } from './software.component'; import { SoftwareComponent } from './software.component';
import { MatDividerModule } from '@angular/material/divider';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
import { MatOptionModule } from '@angular/material/core';
import { ReactiveFormsModule } from '@angular/forms';
import { MatSelectModule } from '@angular/material/select';
describe('SoftwareComponent', () => { describe('SoftwareComponent', () => {
let component: SoftwareComponent; let component: SoftwareComponent;
@ -8,7 +23,25 @@ describe('SoftwareComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [SoftwareComponent] declarations: [SoftwareComponent],
imports: [
HttpClientTestingModule,
ToastrModule.forRoot(),
BrowserAnimationsModule,
MatDividerModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatButtonModule,
MatTableModule,
MatPaginatorModule,
MatTooltipModule,
FormsModule,
MatProgressSpinner,
MatOptionModule,
ReactiveFormsModule,
MatSelectModule
]
}) })
.compileComponents(); .compileComponents();

View File

@ -2,3 +2,35 @@
html, body { height: 100%; } html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
/* Clase general para el contenedor de carga */
.loading-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1000;
background-color: rgba(255, 255, 255, 0.8); /* Fondo semitransparente */
width: 100vw;
height: 100vh;
}
/* Spinner de Angular Material */
.mat-spinner {
width: 100px;
height: 100px;
}
/* Texto que acompaña al spinner */
.loading-container p {
margin-top: 20px;
font-size: 1.2rem;
font-weight: bold;
color: #555;
}