From b74f129dbeed0b0413aa4095e991c92d3924e42c Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 10 Jan 2025 08:26:52 +0100 Subject: [PATCH] refs #1320. Splice create client logic. --- ogWebconsole/src/app/app.module.ts | 2 + .../components/groups/groups.component.html | 13 +- .../app/components/groups/groups.component.ts | 15 ++ .../create-client/create-client.component.css | 190 ++++---------- .../create-client.component.html | 234 +++++++----------- .../create-multiple-client.component.css | 158 ++++++++++++ .../create-multiple-client.component.html | 58 +++++ .../create-multiple-client.component.spec.ts | 23 ++ .../create-multiple-client.component.ts | 182 ++++++++++++++ 9 files changed, 587 insertions(+), 288 deletions(-) create mode 100644 ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.css create mode 100644 ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.html create mode 100644 ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.spec.ts create mode 100644 ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts diff --git a/ogWebconsole/src/app/app.module.ts b/ogWebconsole/src/app/app.module.ts index 4c1ddf0..bbdb13b 100644 --- a/ogWebconsole/src/app/app.module.ts +++ b/ogWebconsole/src/app/app.module.ts @@ -123,6 +123,7 @@ import { EnvVarsComponent } from './components/admin/env-vars/env-vars.component import { MatSortModule } from '@angular/material/sort'; import { MenusComponent } from './components/menus/menus.component'; import { CreateMenuComponent } from './components/menus/create-menu/create-menu.component'; +import { CreateMultipleClientComponent } from './components/groups/shared/clients/create-multiple-client/create-multiple-client.component'; export function HttpLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http, './locale/', '.json'); } @@ -204,6 +205,7 @@ export function HttpLoaderFactory(http: HttpClient) { EnvVarsComponent, MenusComponent, CreateMenuComponent, + CreateMultipleClientComponent, ], bootstrap: [AppComponent], imports: [BrowserModule, diff --git a/ogWebconsole/src/app/components/groups/groups.component.html b/ogWebconsole/src/app/components/groups/groups.component.html index 08ddb74..955fd9d 100644 --- a/ogWebconsole/src/app/components/groups/groups.component.html +++ b/ogWebconsole/src/app/components/groups/groups.component.html @@ -13,9 +13,12 @@ matTooltip="{{ 'newOrganizationalUnitTooltip' | translate }}" matTooltipShowDelay="1000"> {{ 'newOrganizationalUnitButton' | translate }} - + + + + + + - -

o añadelos manualmente:

-
- - -
- + + OgLive + + + {{ oglive.name }} + + + -

Clientes importados:

-
- - - - - + + Número de Serie + + - - - - + + Interfaz de red + + + {{ type.name }} + + + - - -
Nombre {{ client.name }} IP {{ client.ip }}
-
- + + Controlador de red + + + {{ type.name }} + + + - + + MAC + Ejemplo: 00:11:22:33:44:55 + + Formato de MAC inválido. Ejemplo válido: 00:11:22:33:44:55 + - -

Añadir un cliente

