Added new tab in groups. Search client
							parent
							
								
									5fc5ac2f9d
								
							
						
					
					
						commit
						b8cfa9a4e1
					
				| 
						 | 
				
			
			@ -94,6 +94,7 @@ import { CreateCommandGroupComponent } from './components/commands/commands-grou
 | 
			
		|||
import { DetailCommandGroupComponent } from './components/commands/commands-groups/detail-command-group/detail-command-group.component';
 | 
			
		||||
import { CreateTaskComponent } from './components/commands/commands-task/create-task/create-task.component';
 | 
			
		||||
import { DetailTaskComponent } from './components/commands/commands-task/detail-task/detail-task.component';
 | 
			
		||||
import { ClientTabViewComponent } from './components/groups/client-tab-view/client-tab-view.component';
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
    AppComponent,
 | 
			
		||||
| 
						 | 
				
			
			@ -144,7 +145,8 @@ import { DetailTaskComponent } from './components/commands/commands-task/detail-
 | 
			
		|||
    CreateCommandGroupComponent,
 | 
			
		||||
    DetailCommandGroupComponent,
 | 
			
		||||
    CreateTaskComponent,
 | 
			
		||||
    DetailTaskComponent
 | 
			
		||||
    DetailTaskComponent,
 | 
			
		||||
    ClientTabViewComponent
 | 
			
		||||
  ],
 | 
			
		||||
  bootstrap: [AppComponent],
 | 
			
		||||
  imports: [BrowserModule,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,8 +16,6 @@ export class CreateCommandGroupComponent implements OnInit {
 | 
			
		|||
  position: number = 1;
 | 
			
		||||
  enabled: boolean = true;
 | 
			
		||||
 | 
			
		||||
  private apiUrl = 'http://127.0.0.1:8001/commands'; 
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private http: HttpClient,
 | 
			
		||||
    private dialogRef: MatDialogRef<CreateCommandGroupComponent>,
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +27,7 @@ export class CreateCommandGroupComponent implements OnInit {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  loadAvailableCommands(): void {
 | 
			
		||||
    this.http.get<any>(this.apiUrl).subscribe(
 | 
			
		||||
    this.http.get<any>(`${this.baseUrl}/commands`).subscribe(
 | 
			
		||||
      (data) => {
 | 
			
		||||
        this.availableCommands = data['hydra:member'];
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +57,7 @@ export class CreateCommandGroupComponent implements OnInit {
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    console.log('Payload', payload);
 | 
			
		||||
    this.http.post('http://127.0.0.1:8001/command-groups', payload).subscribe({
 | 
			
		||||
    this.http.post(`${this.baseUrl}/command-groups`, payload).subscribe({
 | 
			
		||||
      next: () => {
 | 
			
		||||
        this.toastService.success('Grupo de comandos creado con éxito');
 | 
			
		||||
        this.dialogRef.close();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,16 @@
 | 
			
		|||
 | 
			
		||||
<mat-dialog-content [formGroup]="taskForm">
 | 
			
		||||
  <mat-form-field appearance="fill" class="full-width">
 | 
			
		||||
    <mat-label>Selecciona un Grupo de Comandos</mat-label>
 | 
			
		||||
    <mat-label>Selecciona Comandos</mat-label>
 | 
			
		||||
    <mat-select formControlName="commandGroup" (selectionChange)="onCommandGroupChange()">
 | 
			
		||||
      <mat-option *ngFor="let group of availableCommandGroups" [value]="group.uuid">
 | 
			
		||||
        {{ group.name }}
 | 
			
		||||
      </mat-option>
 | 
			
		||||
    </mat-select>
 | 
			
		||||
    <mat-error *ngIf="taskForm.get('commandGroup')?.invalid">Este campo es obligatorio</mat-error>
 | 
			
		||||
  </mat-form-field>
 | 
			
		||||
  <mat-form-field appearance="fill" class="full-width">
 | 
			
		||||
    <mat-label>Selecciona Grupo de Comandoss</mat-label>
 | 
			
		||||
    <mat-select formControlName="commandGroup" (selectionChange)="onCommandGroupChange()">
 | 
			
		||||
      <mat-option *ngFor="let group of availableCommandGroups" [value]="group.uuid">
 | 
			
		||||
        {{ group.name }}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,7 +69,6 @@ export class CreateTaskComponent implements OnInit {
 | 
			
		|||
 | 
			
		||||
    const payload = {
 | 
			
		||||
      commandGroups: '/command-groups/'+formData.commandGroup,
 | 
			
		||||
      commands: this.selectedGroupCommands.map(cmd => cmd['@id']),
 | 
			
		||||
      dateTime: dateTime,
 | 
			
		||||
      notes: formData.notes || ''
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,9 +9,10 @@ import { HttpClient } from '@angular/common/http';
 | 
			
		|||
  styleUrls: ['./create-command.component.css']
 | 
			
		||||
})
 | 
			
		||||
export class CreateCommandComponent {
 | 
			
		||||
  baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
 | 
			
		||||
  createCommandForm!: FormGroup;
 | 
			
		||||
  isEditMode: boolean;
 | 
			
		||||
  private apiUrl = 'http://127.0.0.1:8080/commands'; // URL para añadir o editar el comando
 | 
			
		||||
  private apiUrl = `${this.baseUrl}/commands`;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private fb: FormBuilder,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
 | 
			
		||||
.header-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  height: 100px;
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  margin-top: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-container  {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  padding: 0 5px;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-string {
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  padding: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-boolean {
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  padding: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-select {
 | 
			
		||||
  flex: 2;
 | 
			
		||||
  padding: 5px;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
  <div class="header-container">
 | 
			
		||||
    <h2 class="title" i18n="@@adminImagesTitle">Administrar clientes</h2>
 | 
			
		||||
    <div class="images-button-row">
 | 
			
		||||
      <button mat-flat-button color="primary" (click)="resetFilters()">Reiniciar filtros</button>
 | 
			
		||||
      <button mat-flat-button color="primary" (click)="addClient($event)">Añadir imagen</button>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <mat-divider class="divider"></mat-divider>
 | 
			
		||||
  <div class="search-container">
 | 
			
		||||
    <mat-form-field appearance="fill" class="search-string">
 | 
			
		||||
      <mat-label i18n="@@searchLabel">Buscar nombre de cliente</mat-label>
 | 
			
		||||
      <input matInput placeholder="Búsqueda" [(ngModel)]="filters['name']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
 | 
			
		||||
      <mat-icon matSuffix>search</mat-icon>
 | 
			
		||||
      <mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
    <mat-form-field appearance="fill" class="search-string">
 | 
			
		||||
      <mat-label i18n="@@searchLabel">Buscar IP</mat-label>
 | 
			
		||||
      <input matInput placeholder="Búsqueda" [(ngModel)]="filters['ip']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
 | 
			
		||||
      <mat-icon matSuffix>search</mat-icon>
 | 
			
		||||
      <mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
    <mat-form-field appearance="fill" class="search-string">
 | 
			
		||||
      <mat-label i18n="@@searchLabel">Buscar MAC</mat-label>
 | 
			
		||||
      <input matInput placeholder="Búsqueda" [(ngModel)]="filters['mac']" (keyup.enter)="search()" i18n-placeholder="@@searchPlaceholder">
 | 
			
		||||
      <mat-icon matSuffix>search</mat-icon>
 | 
			
		||||
      <mat-hint i18n="@@searchHint">Pulsar 'enter' para buscar</mat-hint>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
    <mat-form-field appearance="fill" class="search-select">
 | 
			
		||||
      <mat-label i18n="@@organizational-unit-label">U. Organizativa</mat-label>
 | 
			
		||||
      <mat-select [(ngModel)]="filters['organizationalUnit.id']" (selectionChange)="search()">
 | 
			
		||||
        <mat-option *ngFor="let unit of organizationalUnits" [value]="unit.id" >
 | 
			
		||||
          {{ unit.name }}
 | 
			
		||||
        </mat-option>
 | 
			
		||||
      </mat-select>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
  </div>
 | 
			
		||||
<table mat-table [dataSource]="dataSource">
 | 
			
		||||
  <ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
 | 
			
		||||
    <th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
 | 
			
		||||
    <td mat-cell *matCellDef="let image" >
 | 
			
		||||
      <ng-container >
 | 
			
		||||
        {{ column.cell(image) }}
 | 
			
		||||
      </ng-container>
 | 
			
		||||
    </td>
 | 
			
		||||
  </ng-container>
 | 
			
		||||
 | 
			
		||||
  <ng-container matColumnDef="actions">
 | 
			
		||||
    <th mat-header-cell *matHeaderCellDef i18n="@@columnActions">Acciones</th>
 | 
			
		||||
    <td mat-cell *matCellDef="let client">
 | 
			
		||||
      <button mat-icon-button color="primary" (click)="onEditClick($event, client.uuid)" i18n="@@editImage"> <mat-icon>edit</mat-icon></button>
 | 
			
		||||
      <button mat-icon-button (click)="onDeleteClick($event, client)">
 | 
			
		||||
        <mat-icon i18n="@@deleteElementTooltip">delete</mat-icon>
 | 
			
		||||
      </button>
 | 
			
		||||
    </td>
 | 
			
		||||
  </ng-container>
 | 
			
		||||
 | 
			
		||||
  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
 | 
			
		||||
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
 | 
			
		||||
</table>
 | 
			
		||||
<div class="paginator-container">
 | 
			
		||||
  <mat-paginator [length]="length"
 | 
			
		||||
                 [pageSize]="itemsPerPage"
 | 
			
		||||
                 [pageIndex]="page"
 | 
			
		||||
                 [pageSizeOptions]="pageSizeOptions"
 | 
			
		||||
                 (page)="onPageChange($event)">
 | 
			
		||||
  </mat-paginator>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
			
		||||
 | 
			
		||||
import { ClientTabViewComponent } from './client-tab-view.component';
 | 
			
		||||
 | 
			
		||||
describe('ClientTabViewComponent', () => {
 | 
			
		||||
  let component: ClientTabViewComponent;
 | 
			
		||||
  let fixture: ComponentFixture<ClientTabViewComponent>;
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    await TestBed.configureTestingModule({
 | 
			
		||||
      declarations: [ClientTabViewComponent]
 | 
			
		||||
    })
 | 
			
		||||
    .compileComponents();
 | 
			
		||||
 | 
			
		||||
    fixture = TestBed.createComponent(ClientTabViewComponent);
 | 
			
		||||
    component = fixture.componentInstance;
 | 
			
		||||
    fixture.detectChanges();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(component).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,157 @@
 | 
			
		|||
import { Component } from '@angular/core';
 | 
			
		||||
import {PageEvent} from "@angular/material/paginator";
 | 
			
		||||
import {DatePipe} from "@angular/common";
 | 
			
		||||
import {MatTableDataSource} from "@angular/material/table";
 | 
			
		||||
import {MatDialog} from "@angular/material/dialog";
 | 
			
		||||
import {ToastrService} from "ngx-toastr";
 | 
			
		||||
import {HttpClient} from "@angular/common/http";
 | 
			
		||||
import {DataService} from "./data.service";
 | 
			
		||||
import {EditClientComponent} from "../clients/edit-client/edit-client.component";
 | 
			
		||||
import {CreateClientComponent} from "../clients/create-client/create-client.component";
 | 
			
		||||
import {DeleteModalComponent} from "../../../shared/delete_modal/delete-modal/delete-modal.component";
 | 
			
		||||
import { throwError } from 'rxjs';
 | 
			
		||||
import { catchError } from 'rxjs/operators';
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-client-tab-view',
 | 
			
		||||
  templateUrl: './client-tab-view.component.html',
 | 
			
		||||
  styleUrl: './client-tab-view.component.css'
 | 
			
		||||
})
 | 
			
		||||
export class ClientTabViewComponent {
 | 
			
		||||
  baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
 | 
			
		||||
  dataSource = new MatTableDataSource<any>();
 | 
			
		||||
  length: number = 0;
 | 
			
		||||
  loading:boolean = false;
 | 
			
		||||
  itemsPerPage: number = 10;
 | 
			
		||||
  pageSizeOptions: number[] = [5, 10, 25, 100];
 | 
			
		||||
  page: number = 1;
 | 
			
		||||
  filters: { [key: string]: string } = {};
 | 
			
		||||
  organizationalUnits: any[] = [];
 | 
			
		||||
  datePipe: DatePipe = new DatePipe('es-ES');
 | 
			
		||||
 | 
			
		||||
  private apiUrl = `${this.baseUrl}/clients`;
 | 
			
		||||
 | 
			
		||||
  columns = [
 | 
			
		||||
    {
 | 
			
		||||
      columnDef: 'id',
 | 
			
		||||
      header: 'ID',
 | 
			
		||||
      cell: (client: any) => `${client.id}`
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      columnDef: 'name',
 | 
			
		||||
      header: 'Nombre del cliente',
 | 
			
		||||
      cell: (client: any) => `${client.name}`
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      columnDef: 'ip',
 | 
			
		||||
      header: 'IP',
 | 
			
		||||
      cell: (client: any) => `${client.ip}`
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      columnDef: 'mac',
 | 
			
		||||
      header: 'Mac',
 | 
			
		||||
      cell: (client: any) => `${client.mac}`
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      columnDef: 'organizationalUnit',
 | 
			
		||||
      header: 'Pertenece a',
 | 
			
		||||
      cell: (client: any) => `${client.organizationalUnit.name}`
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      columnDef: 'createdAt',
 | 
			
		||||
      header: 'Fecha de creación',
 | 
			
		||||
      cell: (client: any) => `${this.datePipe.transform(client.createdAt, 'dd/MM/yyyy hh:mm:ss')}`
 | 
			
		||||
    }
 | 
			
		||||
  ];
 | 
			
		||||
  displayedColumns = [...this.columns.map(column => column.columnDef), 'actions'];
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private dataService: DataService,
 | 
			
		||||
    public dialog: MatDialog,
 | 
			
		||||
    private toastService: ToastrService,
 | 
			
		||||
    private http: HttpClient
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.getClients();
 | 
			
		||||
    this.loadOrganizationalUnits();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getClients() {
 | 
			
		||||
    this.http.get<any>(`${this.apiUrl}?&page=${this.page}&itemsPerPage=${this.itemsPerPage}`, { params: this.filters }).subscribe(
 | 
			
		||||
      (data) => {
 | 
			
		||||
        this.dataSource.data = data['hydra:member'];
 | 
			
		||||
        this.length = data['hydra:totalItems'];
 | 
			
		||||
      },
 | 
			
		||||
      (error) => {
 | 
			
		||||
        console.error('Error fetching commands', error);
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onEditClick(event: MouseEvent, uuid: string): void {
 | 
			
		||||
    event.stopPropagation();
 | 
			
		||||
    const dialogRef = this.dialog.open(EditClientComponent, { data: { uuid }, width: '900px' } );
 | 
			
		||||
    dialogRef.afterClosed().subscribe(() => this.getClients());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addClient(event: MouseEvent, organizationalUnit:any = null): void {
 | 
			
		||||
    event.stopPropagation();
 | 
			
		||||
    const dialogRef = this.dialog.open(CreateClientComponent, { data: { organizationalUnit }, width: '900px'});
 | 
			
		||||
    dialogRef.afterClosed().subscribe(() => {
 | 
			
		||||
      this.getClients();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onDeleteClick(event: MouseEvent, client: any): void {
 | 
			
		||||
    event.stopPropagation();
 | 
			
		||||
    const dialogRef = this.dialog.open(DeleteModalComponent, {
 | 
			
		||||
      width: '400px'
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    dialogRef.afterClosed().subscribe(result => {
 | 
			
		||||
      if (result) {
 | 
			
		||||
        this.http.delete<void>(`${this.apiUrl}/${client.uuid}`).pipe(
 | 
			
		||||
          catchError(error => {
 | 
			
		||||
            this.toastService.error(error.error['hydra:description']);
 | 
			
		||||
            return throwError(error);
 | 
			
		||||
          })
 | 
			
		||||
        ).subscribe(() => {
 | 
			
		||||
          this.toastService.success('Elemento eliminado correctamente');
 | 
			
		||||
          this.getClients();
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  resetFilters() {
 | 
			
		||||
    this.loading = true;
 | 
			
		||||
    this.filters = {};
 | 
			
		||||
    this.getClients();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loadOrganizationalUnits() {
 | 
			
		||||
    this.loading = true;
 | 
			
		||||
    this.http.get<any>( `${this.baseUrl}/organizational-units?&page=1&itemsPerPage=10000`).subscribe(
 | 
			
		||||
      response => {
 | 
			
		||||
        this.organizationalUnits = response['hydra:member'];
 | 
			
		||||
        this.loading = false;
 | 
			
		||||
      },
 | 
			
		||||
      error => {
 | 
			
		||||
        console.error('Error fetching parent units:', error);
 | 
			
		||||
        this.loading = false;
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  search(): void {
 | 
			
		||||
    this.loading = true;
 | 
			
		||||
    this.getClients()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onPageChange(event: any): void {
 | 
			
		||||
    this.page = event.pageIndex;
 | 
			
		||||
    this.itemsPerPage = event.pageSize;
 | 
			
		||||
    this.length = event.length;
 | 
			
		||||
    this.getClients();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
import { Injectable } from '@angular/core';
 | 
			
		||||
import {HttpClient, HttpParams} from '@angular/common/http';
 | 
			
		||||
import { Observable, throwError } from 'rxjs';
 | 
			
		||||
import { catchError, map } from 'rxjs/operators';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
export class DataService {
 | 
			
		||||
  baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
 | 
			
		||||
 | 
			
		||||
  private apiUrl = `${this.baseUrl}/organizational-units`;
 | 
			
		||||
  private clientsUrl = `${this.baseUrl}/clients`;
 | 
			
		||||
 | 
			
		||||
  constructor(private http: HttpClient) {}
 | 
			
		||||
 | 
			
		||||
  getClients(itemsPerPage: number = 10, page: number = 1): Observable<any> {
 | 
			
		||||
    return this.http.get<any>(this.clientsUrl, {
 | 
			
		||||
      params: new HttpParams().set('itemsPerPage', itemsPerPage.toString()).set('page', page.toString())
 | 
			
		||||
    })
 | 
			
		||||
      .pipe(
 | 
			
		||||
      map(response => {
 | 
			
		||||
        if (response['hydra:member'] && Array.isArray(response['hydra:member'])) {
 | 
			
		||||
          return response['hydra:member']
 | 
			
		||||
        } else {
 | 
			
		||||
          throw new Error('Unexpected response format');
 | 
			
		||||
        }
 | 
			
		||||
      }),
 | 
			
		||||
      catchError(error => {
 | 
			
		||||
        console.error('Error fetching clients', error);
 | 
			
		||||
        return throwError(error);
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -21,12 +21,6 @@ h1 {
 | 
			
		|||
  padding: 50px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mat-dialog-actions {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: flex-end;
 | 
			
		||||
  padding: 10px 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button {
 | 
			
		||||
  text-transform: none;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
| 
						 | 
				
			
			@ -56,5 +50,14 @@ mat-option .unit-path {
 | 
			
		|||
 | 
			
		||||
.create-client-container {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  height: 90vh;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.grid-form {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-columns: repeat(2, 1fr);
 | 
			
		||||
  gap: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.form-field {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
  <h1 mat-dialog-title i18n="@@add-client-dialog-title">Añadir Cliente</h1>
 | 
			
		||||
  <div class="mat-dialog-content" [ngClass]="{'loading': loading}">
 | 
			
		||||
    <mat-spinner class="loading-spinner" *ngIf="loading"></mat-spinner>
 | 
			
		||||
    <form [formGroup]="clientForm" class="client-form" *ngIf="!loading">
 | 
			
		||||
    <form [formGroup]="clientForm" class="client-form grid-form" *ngIf="!loading">
 | 
			
		||||
      <mat-form-field class="form-field">
 | 
			
		||||
        <mat-label i18n="@@organizational-unit-label">Padre</mat-label>
 | 
			
		||||
        <mat-select formControlName="organizationalUnit">
 | 
			
		||||
| 
						 | 
				
			
			@ -12,10 +12,12 @@
 | 
			
		|||
          </mat-option>
 | 
			
		||||
        </mat-select>
 | 
			
		||||
      </mat-form-field>
 | 
			
		||||
 | 
			
		||||
      <mat-form-field class="form-field">
 | 
			
		||||
        <mat-label i18n="@@name-label">Nombre</mat-label>
 | 
			
		||||
        <input matInput formControlName="name">
 | 
			
		||||
      </mat-form-field>
 | 
			
		||||
 | 
			
		||||
      <mat-form-field class="form-field">
 | 
			
		||||
        <mat-label i18n="@@oglive-label">OgLive</mat-label>
 | 
			
		||||
        <mat-select formControlName="ogLive">
 | 
			
		||||
| 
						 | 
				
			
			@ -24,10 +26,12 @@
 | 
			
		|||
          </mat-option>
 | 
			
		||||
        </mat-select>
 | 
			
		||||
      </mat-form-field>
 | 
			
		||||
 | 
			
		||||
      <mat-form-field class="form-field">
 | 
			
		||||
        <mat-label i18n="@@serial-number-label">Número de Serie</mat-label>
 | 
			
		||||
        <input matInput formControlName="serialNumber">
 | 
			
		||||
      </mat-form-field>
 | 
			
		||||
 | 
			
		||||
      <mat-form-field class="form-field">
 | 
			
		||||
        <mat-label i18n="@@netiface-label">Interfaz de red</mat-label>
 | 
			
		||||
        <mat-select formControlName="netiface">
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +40,7 @@
 | 
			
		|||
          </mat-option>
 | 
			
		||||
        </mat-select>
 | 
			
		||||
      </mat-form-field>
 | 
			
		||||
 | 
			
		||||
      <mat-form-field class="form-field">
 | 
			
		||||
        <mat-label i18n="@@net-driver-label">Controlador de red</mat-label>
 | 
			
		||||
        <mat-select formControlName="netDriver">
 | 
			
		||||
| 
						 | 
				
			
			@ -44,23 +49,27 @@
 | 
			
		|||
          </mat-option>
 | 
			
		||||
        </mat-select>
 | 
			
		||||
      </mat-form-field>
 | 
			
		||||
 | 
			
		||||
      <mat-form-field class="form-field">
 | 
			
		||||
        <mat-label i18n="@@mac-label">MAC</mat-label>
 | 
			
		||||
        <mat-hint i18n="@@mac-hint">Ejemplo: 00:11:22:33:44:55</mat-hint>
 | 
			
		||||
        <input matInput formControlName="mac">
 | 
			
		||||
        <mat-error i18n="@@mac-error">Formato de MAC inválido. Ejemplo válido: 00:11:22:33:44:55</mat-error>
 | 
			
		||||
      </mat-form-field>
 | 
			
		||||
 | 
			
		||||
      <mat-form-field class="form-field">
 | 
			
		||||
        <mat-label i18n="@@ip-label">Dirección IP</mat-label>
 | 
			
		||||
        <mat-hint i18n="@@ip-hint">Ejemplo: 127.0.0.1</mat-hint>
 | 
			
		||||
        <input matInput formControlName="ip">
 | 
			
		||||
        <mat-error i18n="@@ip-error">Formato de dirección IP inválido. Ejemplo válido: 127.0.0.1</mat-error>
 | 
			
		||||
      </mat-form-field>
 | 
			
		||||
 | 
			
		||||
      <mat-form-field class="form-field">
 | 
			
		||||
        <mat-label i18n="@@menu-url-label">Menú URL</mat-label>
 | 
			
		||||
        <input matInput formControlName="menu" type="url">
 | 
			
		||||
        <mat-error i18n="@@menu-url-error">Formato de URL inválido.</mat-error>
 | 
			
		||||
      </mat-form-field>
 | 
			
		||||
 | 
			
		||||
      <mat-form-field class="form-field">
 | 
			
		||||
        <mat-label i18n="@@hardware-profile-label">Perfil de Hardware</mat-label>
 | 
			
		||||
        <mat-select formControlName="hardwareProfile">
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +81,8 @@
 | 
			
		|||
      </mat-form-field>
 | 
			
		||||
    </form>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div mat-dialog-actions>
 | 
			
		||||
 | 
			
		||||
  <div mat-dialog-actions align="end">
 | 
			
		||||
    <button mat-button (click)="onNoClick()" i18n="@@cancel-button">Cancelar</button>
 | 
			
		||||
    <button mat-button [disabled]="!clientForm.valid" (click)="onSubmit()" i18n="@@add-button">Añadir</button>
 | 
			
		||||
  </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,12 +21,6 @@ h1 {
 | 
			
		|||
  padding: 50px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mat-dialog-actions {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: flex-end;
 | 
			
		||||
  padding: 10px 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button {
 | 
			
		||||
  text-transform: none;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
| 
						 | 
				
			
			@ -58,3 +52,13 @@ button {
 | 
			
		|||
.form-field {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.grid-form {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-columns: repeat(2, 1fr);
 | 
			
		||||
  gap: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.form-field {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,8 @@
 | 
			
		|||
<h1 mat-dialog-title i18n="@@edit-client-dialog-title">Editar Cliente</h1>
 | 
			
		||||
<div class="mat-dialog-content" [ngClass]="{'loading': loading}">
 | 
			
		||||
  <mat-spinner class="loading-spinner" *ngIf="loading"></mat-spinner>
 | 
			
		||||
  <form [formGroup]="clientForm" class="client-form" *ngIf="!loading">
 | 
			
		||||
  <form [formGroup]="clientForm" class="client-form grid-form" *ngIf="!loading">
 | 
			
		||||
 | 
			
		||||
    <mat-form-field class="form-field">
 | 
			
		||||
      <mat-label i18n="@@organizational-unit-label">Padre</mat-label>
 | 
			
		||||
      <mat-select formControlName="organizationalUnit">
 | 
			
		||||
| 
						 | 
				
			
			@ -10,10 +11,12 @@
 | 
			
		|||
        </mat-option>
 | 
			
		||||
      </mat-select>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
    <mat-form-field class="form-field">
 | 
			
		||||
      <mat-label i18n="@@name-label">Nombre</mat-label>
 | 
			
		||||
      <input matInput formControlName="name">
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
    <mat-form-field class="form-field">
 | 
			
		||||
      <mat-label i18n="@@oglive-label">OgLive</mat-label>
 | 
			
		||||
      <mat-select formControlName="ogLive">
 | 
			
		||||
| 
						 | 
				
			
			@ -22,10 +25,12 @@
 | 
			
		|||
        </mat-option>
 | 
			
		||||
      </mat-select>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
    <mat-form-field class="form-field">
 | 
			
		||||
      <mat-label i18n="@@serial-number-label">Número de Serie</mat-label>
 | 
			
		||||
      <input matInput formControlName="serialNumber">
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
    <mat-form-field class="form-field">
 | 
			
		||||
      <mat-label i18n="@@netiface-label">Interfaz de red</mat-label>
 | 
			
		||||
      <mat-select formControlName="netiface">
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +39,7 @@
 | 
			
		|||
        </mat-option>
 | 
			
		||||
      </mat-select>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
    <mat-form-field class="form-field">
 | 
			
		||||
      <mat-label i18n="@@net-driver-label">Controlador de red</mat-label>
 | 
			
		||||
      <mat-select formControlName="netDriver">
 | 
			
		||||
| 
						 | 
				
			
			@ -42,21 +48,25 @@
 | 
			
		|||
        </mat-option>
 | 
			
		||||
      </mat-select>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
    <mat-form-field class="form-field">
 | 
			
		||||
      <mat-label i18n="@@mac-label">MAC</mat-label>
 | 
			
		||||
      <input matInput formControlName="mac">
 | 
			
		||||
      <mat-error i18n="@@mac-error">Formato de MAC inválido. Ejemplo válido: 00:11:22:33:44:55</mat-error>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
    <mat-form-field class="form-field">
 | 
			
		||||
      <mat-label i18n="@@ip-label">Dirección IP</mat-label>
 | 
			
		||||
      <input matInput formControlName="ip">
 | 
			
		||||
      <mat-error i18n="@@ip-error">Formato de dirección IP inválido. Ejemplo válido: 127.0.0.1</mat-error>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
    <mat-form-field class="form-field">
 | 
			
		||||
      <mat-label i18n="@@menu-url-label">Menú URL</mat-label>
 | 
			
		||||
      <input matInput formControlName="menu" type="url">
 | 
			
		||||
      <mat-error i18n="@@menu-url-error">Formato de URL inválido.</mat-error>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
    <mat-form-field class="form-field">
 | 
			
		||||
      <mat-label i18n="@@hardware-profile-label">Perfil de Hardware</mat-label>
 | 
			
		||||
      <mat-select formControlName="hardwareProfile">
 | 
			
		||||
| 
						 | 
				
			
			@ -66,9 +76,11 @@
 | 
			
		|||
      </mat-select>
 | 
			
		||||
      <mat-error i18n="@@hardware-profile-error">Formato de URL inválido.</mat-error>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
  </form>
 | 
			
		||||
</div>
 | 
			
		||||
<div mat-dialog-actions>
 | 
			
		||||
 | 
			
		||||
<div mat-dialog-actions align="end">
 | 
			
		||||
  <button mat-button (click)="onNoClick()" i18n="@@cancel-button">Cancelar</button>
 | 
			
		||||
  <button mat-button [disabled]="!clientForm.valid" (click)="onSubmit()">
 | 
			
		||||
    {{ !isEditMode ? 'Añadir' : 'Guardar' }}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -199,4 +199,3 @@ mat-spinner {
 | 
			
		|||
mat-card {
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -297,4 +297,7 @@
 | 
			
		|||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </mat-tab>
 | 
			
		||||
  <mat-tab i18n-label label="Clientes">
 | 
			
		||||
    <app-client-tab-view></app-client-tab-view>
 | 
			
		||||
  </mat-tab>
 | 
			
		||||
</mat-tab-group>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,8 @@ import {HttpClient} from "@angular/common/http";
 | 
			
		|||
import {PageEvent} from "@angular/material/paginator";
 | 
			
		||||
import { SaveFiltersDialogComponent } from './save-filters-dialog/save-filters-dialog.component';
 | 
			
		||||
import { AcctionsModalComponent } from './acctions-modal/acctions-modal.component';
 | 
			
		||||
import {MatTableDataSource} from "@angular/material/table";
 | 
			
		||||
import {DatePipe} from "@angular/common";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-groups',
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +27,7 @@ import { AcctionsModalComponent } from './acctions-modal/acctions-modal.componen
 | 
			
		|||
})
 | 
			
		||||
export class GroupsComponent implements OnInit {
 | 
			
		||||
  baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
 | 
			
		||||
  dataSource = new MatTableDataSource<any>();
 | 
			
		||||
  organizationalUnits: UnidadOrganizativa[] = [];
 | 
			
		||||
  selectedUnidad: UnidadOrganizativa | null = null;
 | 
			
		||||
  selectedDetail: any | null = null;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,12 +40,10 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
  searchTerm: string = '';
 | 
			
		||||
  selectedFilter1: string = 'none';
 | 
			
		||||
  selectedFilter2: string = 'none';
 | 
			
		||||
 | 
			
		||||
  selectedFilterOS: string[] = [];
 | 
			
		||||
  selectedFilterStatus: string[] = [];
 | 
			
		||||
  filterIP: string = '';
 | 
			
		||||
  filterMAC: string = '';
 | 
			
		||||
 | 
			
		||||
  filterName: string = '';
 | 
			
		||||
  filteredResults: any[] = [];
 | 
			
		||||
  savedFilterNames: any[] = [];
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +53,8 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
  pageSizeOptions: number[] = [5, 10, 25, 100];
 | 
			
		||||
  selectedElements: any[] = [];
 | 
			
		||||
  isAllSelected: boolean = false;
 | 
			
		||||
 | 
			
		||||
  filters: { [key: string]: string } = {};
 | 
			
		||||
  datePipe: DatePipe = new DatePipe('es-ES');
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
  private dataService: DataService,
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +157,7 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
 | 
			
		||||
  addOU(event: MouseEvent, parent:any = null): void {
 | 
			
		||||
    event.stopPropagation();
 | 
			
		||||
    const dialogRef = this.dialog.open(CreateOrganizationalUnitComponent, { data: { parent }, width: '700px'});
 | 
			
		||||
    const dialogRef = this.dialog.open(CreateOrganizationalUnitComponent, { data: { parent }, width: '900px'});
 | 
			
		||||
    dialogRef.afterClosed().subscribe(() => {
 | 
			
		||||
      this.dataService.getOrganizationalUnits().subscribe(
 | 
			
		||||
        data => {
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +172,7 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
  addClient(event: MouseEvent, organizationalUnit:any = null): void {
 | 
			
		||||
    event.stopPropagation();
 | 
			
		||||
 | 
			
		||||
    const dialogRef = this.dialog.open(CreateClientComponent, { data: { organizationalUnit }, width: '700px'});
 | 
			
		||||
    const dialogRef = this.dialog.open(CreateClientComponent, { data: { organizationalUnit }, width: '900px'});
 | 
			
		||||
 | 
			
		||||
    dialogRef.afterClosed().subscribe(() => {
 | 
			
		||||
      this.dataService.getOrganizationalUnits().subscribe(
 | 
			
		||||
| 
						 | 
				
			
			@ -318,7 +320,6 @@ export class GroupsComponent implements OnInit {
 | 
			
		|||
    this.applyFilter();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  saveFilters() {
 | 
			
		||||
    const dialogRef = this.dialog.open(SaveFiltersDialogComponent);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue