Add new CSS files and components for DHCP subnets and clients

oggui/ogdhcp
Alvaro Puente Mella 2024-09-05 12:10:13 +02:00
parent f590ff51dc
commit 8ec7644eda
15 changed files with 296 additions and 284 deletions

View File

@ -81,8 +81,8 @@ import { CreatePxeBootFileComponent } from './components/ogboot/pxe-boot-files/c
import { NgxChartsModule } from '@swimlane/ngx-charts';
import { OgdhcpComponent } from './components/ogdhcp/ogdhcp.component';
import { OgDhcpSubnetsComponent } from './components/ogdhcp/og-dhcp-subnets/og-dhcp-subnets.component';
import { CreateSubnetComponent } from './components/ogdhcp/og-dhcp-subnets/create-subnet/create-subnet/create-subnet.component';
import { CreateSubnetComponent } from './components/ogdhcp/og-dhcp-subnets/create-subnet/create-subnet.component';
import { AddClientsToSubnetComponent } from './components/ogdhcp/og-dhcp-subnets/add-clients-to-subnet/add-clients-to-subnet.component';
@NgModule({
declarations: [
AppComponent,
@ -123,7 +123,8 @@ import { CreateSubnetComponent } from './components/ogdhcp/og-dhcp-subnets/creat
CreatePxeBootFileComponent,
OgdhcpComponent,
OgDhcpSubnetsComponent,
CreateSubnetComponent
CreateSubnetComponent,
AddClientsToSubnetComponent
],
bootstrap: [AppComponent],
imports: [BrowserModule,

View File

@ -251,8 +251,6 @@ export class GroupsComponent implements OnInit {
onEditClick(event: MouseEvent, type: any, uuid: string): void {
event.stopPropagation();
console.log('Tipo del elemento a editar:', type);
console.log('UUID del elemento a editar:', uuid);
if (type != "client") {
const dialogRef = this.dialog.open(EditOrganizationalUnitComponent, { data: { uuid }, width: '700px'});
} else {

View File

@ -0,0 +1,17 @@
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100px;
}
mat-form-field {
width: 100%;
}
mat-dialog-actions {
display: flex;
justify-content: flex-end;
}

View File

@ -0,0 +1,21 @@
<h2 mat-dialog-title>Select Clients</h2>
<mat-dialog-content>
<div *ngIf="loading" class="loading-container">
<mat-spinner></mat-spinner>
<p>Cargando clientes...</p>
</div>
<mat-form-field appearance="fill" *ngIf="!loading" style="width: 100%;">
<mat-label>Select Clients</mat-label>
<mat-select [(value)]="selectedClients" multiple>
<mat-option *ngFor="let client of clients" [value]="client">
{{ client.name }} (IP: {{ client.ip }}, MAC: {{ client.mac }})
</mat-option>
</mat-select>
</mat-form-field>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button (click)="close()">Cancel</button>
<button mat-button (click)="save()">Save</button>
</mat-dialog-actions>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AddClientsToSubnetComponent } from './add-clients-to-subnet.component';
describe('AddClientsToSubnetComponent', () => {
let component: AddClientsToSubnetComponent;
let fixture: ComponentFixture<AddClientsToSubnetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AddClientsToSubnetComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AddClientsToSubnetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,45 @@
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { MatDialogRef } from '@angular/material/dialog';
@Component({
selector: 'app-add-clients-to-subnet',
templateUrl: './add-clients-to-subnet.component.html',
styleUrls: ['./add-clients-to-subnet.component.css']
})
export class AddClientsToSubnetComponent implements OnInit {
clients: any[] = [];
selectedClients: any[] = [];
loading: boolean = true; // Variable para controlar el estado de carga
constructor(
private http: HttpClient,
public dialogRef: MatDialogRef<AddClientsToSubnetComponent>
) {}
ngOnInit(): void {
// Comienza la carga
this.loading = true;
// Hacer la llamada HTTP directamente en el componente
this.http.get<any>('http://127.0.0.1:8080/clients?page=1&itemsPerPage=30').subscribe(
response => {
this.clients = response['hydra:member']; // Asignamos los clientes obtenidos a la variable clients
this.loading = false; // Los datos se han cargado, desactivar el spinner
},
error => {
console.error('Error fetching clients:', error);
this.loading = false; // También detener el spinner en caso de error
}
);
}
save() {
console.log('Selected clients:', this.selectedClients);
this.dialogRef.close(this.selectedClients); // Cierra el modal y pasa los clientes seleccionados
}
close() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,27 @@
<h2 mat-dialog-title>Añadir configuración de red</h2>
<mat-dialog-content>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Nombre</mat-label>
<input matInput [(ngModel)]="name" placeholder="Nombre de la subred">
</mat-form-field>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Netmask</mat-label>
<input matInput [(ngModel)]="netmask" placeholder="Netmask">
</mat-form-field>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Dirección IP</mat-label>
<input matInput [(ngModel)]="ipAddress" placeholder="Dirección IP">
</mat-form-field>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Next Server</mat-label>
<input matInput [(ngModel)]="nextServer" placeholder="Next Server">
</mat-form-field>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Boot File Name</mat-label>
<input matInput [(ngModel)]="bootFileName" placeholder="Boot File Name">
</mat-form-field>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button (click)="onNoClick()">Cancelar</button>
<button mat-button (click)="addNetworkConfig()" cdkFocusInitial>Añadir</button>
</mat-dialog-actions>

View File

@ -0,0 +1,55 @@
import { HttpClient } from '@angular/common/http';
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
@Component({
selector: 'app-create-subnet',
templateUrl: './create-subnet.component.html',
styleUrls: ['./create-subnet.component.css']
})
export class CreateSubnetComponent implements OnInit {
name: string = '';
netmask: string = '';
ipAddress: string = '';
nextServer: string = '';
bootFileName: string = '';
constructor(
private toastService: ToastrService,
private http: HttpClient,
public dialogRef: MatDialogRef<CreateSubnetComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
) { }
ngOnInit(): void {
}
onNoClick(): void {
this.dialogRef.close();
}
addNetworkConfig(): void {
const payload = {
name: this.name,
netmask: this.netmask,
ipAddress: this.ipAddress,
nextServer: this.nextServer,
bootFileName: this.bootFileName
};
this.http.post('http://127.0.0.1:8080/subnets', payload)
.subscribe({
next: (response) => {
console.log('Success:', response);
this.toastService.success('Configuración de red añadida exitosamente');
this.dialogRef.close();
},
error: (error) => {
console.error('Error:', error);
this.toastService.error('Error al añadir la configuración de red');
}
});
}
}

View File

@ -1,70 +0,0 @@
<h2 mat-dialog-title>{{ isEditMode ? 'Editar' : 'Añadir' }} configuración de red</h2>
<mat-horizontal-stepper [linear]="true" #stepper>
<!-- Paso 1: Selección de Aulas -->
<mat-step [stepControl]="aulasForm">
<form [formGroup]="aulasForm">
<ng-template matStepLabel>Seleccionar Aulas</ng-template>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Seleccionar Aulas</mat-label>
<mat-select formControlName="selectedAulas" (selectionChange)="onAulasSelectionChange($event)" multiple>
<mat-option *ngFor="let aula of aulas" [value]="aula">{{ aula.name }}</mat-option>
</mat-select>
</mat-form-field>
<div>
<button mat-button matStepperNext>Continuar</button>
</div>
</form>
</mat-step>
<!-- Paso 2: Configuración de Subnet -->
<mat-step [stepControl]="subnetForm">
<form [formGroup]="subnetForm">
<ng-template matStepLabel>Configurar Subnet</ng-template>
<!-- Lista de Aulas Seleccionadas -->
<div>
<h3>Aulas Seleccionadas:</h3>
<ul *ngIf="selectedAulas.length > 0">
<li *ngFor="let aula of selectedAulas">{{ aula.name }}</li>
</ul>
</div>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Nombre</mat-label>
<input matInput formControlName="name" placeholder="Nombre de la subred">
</mat-form-field>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Next Server</mat-label>
<mat-select formControlName="nextServer">
<mat-option *ngFor="let server of nextServerOptions" [value]="server">{{ server }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Boot File Name</mat-label>
<mat-select formControlName="bootFileName">
<mat-option *ngFor="let bootFile of bootFileNameOptions" [value]="bootFile">{{ bootFile }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Netmask</mat-label>
<input matInput formControlName="netmask" placeholder="Netmask">
</mat-form-field>
<mat-form-field appearance="fill" class="full-width">
<mat-label>Dirección IP</mat-label>
<input matInput formControlName="ipAddress" placeholder="Dirección IP">
</mat-form-field>
<div>
<button mat-button matStepperPrevious>Regresar</button>
<button mat-button (click)="submitForm()">Guardar</button>
</div>
</form>
</mat-step>
</mat-horizontal-stepper>

View File

@ -1,137 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { Component, Inject, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
@Component({
selector: 'app-create-subnet',
templateUrl: './create-subnet.component.html',
styleUrls: ['./create-subnet.component.css']
})
export class CreateSubnetComponent implements OnInit {
aulasForm: FormGroup;
subnetForm: FormGroup;
aulas: any[] = []; // Lista de aulas
selectedAulas: any[] = []; // Aulas seleccionadas
nextServerOptions: string[] = []; // Opciones de Next Server
bootFileNameOptions: string[] = []; // Opciones de Boot File Name
isEditMode: boolean = false;
constructor(
private fb: FormBuilder, // FormBuilder para manejar los formularios
private toastService: ToastrService,
private http: HttpClient,
public dialogRef: MatDialogRef<CreateSubnetComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
) {
// Inicializar los formularios
this.aulasForm = this.fb.group({
selectedAulas: [[], Validators.required]
});
this.subnetForm = this.fb.group({
name: ['', Validators.required],
netmask: ['', Validators.required],
ipAddress: ['', Validators.required],
nextServer: ['', Validators.required],
bootFileName: ['', Validators.required]
});
}
ngOnInit(): void {
this.loadAulas(); // Cargar las aulas al iniciar el componente
if (this.data && this.data.subnet) {
this.isEditMode = true;
this.subnetForm.patchValue({
name: this.data.subnet.name,
netmask: this.data.subnet.netmask,
ipAddress: this.data.subnet.ipAddress,
nextServer: this.data.subnet.nextServer,
bootFileName: this.data.subnet.bootFileName
});
this.selectedAulas = this.data.subnet.selectedAulas || [];
this.updateServerAndBootFileOptions();
}
}
loadAulas(): void {
this.http.get<any>('http://127.0.0.1:8001/organizational-units?page=1&itemsPerPage=30')
.subscribe(response => {
this.aulas = response['hydra:member'];
}, error => {
console.error('Error loading aulas:', error);
this.toastService.error('Error al cargar las aulas');
});
}
onAulasSelectionChange(event: any): void {
this.selectedAulas = event.value;
this.updateServerAndBootFileOptions();
}
updateServerAndBootFileOptions(): void {
// Vaciar las opciones previas
this.nextServerOptions = [];
this.bootFileNameOptions = [];
// Agregar las opciones de "nextServer" y "bootFileName" basadas en las aulas seleccionadas
this.selectedAulas.forEach(aula => {
if (aula.networkSettings) {
if (aula.networkSettings.nextServer && !this.nextServerOptions.includes(aula.networkSettings.nextServer)) {
this.nextServerOptions.push(aula.networkSettings.nextServer);
}
if (aula.networkSettings.bootFileName && !this.bootFileNameOptions.includes(aula.networkSettings.bootFileName)) {
this.bootFileNameOptions.push(aula.networkSettings.bootFileName);
}
}
});
}
onNoClick(): void {
this.dialogRef.close();
}
submitForm(): void {
if (this.aulasForm.invalid || this.subnetForm.invalid) {
this.toastService.error('Por favor, complete los formularios.');
return;
}
const payload = {
...this.subnetForm.value,
/* selectedAulas: this.selectedAulas.map(aula => aula.uuid) */// Enviar solo los UUID de las aulas seleccionadas
};
if (this.isEditMode) {
this.http.patch(`http://127.0.0.1:8001/subnets/${this.data.subnet.uuid}`, payload)
.subscribe({
next: (response) => {
console.log('Success:', response);
this.toastService.success('Configuración de red actualizada exitosamente');
this.dialogRef.close();
},
error: (error) => {
console.error('Error:', error);
this.toastService.error('Error al actualizar la configuración de red');
}
});
} else {
this.http.post('http://127.0.0.1:8001/subnets', payload)
.subscribe({
next: (response) => {
console.log('Success:', response);
this.toastService.success('Configuración de red añadida exitosamente');
this.dialogRef.close();
},
error: (error) => {
console.error('Error:', error);
this.toastService.error('Error al añadir la configuración de red');
}
});
}
}
}

View File

@ -106,4 +106,3 @@ table {
.example-button-row .mat-mdc-button-base {
margin: 8px 8px 8px 0;
}

View File

@ -1,73 +1,90 @@
<mat-accordion class="example-headers-align">
<mat-expansion-panel hideToggle>
<mat-expansion-panel-header>
<mat-panel-title> Información de Subnets </mat-panel-title>
<mat-panel-description>
<mat-icon [style.color]="getIcon().color">{{ getIcon().name }}</mat-icon>
</mat-panel-description>
</mat-expansion-panel-header>
<p *ngIf="alertMessage">Subnets sincronizadas: {{ alertMessage }}</p>
<p *ngIf="alertMessage">Número de subnets en base de datos: {{ length }}</p>
<div class="example-button-row">
<button mat-flat-button color="primary" (click)="syncSubnets()"> Sincronizar Subnets</button>
</div>
</mat-expansion-panel>
</mat-accordion>
<div class="header-container">
<h2 class="title" i18n="@@subnetsTitle">Administrar Subnets</h2>
<div class="subnets-button-row">
<button mat-flat-button color="primary" (click)="addSubnet()">Añadir Subnet</button>
<mat-expansion-panel hideToggle>
<mat-expansion-panel-header>
<mat-panel-title> Información de Subnets </mat-panel-title>
<mat-panel-description>
<mat-icon [style.color]="getIcon().color">{{ getIcon().name }}</mat-icon>
</mat-panel-description>
</mat-expansion-panel-header>
<p *ngIf="alertMessage">Subnets sincronizadas: {{ alertMessage }}</p>
<p *ngIf="alertMessage">Número de subnets en base de datos: {{ length }}</p>
<div class="example-button-row">
<button mat-flat-button color="primary" (click)="syncSubnets()"> Sincronizar Subnets</button>
</div>
</mat-expansion-panel>
</mat-accordion>
<div class="header-container">
<h2 class="title" i18n="@@subnetsTitle">Administrar Subnets</h2>
<div class="subnets-button-row">
<button mat-flat-button color="primary" (click)="addSubnet()">Añadir Subnet</button>
</div>
<mat-divider class="divider"></mat-divider>
<table mat-table [dataSource]="dataSource" 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 subnet">
<!-- Mostrar otros campos normalmente -->
<ng-container *ngIf="column.columnDef === 'name'">
{{ subnet.name }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'netmask'">
{{ subnet.netmask }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'ipAddress'">
{{ subnet.ipAddress }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'nextServer'">
{{ subnet.nextServer }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'bootFileName'">
{{ subnet.bootFileName }}
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions">Acciones</th>
<td mat-cell *matCellDef="let subnet">
<button mat-icon-button color="primary" (click)="editSubnet(subnet)" i18n="@@editSubnet"> <mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="deleteSubnet(subnet)"><mat-icon>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>
</div>
<mat-divider class="divider"></mat-divider>
<table mat-table [dataSource]="dataSource" 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 subnet">
<!-- Mostrar otros campos normalmente -->
<ng-container *ngIf="column.columnDef === 'name'">
{{ subnet.name }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'netmask'">
{{ subnet.netmask }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'ipAddress'">
{{ subnet.ipAddress }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'nextServer'">
{{ subnet.nextServer }}
</ng-container>
<ng-container *ngIf="column.columnDef === 'bootFileName'">
{{ subnet.bootFileName }}
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef i18n="@@columnActions">Acciones</th>
<td mat-cell *matCellDef="let subnet">
<button mat-icon-button color="primary" (click)="editSubnet(subnet)" i18n="@@editSubnet">
<mat-icon>edit</mat-icon></button>
<button mat-icon-button color="warn" (click)="deleteSubnet(subnet)"><mat-icon>delete</mat-icon></button>
<!-- Icono de tres puntos y menú desplegable -->
<button mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item (click)="addSubnet()">
<mat-icon>add</mat-icon>
<span>Añadir Subnet</span>
</button>
<button mat-menu-item (click)="updateSubnet()">
<mat-icon>edit</mat-icon>
<span>Actualizar</span>
</button>
<button mat-menu-item (click)="addClientsToSubnet()">
<mat-icon>computer</mat-icon>
<span>Add Clients to Subnet</span>
</button>
</mat-menu>
</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>

View File

@ -1,11 +1,12 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { CreateSubnetComponent } from './create-subnet/create-subnet/create-subnet.component';
import { CreateSubnetComponent } from './create-subnet/create-subnet.component';
import { MatDialog } from '@angular/material/dialog';
import { HttpClient } from '@angular/common/http';
import { DeleteModalComponent } from '../../../shared/delete_modal/delete-modal/delete-modal.component';
import { ToastrService } from 'ngx-toastr';
import { AddClientsToSubnetComponent } from './add-clients-to-subnet/add-clients-to-subnet.component';
export interface Subnet {
'@id': string;
@ -125,6 +126,21 @@ export class OgDhcpSubnetsComponent {
});
}
updateSubnet() {
console.log('Update Subnet action selected');
}
addClientsToSubnet() {
const dialogRef = this.dialog.open (AddClientsToSubnetComponent, {
width: '600px'
});
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed');
this.loadSubnets()
});
}
onPageChange(event: any) {
this.page = event.pageIndex;
this.itemsPerPage = event.pageSize;