- -
+ + Dirección IP + Ejemplo: 127.0.0.1 + + Formato de dirección IP inválido. Ejemplo válido: 127.0.0.1 + - - {{ 'nameLabel' | translate }} - - + + Plantilla PXE + + + {{ template.name }} + + + - - {{ 'ogLiveLabel' | translate }} - - - {{ oglive.filename }} - - - + + Perfil de Hardware + + + {{ unit.description }} + + + Formato de URL inválido. + - - {{ 'serialNumberLabel' | translate }} - - + + Repositorio + + + {{ repository.name }} + + + - - {{ 'netifaceLabel' | translate }} - - - {{ type.name }} - - - - - - {{ 'netDriverLabel' | translate }} - - - {{ type.name }} - - - - - - {{ 'macLabel' | translate }} - {{ 'macHint' | translate }} - - {{ 'macError' | translate }} - - - - {{ 'ipLabel' | translate }} - {{ 'ipHint' | translate }} - - {{ 'ipError' | translate }} - - - - {{ 'templateLabel' | translate }} - - - {{ template.name }} - - - - - - {{ 'hardwareProfileLabel' | translate }} - - - {{ unit.description }} - - - {{ 'hardwareProfileError' | translate }} - - - - Repositorio - - - {{ repository.name }} - - - - - - {{ 'menuLabel' | translate }} - - - {{ menu.name }} - - - {{ 'menuError' | translate }} - -
-
- + + {{ 'menuLabel' | translate }} + + + {{ menu.name }} + + + {{ 'menuError' | translate }} + +
- - - + +
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.css b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.css new file mode 100644 index 0000000..64a2fb8 --- /dev/null +++ b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.css @@ -0,0 +1,158 @@ +.create-client-container { + display: flex; + flex-direction: column; + padding: 16px; +} + +h1, h3, h4 { + margin: 0 0 16px; + color: #333; + font-weight: 600; +} + +h1 { + font-size: 20px; +} + +h3 { + font-size: 18px; +} + +h4 { + font-size: 16px; + margin-top: 16px; +} + +.inputs-container { + display: flex; + gap: 24px; + margin-top: 16px; +} + +.mat-dialog-content { + flex: 1; + background-color: #f9f9f9; + border-radius: 8px; + padding: 16px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + min-width: 600px; + max-width: 90vw; + width: 800px; +} + +.create-multiple-client-container { + flex: 1; + background-color: #f9f9f9; + border-radius: 8px; + padding: 16px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.client-form { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 16px; +} + +.form-field { + width: 100%; +} + +.mat-form-field { + width: 100%; +} + +.scrollable-table { + max-height: 200px; + overflow-y: auto; + margin-top: 16px; + border: 1px solid #ddd; + border-radius: 8px; +} + +table { + width: 100%; + border-collapse: collapse; +} + +th, td { + text-align: left; + padding: 8px; + border-bottom: 1px solid #ddd; +} + +th { + background-color: #f1f1f1; + font-weight: bold; +} + +tr:hover { + background-color: #f9f9f9; +} + +button { + margin-right: 8px; +} + +button:last-child { + margin-right: 0; +} + +.mat-dialog-actions { + margin-top: 16px; + display: flex; + justify-content: space-between; +} + +button.mat-raised-button { + text-transform: none; + font-weight: 600; +} + +.loading-spinner { + margin: 16px auto; + display: block; +} + +.toggle-button { + background: none; + border: none; + color: #007BFF; + cursor: pointer; + font-size: 14px; + text-decoration: underline; +} + +.toggle-button:hover { + text-decoration: none; +} + +.mat-divider { + margin: 0 16px; +} + +.upload-container { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; +} + +input[type="file"] { + display: none; +} + +@media (max-width: 768px) { + .inputs-container { + flex-direction: column; + gap: 16px; + } + + .mat-dialog-content, .create-multiple-client-container { + padding: 12px; + } + + .scrollable-table { + max-height: 150px; + } +} diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.html b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.html new file mode 100644 index 0000000..91955c9 --- /dev/null +++ b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.html @@ -0,0 +1,58 @@ +
+

Añadir multiples clientes

+
+
+ +
+ + {{ 'organizationalUnitLabel' | translate }} + + +
{{ unit.name }}
+
+
+
+
+ +
+
+ + +

o añadelos manualmente:

+
+ + +
+
+ +

Clientes importados:

+
+ + + + + + + + + + + + + + + + + + +
Nombre {{ client.name }} IP {{ client.ip }} Mac {{ client.mac }}
+
+
+
+
+ +
+ + +
+
diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.spec.ts b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.spec.ts new file mode 100644 index 0000000..bae410d --- /dev/null +++ b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateMultipleClientComponent } from './create-multiple-client.component'; + +describe('CreateMultipleClientComponent', () => { + let component: CreateMultipleClientComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CreateMultipleClientComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CreateMultipleClientComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts new file mode 100644 index 0000000..517bbc2 --- /dev/null +++ b/ogWebconsole/src/app/components/groups/shared/clients/create-multiple-client/create-multiple-client.component.ts @@ -0,0 +1,182 @@ +import {Component, Inject} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from "@angular/forms"; +import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; +import {HttpClient} from "@angular/common/http"; +import {MatSnackBar} from "@angular/material/snack-bar"; +import {ToastrService} from "ngx-toastr"; +import {DataService} from "../../../services/data.service"; + +@Component({ + selector: 'app-create-multiple-client', + templateUrl: './create-multiple-client.component.html', + styleUrl: './create-multiple-client.component.css' +}) +export class CreateMultipleClientComponent { + baseUrl: string = import.meta.env.NG_APP_BASE_API_URL; + clientForm!: FormGroup; + parentUnits: any[] = []; + hardwareProfiles: any[] = []; + ogLives: any[] = []; + menus: any[] = []; + templates: any[] = []; + uploadedClients: any[] = []; + repositories: any[] = []; + loading: boolean = false; + displayedColumns: string[] = ['name', 'ip', 'mac']; + showTextarea: boolean = true; + protected netifaceTypes = [ + { name: 'Eth0', value: 'eth0' }, + { name: 'Eth1', value: 'eth1' }, + { name: 'Eth2', value: 'eth2' } + ]; + protected netDriverTypes = [ + { name: 'Generic', value: 'generic' } + ]; + + constructor( + private fb: FormBuilder, + private dialogRef: MatDialogRef, + private http: HttpClient, + private snackBar: MatSnackBar, + private toastService: ToastrService, + private dataService: DataService, + @Inject(MAT_DIALOG_DATA) public data: any + ) {} + + ngOnInit(): void { + this.initForm(); + this.loadParentUnits(); + } + + initForm(): void { + this.clientForm = this.fb.group({ + organizationalUnit: [ + this.data.organizationalUnit ? this.data.organizationalUnit['@id'] : null, + Validators.required + ], + name: ['', Validators.required], + serialNumber: [''], + netiface: null, + netDriver: null, + mac: ['', Validators.required], + ip: ['', Validators.required], + template: [null], + hardwareProfile: [ + this.data.organizationalUnit?.networkSettings?.hardwareProfile?.['@id'] || null + ], + ogLive: [null], + repository: [null], + menu: [null] + }); + } + + loadParentUnits(): void { + this.loading = true; + const url = `${this.baseUrl}/organizational-units?page=1&itemsPerPage=10000`; + + this.http.get(url).subscribe( + response => { + this.parentUnits = response['hydra:member']; + this.loading = false; + }, + error => { + console.error('Error fetching parent units:', error); + this.loading = false; + } + ); + } + + onFileUpload(event: any): void { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + + reader.onload = (e: any) => { + const textData = e.target.result; + const regex = /host\s+(\S+)\s+\{\s+hardware\s+ethernet\s+([\da-fA-F:]+);\s+fixed-address\s+([\d.]+);\s+\}/g; + let match; + const clients = []; + + while ((match = regex.exec(textData)) !== null) { + clients.push({ + name: match[1], + mac: match[2], + ip: match[3] + }); + } + + if (clients.length > 0) { + this.uploadedClients = clients; + this.toastService.success('Archivo cargado correctamente, los datos están listos para enviarse.', 'Éxito'); + this.showTextarea = false; + } else { + this.toastService.error('No se encontraron datos válidos', 'Error'); + this.showTextarea = true; + } + }; + + reader.readAsText(file); + } + } + + onTextarea(text: string): void { + const regex = /host\s+(\S+)\s+\{\s+hardware\s+ethernet\s+([\da-fA-F:]+);\s+fixed-address\s+([\d.]+);\s+\}/g; + let match; + const clients = []; + + while ((match = regex.exec(text)) !== null) { + clients.push({ + name: match[1], + mac: match[2], + ip: match[3] + }); + } + + if (clients.length > 0) { + this.uploadedClients = clients; + this.toastService.success('Datos cargados correctamente, los datos están listos para enviarse.', 'Éxito'); + this.showTextarea = false; + } else { + this.toastService.error('No se encontraron datos válidos', 'Error'); + this.showTextarea = true; + } + } + + onSubmit(): void { + if (this.uploadedClients.length > 0) { + this.uploadedClients.forEach(client => { + const formData = { + organizationalUnit: this.clientForm.value.organizationalUnit || null, + name: client.name || null, + mac: client.mac || null, + ip: client.ip || null, + template: this.clientForm.value.template || null, + hardwareProfile: this.clientForm.value.hardwareProfile || null, + ogLive: this.clientForm.value.ogLive || null, + repository: this.clientForm.value.repository || null, + serialNumber: null, + netiface: null, + netDriver: null + }; + + this.http.post(`${this.baseUrl}/clients`, formData).subscribe( + response => { + this.toastService.success(`Cliente ${client.name} creado exitosamente`, 'Éxito'); + }, + error => { + console.error(`Error al crear el cliente ${client.name}:`, error); + this.toastService.error(`Error al crear el cliente ${client.name}`, 'Error'); + } + ); + }); + this.uploadedClients = []; + this.dialogRef.close(); + } else { + this.toastService.error('No hay clientes cargados para añadir', 'Error'); + } + } + + onNoClick(): void { + this.dialogRef.close(); + } +}